feat(SCOPONE-0010): improve capture pacing and settings

This commit is contained in:
Giancarmine Salucci
2026-04-09 23:00:59 +02:00
parent 77ab1f43a6
commit c107489b0a
7 changed files with 740 additions and 176 deletions

193
src/scenes/SettingsScene.ts Normal file
View File

@@ -0,0 +1,193 @@
import Phaser from 'phaser';
import {
AudioPreferences,
SettingsSceneData,
loadAudioPreferences,
saveAudioPreferences,
} from '../game/preferences';
type ToggleDefinition = {
key: keyof AudioPreferences;
label: string;
description: string;
};
type ToggleVisuals = {
background: Phaser.GameObjects.Rectangle;
statusText: Phaser.GameObjects.Text;
};
const TOGGLE_DEFINITIONS: ToggleDefinition[] = [
{
key: 'musicEnabled',
label: 'Musica del tavolo',
description: 'Controlla il tappeto sonoro di sottofondo durante la partita.',
},
{
key: 'effectsEnabled',
label: 'Effetti di gioco',
description: 'Attiva prese, scope e segnali sonori senza toccare la musica.',
},
];
export class SettingsScene extends Phaser.Scene {
private preferences: AudioPreferences = loadAudioPreferences();
private returnSceneKey = 'MenuScene';
private toggleVisuals = new Map<keyof AudioPreferences, ToggleVisuals>();
constructor() {
super({ key: 'SettingsScene' });
}
create(data?: SettingsSceneData): void {
const width = this.scale.width;
const height = this.scale.height;
this.preferences = loadAudioPreferences();
this.returnSceneKey = data?.returnSceneKey ?? 'MenuScene';
this.add.rectangle(0, 0, width, height, 0x123d22).setOrigin(0);
this.add.rectangle(width / 2, height / 2, width - 120, height - 120, 0x0d2216, 0.88)
.setStrokeStyle(2, 0xd9b75f, 0.45);
this.add.text(width / 2, 120, 'Impostazioni audio', {
fontFamily: 'Georgia, serif',
fontSize: '46px',
color: '#ffd700',
stroke: '#000000',
strokeThickness: 4,
resolution: 2,
}).setOrigin(0.5);
this.add.text(width / 2, 176, 'Musica ed effetti sono separati: ogni scelta viene salvata subito e sarà usata nelle prossime partite.', {
fontFamily: 'Georgia, serif',
fontSize: '19px',
color: '#d7ead1',
resolution: 2,
align: 'center',
wordWrap: { width: 760 },
}).setOrigin(0.5);
TOGGLE_DEFINITIONS.forEach((definition, index) => {
this.createToggleRow(definition, width / 2, 286 + index * 132, 760, 96);
});
this.add.text(width / 2, 556, 'Puoi tornare al menu in qualsiasi momento: la difficoltà si sceglie lì, laudio resta salvato qui.', {
fontFamily: 'Georgia, serif',
fontSize: '16px',
color: '#cfe5cd',
resolution: 2,
align: 'center',
wordWrap: { width: 720 },
}).setOrigin(0.5);
this.createButton(width / 2, height - 112, 280, 60, 'Torna al menu', 'Rientra alla schermata iniziale', () => {
this.returnToMenu();
});
}
private createToggleRow(definition: ToggleDefinition, x: number, y: number, width: number, height: number): void {
const row = this.add.rectangle(x, y, width, height, 0x173323, 0.96)
.setStrokeStyle(2, 0xcaa74a, 0.35)
.setInteractive({ useHandCursor: true });
this.add.text(x - width / 2 + 28, y - 16, definition.label, {
fontFamily: 'Georgia, serif',
fontSize: '24px',
color: '#ffffff',
resolution: 2,
}).setOrigin(0, 0.5);
this.add.text(x - width / 2 + 28, y + 14, definition.description, {
fontFamily: 'Georgia, serif',
fontSize: '16px',
color: '#d8ead2',
resolution: 2,
wordWrap: { width: 470 },
}).setOrigin(0, 0.5);
const toggleBackground = this.add.rectangle(x + width / 2 - 106, y, 148, 46, 0x356b39, 1)
.setStrokeStyle(2, 0xf5e1a4, 0.45);
const statusText = this.add.text(x + width / 2 - 106, y, '', {
fontFamily: 'Georgia, serif',
fontSize: '20px',
color: '#ffffff',
resolution: 2,
}).setOrigin(0.5);
this.toggleVisuals.set(definition.key, {
background: toggleBackground,
statusText,
});
row.on('pointerdown', () => {
this.togglePreference(definition.key);
});
row.on('pointerover', () => row.setFillStyle(0x1d402c, 1));
row.on('pointerout', () => row.setFillStyle(0x173323, 0.96));
this.refreshToggle(definition.key);
}
private createButton(
x: number,
y: number,
width: number,
height: number,
label: string,
subtitle: string,
onClick: () => void,
): void {
const button = this.add.rectangle(x, y, width, height, 0x1f6f78, 1)
.setStrokeStyle(2, 0xf5e1a4, 0.4)
.setInteractive({ useHandCursor: true });
this.add.text(x, y - 10, label, {
fontFamily: 'Georgia, serif',
fontSize: '21px',
color: '#ffffff',
stroke: '#000000',
strokeThickness: 2,
resolution: 2,
}).setOrigin(0.5);
this.add.text(x, y + 14, subtitle, {
fontFamily: 'Georgia, serif',
fontSize: '13px',
color: '#f7f1d5',
resolution: 2,
align: 'center',
}).setOrigin(0.5);
button.on('pointerover', () => button.setFillStyle(0x2f8f99));
button.on('pointerout', () => button.setFillStyle(0x1f6f78));
button.on('pointerdown', onClick);
}
private togglePreference(key: keyof AudioPreferences): void {
this.preferences = saveAudioPreferences({
...this.preferences,
[key]: !this.preferences[key],
});
this.refreshToggle(key);
}
private refreshToggle(key: keyof AudioPreferences): void {
const visuals = this.toggleVisuals.get(key);
if (!visuals) {
return;
}
const enabled = this.preferences[key];
visuals.background.setFillStyle(enabled ? 0x356b39 : 0x7a2f2f);
visuals.statusText.setText(enabled ? 'Attivo' : 'Disattivato');
}
private returnToMenu(): void {
this.cameras.main.fadeOut(250, 0, 30, 0);
this.cameras.main.once('camerafadeoutcomplete', () => {
this.scene.start(this.returnSceneKey);
});
}
}