Polish opponent select scene and restore category on return
- Replace flat title/subtitle pills with brass Plaque headers in OpponentSelectScene for visual consistency with the game menu - Shift opponent scroll area down (OPP_SCROLL_TOP 155 -> 180, height 440 -> 415) to clear the subtitle plaque band - Pass selected category back to GameMenu via scene.start data on Back so the active grid is restored instead of resetting to default - Add GameMenuScene.init() to capture incoming category and reset stale instance props (_currentCategory, _gridAnim) since scene.start reuses the existing instance - Guard fadeIn target list with .filter(Boolean) to skip any uncreated elements
This commit is contained in:
parent
74cd4df85b
commit
1298648e97
|
|
@ -27,6 +27,16 @@ const GRID_TRAVEL = GAME_WIDTH + 800;
|
||||||
export default class GameMenuScene extends Phaser.Scene {
|
export default class GameMenuScene extends Phaser.Scene {
|
||||||
constructor() { super('GameMenu'); }
|
constructor() { super('GameMenu'); }
|
||||||
|
|
||||||
|
init(data) {
|
||||||
|
// Set when coming back from the opponent picker — restore that category.
|
||||||
|
this._initialCategory = (data && data.category) || null;
|
||||||
|
// scene.start() reuses the existing scene instance, so instance props from
|
||||||
|
// the previous visit survive — reset them so this create() starts clean
|
||||||
|
// (otherwise showCategory()'s "same category" guard swallows the restore).
|
||||||
|
this._currentCategory = null;
|
||||||
|
this._gridAnim = null;
|
||||||
|
}
|
||||||
|
|
||||||
async create() {
|
async create() {
|
||||||
playMenuMusic();
|
playMenuMusic();
|
||||||
const cx = GAME_WIDTH / 2;
|
const cx = GAME_WIDTH / 2;
|
||||||
|
|
@ -121,9 +131,15 @@ export default class GameMenuScene extends Phaser.Scene {
|
||||||
|
|
||||||
this._backBtn = new Button(this, cx, GAME_HEIGHT - 60, 'Back', () => this.scene.start('Landing'), { variant: 'ghost' });
|
this._backBtn = new Button(this, cx, GAME_HEIGHT - 60, 'Back', () => this.scene.start('Landing'), { variant: 'ghost' });
|
||||||
|
|
||||||
|
// Returning from the opponent picker: restore the category that was
|
||||||
|
// active when the game was chosen — its grid flies back in from the right.
|
||||||
|
if (this._initialCategory) {
|
||||||
|
this.showCategory(this._initialCategory);
|
||||||
|
}
|
||||||
|
|
||||||
// The landing scene zooms the logo out before starting us, so fade the
|
// The landing scene zooms the logo out before starting us, so fade the
|
||||||
// menu controls in to complete the handoff.
|
// menu controls in to complete the handoff.
|
||||||
const fadeIn = [...Object.values(this._tabs), ...Object.values(this._tabIcons), this._hintText, this._backBtn];
|
const fadeIn = [...Object.values(this._tabs), ...Object.values(this._tabIcons), this._hintText, this._backBtn].filter(Boolean);
|
||||||
for (const obj of fadeIn) obj.setAlpha(0);
|
for (const obj of fadeIn) obj.setAlpha(0);
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
targets: fadeIn,
|
targets: fadeIn,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import * as Phaser from 'phaser';
|
import * as Phaser from 'phaser';
|
||||||
import { GAME_HEIGHT, GAME_WIDTH, COLORS } from '../config.js';
|
import { GAME_HEIGHT, GAME_WIDTH, COLORS } from '../config.js';
|
||||||
import { Button } from '../ui/Button.js';
|
import { Button } from '../ui/Button.js';
|
||||||
|
import { Plaque } from '../ui/Plaque.js';
|
||||||
import { playMenuMusic, stopMenuMusic, setMenuMusicVolume } from '../ui/MenuMusic.js';
|
import { playMenuMusic, stopMenuMusic, setMenuMusicVolume } from '../ui/MenuMusic.js';
|
||||||
import { enqueue as enqueueSpeech, resetQueue as resetSpeechQueue } from '../ui/SpeechQueue.js';
|
import { enqueue as enqueueSpeech, resetQueue as resetSpeechQueue } from '../ui/SpeechQueue.js';
|
||||||
|
|
||||||
|
|
@ -17,8 +18,8 @@ const CARD_TILE_GAP = 14;
|
||||||
|
|
||||||
// Opponent grid scroll area
|
// Opponent grid scroll area
|
||||||
const OPP_SCROLL_W = 1780;
|
const OPP_SCROLL_W = 1780;
|
||||||
const OPP_SCROLL_H = 440;
|
const OPP_SCROLL_H = 415;
|
||||||
const OPP_SCROLL_TOP = 155; // top edge of scroll area
|
const OPP_SCROLL_TOP = 180; // top edge of scroll area (below the subtitle plaque)
|
||||||
|
|
||||||
export default class OpponentSelectScene extends Phaser.Scene {
|
export default class OpponentSelectScene extends Phaser.Scene {
|
||||||
constructor() { super('OpponentSelect'); }
|
constructor() { super('OpponentSelect'); }
|
||||||
|
|
@ -58,21 +59,32 @@ export default class OpponentSelectScene extends Phaser.Scene {
|
||||||
this.add.image(cx, GAME_HEIGHT / 2, bgKey).setDisplaySize(GAME_WIDTH, GAME_HEIGHT).setDepth(-1);
|
this.add.image(cx, GAME_HEIGHT / 2, bgKey).setDisplaySize(GAME_WIDTH, GAME_HEIGHT).setDepth(-1);
|
||||||
this.add.rectangle(cx, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 0x000000, 0.45).setDepth(-1);
|
this.add.rectangle(cx, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 0x000000, 0.45).setDepth(-1);
|
||||||
|
|
||||||
const titleText = this.add.text(cx, 60, this.gameDef.name, {
|
// Brass-plaque headers, same treatment as the game menu headings. The
|
||||||
|
// subtitle plaque sits in the band between the title plaque and the
|
||||||
|
// opponent list (OPP_SCROLL_TOP) — keep those in sync if you move either.
|
||||||
|
const TITLE_Y = 55;
|
||||||
|
const PLATE_GAP = 10;
|
||||||
|
const titleText = this.add.text(cx, TITLE_Y, this.gameDef.name, {
|
||||||
fontFamily: 'Righteous',
|
fontFamily: 'Righteous',
|
||||||
fontSize: '52px',
|
fontSize: '52px',
|
||||||
color: COLORS.textHex,
|
color: COLORS.textHex,
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
const titlePill = this.add.rectangle(cx, 60, titleText.width + 48, titleText.height + 20, 0x000000, 0.72);
|
titleText.setLetterSpacing(2);
|
||||||
this.children.moveBelow(titlePill, titleText);
|
titleText.setShadow(0, 4, 'rgba(0,0,0,0.9)', 8);
|
||||||
|
const titlePlate = new Plaque(this, titleText.width + 64, titleText.height + 28).setPosition(cx, TITLE_Y);
|
||||||
|
this.children.moveBelow(titlePlate, titleText);
|
||||||
|
|
||||||
const subtitleText = this.add.text(cx, 122, 'Choose your opponent', {
|
const subtitleText = this.add.text(cx, 0, 'Choose your opponent', {
|
||||||
fontFamily: 'Righteous',
|
fontFamily: 'Righteous',
|
||||||
fontSize: '36px',
|
fontSize: '36px',
|
||||||
color: COLORS.mutedHex,
|
color: COLORS.mutedHex,
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
const subtitlePill = this.add.rectangle(cx, 122, subtitleText.width + 48, subtitleText.height + 20, 0x000000, 0.72);
|
subtitleText.setShadow(0, 3, 'rgba(0,0,0,0.8)', 5);
|
||||||
this.children.moveBelow(subtitlePill, subtitleText);
|
const subPlateH = subtitleText.height + 28;
|
||||||
|
const subtitleY = TITLE_Y + (titleText.height + 28) / 2 + PLATE_GAP + subPlateH / 2;
|
||||||
|
subtitleText.setY(subtitleY);
|
||||||
|
const subtitlePlate = new Plaque(this, subtitleText.width + 64, subPlateH, { radius: 16 }).setPosition(cx, subtitleY);
|
||||||
|
this.children.moveBelow(subtitlePlate, subtitleText);
|
||||||
|
|
||||||
let opponents = [];
|
let opponents = [];
|
||||||
try {
|
try {
|
||||||
|
|
@ -87,7 +99,7 @@ export default class OpponentSelectScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
const min = this.gameDef.minOpponents ?? 1;
|
const min = this.gameDef.minOpponents ?? 1;
|
||||||
new Button(this, cx - 150, 1013, 'Back', () => this.scene.start('GameMenu'), {
|
new Button(this, cx - 150, 1013, 'Back', () => this.scene.start('GameMenu', { category: this.gameDef.category }), {
|
||||||
variant: 'ghost',
|
variant: 'ghost',
|
||||||
width: 280,
|
width: 280,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue