| /** | |
| * ============================================================================ | |
| * TEMPLATE: Character Selection Scene | |
| * ============================================================================ | |
| * | |
| * INSTRUCTIONS FOR AGENT: | |
| * 1. Copy this file and rename (e.g., HeroSelectScene.ts) | |
| * 2. Rename the class and constructor scene key | |
| * 3. Implement getSelectableCharacters() with your game's characters | |
| * 4. Set getNextSceneKey() to your first game scene | |
| * 5. Override hooks as needed for custom UI | |
| * | |
| * CRITICAL RULES: | |
| * - This extends BaseCharacterSelectScene. All hooks are available. | |
| * - Override getSelectableCharacters() (REQUIRED). | |
| * - Override getNextSceneKey() to navigate to your battle/chapter scene. | |
| * - For PVP (2 players pick sequentially): | |
| * Override shouldAutoTransition() to return false. | |
| * Track P1/P2 picks manually, then call triggerTransition(). | |
| * | |
| * REGISTRY: | |
| * - On confirm, base class stores: registry.set('selectedCharacter', character) | |
| * - Retrieve in battle scene: this.registry.get('selectedCharacter') | |
| * - For PVP, store per-player: | |
| * this.registry.set('p1Character', character) | |
| * this.registry.set('p2Character', character) | |
| * | |
| * TYPE IMPORTS: | |
| * All interfaces/types MUST use the "type" keyword: | |
| * import { BaseCharacterSelectScene, type SelectableCharacter } from './BaseCharacterSelectScene'; | |
| * | |
| * FILE CHECKLIST (complete AFTER implementing this scene): | |
| * [ ] main.ts — import and register this scene with game.scene.add() | |
| * [ ] LevelManager.ts — add scene key to LEVEL_ORDER | |
| * [ ] asset-pack.json — register ALL character portrait/expression texture keys | |
| * [ ] gameConfig.json — merge characterSelectConfig values if customizing grid | |
| * ============================================================================ | |
| */ | |
| import { | |
| BaseCharacterSelectScene, | |
| type SelectableCharacter, | |
| } from './BaseCharacterSelectScene'; | |
| export class _TemplateCharacterSelect extends BaseCharacterSelectScene { | |
| constructor() { | |
| super({ key: '_TemplateCharacterSelect' }); // TODO: Replace with your scene key | |
| } | |
| // ============================================================================ | |
| // REQUIRED: Define selectable characters | |
| // ============================================================================ | |
| protected override getSelectableCharacters(): SelectableCharacter[] { | |
| return [ | |
| // TODO: Define your characters | |
| // { | |
| // id: 'warrior', | |
| // name: 'Warrior', | |
| // description: 'A brave warrior with strong defense', | |
| // imageKey: 'warrior_portrait', // Must match asset-pack.json key | |
| // stats: { hp: 120, atk: 15, def: 25 }, | |
| // metadata: { | |
| // expressions: { | |
| // neutral: 'warrior_neutral', | |
| // angry: 'warrior_angry', | |
| // victory: 'warrior_victory', | |
| // }, | |
| // }, | |
| // }, | |
| // { | |
| // id: 'mage', | |
| // name: 'Mage', | |
| // description: 'Master of arcane spells', | |
| // imageKey: 'mage_portrait', | |
| // stats: { hp: 80, atk: 25, def: 10 }, | |
| // metadata: { | |
| // expressions: { | |
| // neutral: 'mage_neutral', | |
| // angry: 'mage_angry', | |
| // victory: 'mage_victory', | |
| // }, | |
| // }, | |
| // }, | |
| ]; | |
| } | |
| // ============================================================================ | |
| // REQUIRED: Navigation | |
| // ============================================================================ | |
| protected override getNextSceneKey(): string { | |
| return 'ChapterSelectScene'; // TODO: Replace with your first game scene | |
| } | |
| // ============================================================================ | |
| // OPTIONAL: Scene setup hooks | |
| // ============================================================================ | |
| protected override createBackground(): void { | |
| const cam = this.cameras.main; | |
| // TODO: Use your background image | |
| // if (this.textures.exists('select_bg')) { | |
| // const bg = this.add.image(cam.width / 2, cam.height / 2, 'select_bg'); | |
| // bg.setDisplaySize(cam.width, cam.height); | |
| // } else { | |
| this.add.rectangle( | |
| cam.width / 2, | |
| cam.height / 2, | |
| cam.width, | |
| cam.height, | |
| 0x1a1a2e, | |
| ); | |
| // } | |
| } | |
| protected override createTitle(): void { | |
| const cam = this.cameras.main; | |
| // IMPORTANT: Assign to this.titleText so PVP mode can update it. | |
| this.titleText = this.add | |
| .text(cam.width / 2, 50, 'SELECT YOUR CHARACTER', { | |
| fontSize: '36px', | |
| fontFamily: 'Arial', | |
| color: '#ffffff', | |
| stroke: '#000000', | |
| strokeThickness: 4, | |
| fontStyle: 'bold', | |
| }) | |
| .setOrigin(0.5); | |
| } | |
| protected override getBackgroundMusicKey(): string | undefined { | |
| // TODO: Return audio key for background music | |
| // return this.cache.audio.exists('select_bgm') ? 'select_bgm' : undefined; | |
| return undefined; | |
| } | |
| protected override createCustomUI(): void { | |
| // TODO: Add custom UI elements (decorations, instructions, etc.) | |
| } | |
| protected override onCharacterSelected(character: SelectableCharacter): void { | |
| // Base class already stores: registry.set('selectedCharacter', character) | |
| // Use 'selectedCharacter' to retrieve in battle scene: | |
| // const char = this.registry.get('selectedCharacter'); | |
| // TODO: Play selection SFX | |
| // if (this.cache.audio.exists('confirm_sfx')) { | |
| // this.sound.play('confirm_sfx'); | |
| // } | |
| } | |
| // ============================================================================ | |
| // OPTIONAL: Grid customization | |
| // ============================================================================ | |
| // protected override getGridConfig(): GridConfig { | |
| // return { | |
| // maxColumns: 5, // 5 columns instead of default 4 | |
| // cardWidth: 160, // Slightly narrower cards | |
| // cardHeight: 220, | |
| // gapX: 15, | |
| // gapY: 15, | |
| // }; | |
| // } | |
| // ============================================================================ | |
| // OPTIONAL: Audio hooks | |
| // ============================================================================ | |
| // protected override playSelectSound(): void { | |
| // if (this.cache.audio.exists('ui_select')) { | |
| // this.sound.play('ui_select', { volume: 0.3 }); | |
| // } | |
| // } | |
| // protected override playConfirmSound(): void { | |
| // if (this.cache.audio.exists('ui_confirm')) { | |
| // this.sound.play('ui_confirm', { volume: 0.5 }); | |
| // } | |
| // } | |
| // ============================================================================ | |
| // PVP SEQUENTIAL PICK PATTERN (optional) | |
| // ============================================================================ | |
| // Uncomment for games where P1 and P2 each pick a character: | |
| // | |
| // private pickingPlayer: 'P1' | 'P2' = 'P1'; | |
| // | |
| // protected override shouldAutoTransition(): boolean { | |
| // return false; // We handle transition manually | |
| // } | |
| // | |
| // protected override onCharacterSelected(character: SelectableCharacter): void { | |
| // const pickedIndex = this.selectedIndex; // Save before reset | |
| // if (this.pickingPlayer === 'P1') { | |
| // this.registry.set('p1Character', character); | |
| // this.pickingPlayer = 'P2'; | |
| // // Gray out P1's card, reset highlight to first available card | |
| // this.resetForNextPick(pickedIndex); | |
| // // Update title for P2 | |
| // if (this.titleText) { | |
| // this.titleText.setText('PLAYER 2: SELECT CHARACTER'); | |
| // } | |
| // } else { | |
| // this.registry.set('p2Character', character); | |
| // this.triggerTransition(); // Both players picked, go to battle | |
| // } | |
| // } | |
| } | |