import Phaser from 'phaser'; import { Difficulty } from '../game/types'; import { GameSceneData, loadAudioPreferences } from '../game/preferences'; import { SettingsScene } from './SettingsScene'; type MenuButtonPalette = { base: number; hover: number; }; type DifficultyOption = { label: string; subtitle: string; value: Difficulty; palette: MenuButtonPalette; }; type RuleSection = { heading: string; lines: string[]; }; const TITLE_STYLE: Phaser.Types.GameObjects.Text.TextStyle = { fontFamily: 'Georgia, serif', fontSize: '52px', color: '#ffd700', stroke: '#000000', strokeThickness: 4, resolution: 2, }; const PANEL_TITLE_STYLE: Phaser.Types.GameObjects.Text.TextStyle = { fontFamily: 'Georgia, serif', fontSize: '24px', color: '#ffd700', resolution: 2, }; const BODY_STYLE: Phaser.Types.GameObjects.Text.TextStyle = { fontFamily: 'Georgia, serif', fontSize: '18px', color: '#f8f5e6', resolution: 2, }; export class MenuScene extends Phaser.Scene { constructor() { super({ key: 'MenuScene' }); } create(): void { const width = this.scale.width; const height = this.scale.height; const audioPreferences = loadAudioPreferences(); this.drawBackground(width, height); this.drawDecorativeCards(width, height); this.ensureSettingsSceneAvailable(); this.add.text(width / 2, 92, 'Scopone Scientifico', TITLE_STYLE).setOrigin(0.5); this.add.text(width / 2, 142, 'Due squadre da due, una mano intera e lettura del tavolo fino all’ultimo punto.', { fontFamily: 'Georgia, serif', fontSize: '21px', color: '#d9f2d2', resolution: 2, }).setOrigin(0.5); this.createRulesPanel(width, height); this.createControlPanel(width, height, audioPreferences); } private drawBackground(width: number, height: number): void { this.add.rectangle(0, 0, width, height, 0x123d22).setOrigin(0); this.add.rectangle(width / 2, height / 2, width - 60, height - 60, 0x0b2916, 0.28) .setStrokeStyle(2, 0xe8c25d, 0.35); this.add.rectangle(width * 0.34, height * 0.58, width * 0.46, height * 0.50, 0x0d2215, 0.82) .setStrokeStyle(2, 0xc8a445, 0.4); this.add.rectangle(width * 0.77, height * 0.58, width * 0.24, height * 0.50, 0x10261b, 0.86) .setStrokeStyle(2, 0xc8a445, 0.4); } private drawDecorativeCards(width: number, height: number): void { const positions = [ [width * 0.08, height * 0.85], [width * 0.14, height * 0.87], [width * 0.92, height * 0.85], [width * 0.86, height * 0.87], ]; positions.forEach(([x, y]) => { this.add.image(x, y, 'retro').setScale(0.08).setAngle(Phaser.Math.Between(-15, 15)).setAlpha(0.9); }); } private createRulesPanel(width: number, height: number): void { const panelX = width * 0.12; const panelY = 206; const sections: RuleSection[] = [ { heading: 'Tavolo e squadre', lines: [ 'Si gioca in coppia: Sud e Nord contro Ovest ed Est.', 'Nel vero scopone si distribuiscono tutte le 40 carte, 10 a giocatore.', ], }, { heading: 'Come si prende', lines: [ 'Ogni carta cattura una carta dello stesso valore oppure una combinazione equivalente.', 'Se sul tavolo c’è una presa diretta dello stesso valore, quella ha sempre la precedenza.', ], }, { heading: 'Punteggio partita', lines: [ 'A fine mano contano carte, denari, settebello, primiera e scope.', 'La sfida prosegue mano dopo mano finché una squadra arriva ad almeno 11 punti.', ], }, ]; this.add.text(panelX, panelY, 'Regolamento essenziale', PANEL_TITLE_STYLE).setOrigin(0, 0.5); let currentY = panelY + 44; sections.forEach((section) => { this.add.text(panelX, currentY, section.heading, { fontFamily: 'Georgia, serif', fontSize: '20px', color: '#ffffff', resolution: 2, }).setOrigin(0, 0.5); currentY += 30; section.lines.forEach((line) => { this.add.text(panelX, currentY, `• ${line}`, { ...BODY_STYLE, wordWrap: { width: width * 0.40 }, lineSpacing: 5, }).setOrigin(0, 0); currentY += 54; }); currentY += 6; }); this.add.text(panelX, height - 92, 'Scegli la difficoltà quando vuoi iniziare: le preferenze audio vengono lette al momento della partita.', { ...BODY_STYLE, fontSize: '16px', color: '#cfe5cd', wordWrap: { width: width * 0.41 }, }).setOrigin(0, 0.5); } private createControlPanel(width: number, height: number, audioPreferences: ReturnType): void { const panelCenterX = width * 0.77; const difficultyOptions: DifficultyOption[] = [ { label: 'Principiante', subtitle: 'AI prudente e leggibile', value: 'beginner', palette: { base: 0x2e7d32, hover: 0x43a047 }, }, { label: 'Avanzato', subtitle: 'Pressione costante sul tavolo', value: 'advanced', palette: { base: 0xd97706, hover: 0xf59e0b }, }, { label: 'Maestro', subtitle: 'Massima lettura e priorità alle prese forti', value: 'master', palette: { base: 0xb91c1c, hover: 0xdc2626 }, }, ]; this.add.text(panelCenterX, 214, 'Inizia una partita', PANEL_TITLE_STYLE).setOrigin(0.5); this.add.text(panelCenterX, 250, 'Ogni partita usa la difficoltà scelta qui sotto e le preferenze audio salvate.', { ...BODY_STYLE, fontSize: '16px', color: '#d7ead1', align: 'center', wordWrap: { width: 250 }, }).setOrigin(0.5, 0); difficultyOptions.forEach((option, index) => { const y = 340 + index * 96; this.createButton(panelCenterX, y, 260, 64, option.label, option.subtitle, option.palette, () => { this.startGame(option.value); }); }); const musicLabel = audioPreferences.musicEnabled ? 'attiva' : 'disattivata'; const effectsLabel = audioPreferences.effectsEnabled ? 'attivi' : 'disattivati'; this.add.text(panelCenterX, height - 154, `Musica ${musicLabel} · Effetti ${effectsLabel}`, { ...BODY_STYLE, fontSize: '16px', color: '#cfe5cd', align: 'center', }).setOrigin(0.5); this.createButton( panelCenterX, height - 100, 260, 58, 'Impostazioni audio', 'Modifica musica ed effetti in modo indipendente', { base: 0x1f6f78, hover: 0x2f8f99 }, () => { this.openSettings(); }, ); } private createButton( x: number, y: number, width: number, height: number, label: string, subtitle: string, palette: MenuButtonPalette, onClick: () => void, ): void { const background = this.add.rectangle(x, y, width, height, palette.base, 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); background.on('pointerover', () => background.setFillStyle(palette.hover)); background.on('pointerout', () => background.setFillStyle(palette.base)); background.on('pointerdown', onClick); } private startGame(difficulty: Difficulty): void { const gameData: GameSceneData = { difficulty, audioPreferences: loadAudioPreferences(), }; this.cameras.main.fadeOut(300, 0, 30, 0); this.cameras.main.once('camerafadeoutcomplete', () => { this.scene.start('GameScene', gameData); }); } private openSettings(): void { this.ensureSettingsSceneAvailable(); this.cameras.main.fadeOut(250, 0, 30, 0); this.cameras.main.once('camerafadeoutcomplete', () => { this.scene.start('SettingsScene', { returnSceneKey: 'MenuScene' }); }); } private ensureSettingsSceneAvailable(): void { let existingScene: Phaser.Scene | null = null; try { existingScene = this.scene.get('SettingsScene'); } catch { existingScene = null; } if (existingScene instanceof SettingsScene) { return; } if (existingScene) { this.scene.remove('SettingsScene'); } this.scene.add('SettingsScene', SettingsScene, false); } }