refactor(phase10): manage staging buttons via dedicated lifecycle methods

- Extract button creation and destruction into `_createStagingButtons` and `_destroyStagingButtons`.
- Ensure buttons are properly destroyed when staging is cleared, submitted, or when turn conditions change.
- Prevent memory leaks and stale UI state by centralizing button lifecycle management.
- Add piece click sound effect in Backgammon on landing.
This commit is contained in:
Brian Fertig 2026-05-17 14:44:45 -06:00
parent 208645246c
commit 238c744e93
2 changed files with 34 additions and 11 deletions

View File

@ -802,7 +802,10 @@ export default class BackgammonGame extends Phaser.Scene {
container.x = to.x;
container.y = to.y;
// Squash-and-stretch on landing
this.tweens.add({ targets: container, scaleX: 1.3, scaleY: 0.7, duration: 60, yoyo: true, ease: 'Quad.easeOut', onComplete });
this.tweens.add({ targets: container, scaleX: 1.3, scaleY: 0.7, duration: 60, yoyo: true, ease: 'Quad.easeOut', onComplete: () => {
playSound(this, SFX.PIECE_CLICK);
onComplete();
}});
},
});
}

View File

@ -152,6 +152,8 @@ export default class Phase10Game extends Phaser.Scene {
this.dragState = null;
this.potentialDrag = null;
this.stagingGroups = null;
this.submitBtn = null;
this.clearBtn = null;
}
create() {
@ -761,16 +763,6 @@ export default class Phase10Game extends Phaser.Scene {
cx += gw + GROUP_GAP;
}
const btnX = panelX + panelW + 160;
const submitBtn = new Button(this, btnX, laidStart.y - 18, 'Submit Phase', () => this.submitStagingLaydown(), {
width: 200, height: 44, fontSize: 18,
}).setDepth(D.ui);
this.transientObjs.push(submitBtn);
const clearBtn = new Button(this, btnX, laidStart.y + 34, 'Clear', () => this.clearStagingLaydown(), {
variant: 'ghost', width: 200, height: 34, fontSize: 14,
}).setDepth(D.ui);
this.transientObjs.push(clearBtn);
}
submitStagingLaydown() {
@ -798,6 +790,7 @@ export default class Phase10Game extends Phaser.Scene {
return;
}
this.stagingGroups = null;
this._destroyStagingButtons();
this.gs = next;
this.selectedHandIdx = null;
this.clearHighlights();
@ -954,6 +947,7 @@ export default class Phase10Game extends Phaser.Scene {
this.matchOver = false;
if (this.dragState) this.endCardDragImmediate();
this.stagingGroups = null;
this._destroyStagingButtons();
this.clearAllCardObjs();
this.clearHighlights();
this.selectedHandIdx = null;
@ -1241,6 +1235,7 @@ export default class Phase10Game extends Phaser.Scene {
const eligible = this.isLocalTurn() && this.gs.drawnThisTurn && !player.laidDown;
if (!eligible) {
this.stagingGroups = null;
this._destroyStagingButtons();
return;
}
if (!this.stagingGroups) {
@ -1251,9 +1246,34 @@ export default class Phase10Game extends Phaser.Scene {
count: g.count,
slots: new Array(g.count).fill(null),
}));
this._createStagingButtons();
}
}
_createStagingButtons() {
this._destroyStagingButtons();
const { laidStart } = slotLayout('bottom');
let totalW = 0;
for (let i = 0; i < this.stagingGroups.length; i++) {
if (i > 0) totalW += GROUP_GAP;
totalW += this.stagingGroups[i].count * (LAIDOWN_CARD_W * 0.75) + (LAIDOWN_CARD_W * 0.25);
}
const panelX = laidStart.x - 14;
const panelW = totalW + 28;
const btnX = panelX + panelW + 160;
this.submitBtn = new Button(this, btnX, laidStart.y - 18, 'Submit Phase', () => this.submitStagingLaydown(), {
width: 200, height: 44, fontSize: 18,
}).setDepth(D.ui);
this.clearBtn = new Button(this, btnX, laidStart.y + 34, 'Clear', () => this.clearStagingLaydown(), {
variant: 'ghost', width: 200, height: 34, fontSize: 14,
}).setDepth(D.ui);
}
_destroyStagingButtons() {
if (this.submitBtn) { this.submitBtn.destroy(); this.submitBtn = null; }
if (this.clearBtn) { this.clearBtn.destroy(); this.clearBtn = null; }
}
// ── Local input ─────────────────────────────────────────────────────────
isLocalTurn() {