feat(combat): add 'overwhelm' win condition with visual effects and result display
- Introduces `winType` field to track victory conditions (commander/depletion/overwhelm) - Implements `_overwhelmCheck()` logic to detect when one side exhausts all resources while the other retains combat capability - Adds `_animateOverwhelm()` sequence: banner, rally, strikes, HP drain, and commander defeat with shake/flash/particle effects - Updates `BattleResultScene` to show "VICTORY BY OVERWHELM!" / "OVERWHELMED" titles and annotate report accordingly
This commit is contained in:
parent
d1a5deb7ed
commit
3e8c4ab7b8
|
|
@ -219,21 +219,21 @@ export class BattleScene extends Phaser.Scene {
|
||||||
this.playerGoesFirst = true;
|
this.playerGoesFirst = true;
|
||||||
this.initiativeIndicator = null;
|
this.initiativeIndicator = null;
|
||||||
|
|
||||||
const autoBtn = this.add.rectangle(1680, 35, 180, 44, 0x224422)
|
this.autoBtn = this.add.rectangle(1680, 35, 180, 44, 0x224422)
|
||||||
.setInteractive({ useHandCursor: true })
|
.setInteractive({ useHandCursor: true })
|
||||||
.setStrokeStyle(1, 0x44aa44);
|
.setStrokeStyle(1, 0x44aa44);
|
||||||
this.autoBtnText = this.add.text(1680, 35, 'Auto: OFF', {
|
this.autoBtnText = this.add.text(1680, 35, 'Auto: OFF', {
|
||||||
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
|
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
autoBtn.on('pointerdown', () => this._toggleAuto());
|
this.autoBtn.on('pointerdown', () => this._toggleAuto());
|
||||||
|
|
||||||
const nextBtn = this.add.rectangle(1840, 35, 180, 44, 0x1a3a5c)
|
this.nextBtn = this.add.rectangle(1840, 35, 180, 44, 0x1a3a5c)
|
||||||
.setInteractive({ useHandCursor: true })
|
.setInteractive({ useHandCursor: true })
|
||||||
.setStrokeStyle(1, 0x4488ff);
|
.setStrokeStyle(1, 0x4488ff);
|
||||||
this.add.text(1840, 35, 'Next Turn', {
|
this.nextBtnText = this.add.text(1840, 35, 'Next Turn', {
|
||||||
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
|
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
nextBtn.on('pointerdown', () => this._beginTurn());
|
this.nextBtn.on('pointerdown', () => this._beginTurn());
|
||||||
|
|
||||||
// Keyboard
|
// Keyboard
|
||||||
this.input.keyboard.on('keydown-SPACE', () => this._beginTurn());
|
this.input.keyboard.on('keydown-SPACE', () => this._beginTurn());
|
||||||
|
|
@ -761,6 +761,7 @@ export class BattleScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
_beginTurn() {
|
_beginTurn() {
|
||||||
|
this._clearNextTurnHint();
|
||||||
if (this._tutorialActive) return;
|
if (this._tutorialActive) return;
|
||||||
if (this.waitingForPick || this.isAnimating) return;
|
if (this.waitingForPick || this.isAnimating) return;
|
||||||
if (this.engine.winner) { this._showResult(); return; }
|
if (this.engine.winner) { this._showResult(); return; }
|
||||||
|
|
@ -924,6 +925,7 @@ export class BattleScene extends Phaser.Scene {
|
||||||
this._animateInitiativeHandoff();
|
this._animateInitiativeHandoff();
|
||||||
this.isAnimating = false;
|
this.isAnimating = false;
|
||||||
this.statusText.setText('Press SPACE or click Next Turn to advance');
|
this.statusText.setText('Press SPACE or click Next Turn to advance');
|
||||||
|
if (!this.autoPlay) this._startNextTurnHint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
@ -4738,10 +4740,76 @@ export class BattleScene extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_startNextTurnHint() {
|
||||||
|
this._clearNextTurnHint();
|
||||||
|
const btnX = 1840, btnY = 35;
|
||||||
|
|
||||||
|
// After 2s: yellow arrow that bobs up and down below the Next Turn button
|
||||||
|
this._nextTurnHintTimer1 = this.time.delayedCall(2000, () => {
|
||||||
|
if (this.autoPlay || this.isAnimating || this.waitingForPick) return;
|
||||||
|
this._nextTurnArrow = this.add.text(btnX, btnY + 50, '▲', {
|
||||||
|
fontSize: '32px', color: '#ffdd00', fontFamily: 'Audiowide'
|
||||||
|
}).setOrigin(0.5).setDepth(101);
|
||||||
|
|
||||||
|
this.tweens.add({
|
||||||
|
targets: this._nextTurnArrow,
|
||||||
|
y: { from: btnY + 44, to: btnY + 58 },
|
||||||
|
duration: 450,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: -1,
|
||||||
|
ease: 'Sine.easeInOut'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// After 5s: dim the screen, lift the buttons above the overlay, show hint text
|
||||||
|
this._nextTurnHintTimer2 = this.time.delayedCall(5000, () => {
|
||||||
|
if (this.autoPlay || this.isAnimating || this.waitingForPick) return;
|
||||||
|
|
||||||
|
// Full-screen dim overlay
|
||||||
|
const { width, height } = this.scale;
|
||||||
|
this._nextTurnDim = this.add.rectangle(width / 2, height / 2, width, height, 0x000000, 0.55)
|
||||||
|
.setDepth(98);
|
||||||
|
|
||||||
|
// Lift buttons above the dim
|
||||||
|
this.autoBtn.setDepth(99);
|
||||||
|
this.autoBtnText.setDepth(99);
|
||||||
|
this.nextBtn.setDepth(99);
|
||||||
|
this.nextBtnText.setDepth(99);
|
||||||
|
|
||||||
|
// Bring arrow above the dim too
|
||||||
|
if (this._nextTurnArrow) this._nextTurnArrow.setDepth(101);
|
||||||
|
|
||||||
|
// Hint text — right-aligned so it stays within the screen
|
||||||
|
this._nextTurnHintLabel = this.add.text(1910, btnY + 95, "Click 'Next' to begin the next turn", {
|
||||||
|
fontSize: '16px', color: '#ffffff', fontFamily: 'Audiowide'
|
||||||
|
}).setOrigin(1, 0.5).setDepth(101);
|
||||||
|
|
||||||
|
this.tweens.add({
|
||||||
|
targets: this._nextTurnHintLabel,
|
||||||
|
alpha: { from: 1, to: 0.3 },
|
||||||
|
duration: 800,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: -1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearNextTurnHint() {
|
||||||
|
if (this._nextTurnHintTimer1) { this._nextTurnHintTimer1.remove(); this._nextTurnHintTimer1 = null; }
|
||||||
|
if (this._nextTurnHintTimer2) { this._nextTurnHintTimer2.remove(); this._nextTurnHintTimer2 = null; }
|
||||||
|
if (this._nextTurnArrow) { this.tweens.killTweensOf(this._nextTurnArrow); this._nextTurnArrow.destroy(); this._nextTurnArrow = null; }
|
||||||
|
if (this._nextTurnHintLabel) { this.tweens.killTweensOf(this._nextTurnHintLabel); this._nextTurnHintLabel.destroy(); this._nextTurnHintLabel = null; }
|
||||||
|
if (this._nextTurnDim) { this._nextTurnDim.destroy(); this._nextTurnDim = null; }
|
||||||
|
// Reset button depths
|
||||||
|
if (this.autoBtn) { this.autoBtn.setDepth(0); this.autoBtnText.setDepth(0); }
|
||||||
|
if (this.nextBtn) { this.nextBtn.setDepth(0); this.nextBtnText.setDepth(0); }
|
||||||
|
}
|
||||||
|
|
||||||
_toggleAuto() {
|
_toggleAuto() {
|
||||||
this.autoPlay = !this.autoPlay;
|
this.autoPlay = !this.autoPlay;
|
||||||
this.autoBtnText.setText(`Auto: ${this.autoPlay ? 'ON' : 'OFF'}`);
|
this.autoBtnText.setText(`Auto: ${this.autoPlay ? 'ON' : 'OFF'}`);
|
||||||
if (this.autoPlay) {
|
if (this.autoPlay) {
|
||||||
|
this._clearNextTurnHint();
|
||||||
this.autoTimer = this.time.addEvent({
|
this.autoTimer = this.time.addEvent({
|
||||||
delay: 300,
|
delay: 300,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue