bump: adjust card stats and fix drain death event handling

- Updated `data/cards.json`:
  - Increased `strike` values for several cards (6→9, 12→16, 24→25, 3→4, 5→12, 7→20, 10→28).
  - Increased `swipe` values (5→7, 7→10, 10→15).
  - Boosted `armor` for specific cards (7→10, 10→15, 15→20).

- Fixed `src/combat/CombatEngine.js`:
  - Corrected drain death event logic to properly identify the dead side and allies.
  - Added calls to `_processOnDeath` when a card or splash target is destroyed by drain effects.

- Enhanced `src/objects/CardObject.js`:
  - Updated `animateBerserkGain` to accept an optional `finalValue` parameter, ensuring the ATK text displays the correct value after the animation completes.

- Refined `src/scenes/BattleScene.js`:
  - Fixed ATK display logic for Swarm effects to correctly show the buffed value during the attack animation.
  - Integrated Hive Link animations into the death sequence for both standard attacks and pre-battle deaths, ensuring effects play before the next round proceeds.
This commit is contained in:
Brian Fertig 2026-03-21 11:18:15 -06:00
parent 35e995aff1
commit 776eb9c4bd
4 changed files with 48 additions and 24 deletions

View File

@ -571,7 +571,7 @@
},
{
"name": "strike",
"value": 6,
"value": 9,
"trigger": "preAttack"
}
]
@ -590,7 +590,7 @@
},
{
"name": "strike",
"value": 12,
"value": 16,
"trigger": "preAttack"
}
]
@ -609,7 +609,7 @@
},
{
"name": "strike",
"value": 24,
"value": 25,
"trigger": "preAttack"
}
]
@ -678,7 +678,7 @@
"skills": [
{
"name": "swipe",
"value": 5,
"value": 7,
"trigger": "preAttack"
}
]
@ -691,7 +691,7 @@
"skills": [
{
"name": "swipe",
"value": 7,
"value": 10,
"trigger": "preAttack"
}
]
@ -704,7 +704,7 @@
"skills": [
{
"name": "swipe",
"value": 10,
"value": 15,
"trigger": "preAttack"
}
]
@ -1400,7 +1400,7 @@
"skills": [
{
"name": "strike",
"value": 3,
"value": 4,
"trigger": "preAttack",
"all": true
}
@ -1409,12 +1409,12 @@
{
"attack": 14,
"health": 36,
"armor": 7,
"armor": 10,
"delay": 2,
"skills": [
{
"name": "strike",
"value": 5,
"value": 12,
"trigger": "preAttack",
"all": true
}
@ -1423,12 +1423,12 @@
{
"attack": 20,
"health": 54,
"armor": 10,
"armor": 15,
"delay": 2,
"skills": [
{
"name": "strike",
"value": 7,
"value": 20,
"trigger": "preAttack",
"all": true
}
@ -1437,12 +1437,12 @@
{
"attack": 30,
"health": 81,
"armor": 15,
"armor": 20,
"delay": 2,
"skills": [
{
"name": "strike",
"value": 10,
"value": 28,
"trigger": "preAttack",
"all": true
}

View File

@ -571,15 +571,22 @@ export class CombatEngine {
hpBefore, targetIsCommander, heal: ctx.drainHeal, secondaries
});
this._log(`${card.name} drains ${ctx.drainTarget.name} for ${ctx.drainDamage}`);
const _drainerIsPlayer = this.playerLanes.includes(card) || card === this.playerCommander;
if (!targetIsCommander && ctx.drainTarget.currentHP <= 0) {
this._log(`${ctx.drainTarget.name} is destroyed by drain`);
this.events.push({ type: 'death', card: ctx.drainTarget, side: this.playerLanes.includes(card) || card === this.playerCommander ? 'opponent' : 'player' });
const _deadSide = _drainerIsPlayer ? 'opponent' : 'player';
const _deadAllies = _drainerIsPlayer ? this.opponentLanes : this.playerLanes;
this.events.push({ type: 'death', card: ctx.drainTarget, side: _deadSide });
this._processOnDeath(ctx.drainTarget, _deadAllies, _deadSide);
}
for (const sec of secondaries) {
this._log(`${card.name} drain splashes ${sec.target.name} for ${sec.damage}`);
if (sec.target.currentHP <= 0) {
this._log(`${sec.target.name} is destroyed by drain splash`);
this.events.push({ type: 'death', card: sec.target, side: this.playerLanes.includes(card) || card === this.playerCommander ? 'opponent' : 'player' });
const _deadSide = _drainerIsPlayer ? 'opponent' : 'player';
const _deadAllies = _drainerIsPlayer ? this.opponentLanes : this.playerLanes;
this.events.push({ type: 'death', card: sec.target, side: _deadSide });
this._processOnDeath(sec.target, _deadAllies, _deadSide);
}
}
}

View File

@ -396,7 +396,7 @@ export class CardObject extends Phaser.GameObjects.Container {
});
}
animateBerserkGain(gain, onComplete) {
animateBerserkGain(gain, onComplete, finalValue = null) {
if (!this.atkText || !this.scene) { if (onComplete) onComplete(); return; }
const w = this.options.width || 80;
@ -433,7 +433,7 @@ export class CardObject extends Phaser.GameObjects.Container {
// Hold briefly, then update value and scale back
this.scene.time.delayedCall(500, () => {
if (!this.scene) { if (onComplete) onComplete(); return; }
this.atkText.setText(`${this.cardData.currentAttack}`);
this.atkText.setText(`${finalValue !== null ? finalValue : this.cardData.currentAttack}`);
this.scene.tweens.add({
targets: this.atkText,
scaleX: 1, scaleY: 1,

View File

@ -459,6 +459,16 @@ export class BattleScene extends Phaser.Scene {
this._animateAttack(round.attackEvent, () => {
this._refreshCardAfterAttack(round.attackEvent.defender);
this._refreshCardAfterAttack(round.attackEvent.attacker);
// If swarm buffed this attacker and will revert in postAttack, restore the
// buffed ATK display immediately — obj.refresh() above already reset it to the
// un-buffed value because the engine reverts currentAttack synchronously.
if (postAttackEvent?.swarmRevert && round.attackEvent.attacker) {
const rev = postAttackEvent.swarmRevert;
if (rev.card.instanceId === round.attackEvent.attacker.instanceId) {
const obj = this.cardObjects.get(rev.card.instanceId) ?? this.commanderObjects?.get(rev.card.instanceId);
if (obj?.atkText?.scene) obj.atkText.setText(`${rev.card.currentAttack + rev.amount}`);
}
}
this.time.delayedCall(220, () => {
this._processOnAttackAllEvents(round.onAttackAllEvents, () => {
animateRound(roundIdx + 1);
@ -703,12 +713,13 @@ export class BattleScene extends Phaser.Scene {
ease: 'Linear'
});
// Set ATK text to pre-buff value so animateBerserkGain shows the transition
if (obj.atkText) obj.atkText.setText(`${attacker.currentAttack - fire.gain}`);
// currentAttack is already reverted synchronously by processNextAttack, so it equals
// the pre-buff value (X). Start the animation from X and animate to X+gain.
if (obj.atkText) obj.atkText.setText(`${attacker.currentAttack}`);
obj.animateBerserkGain(fire.gain, () => {
if (swarmSprite.scene) swarmSprite.destroy();
onComplete();
});
}, attacker.currentAttack + fire.gain);
}
// Swarm fades after the card attacks: floating -N text communicates the ATK loss.
@ -1717,9 +1728,12 @@ export class BattleScene extends Phaser.Scene {
const afterImpact = () => {
if (targetObj.hpText && targetObj.scene) targetObj.hpText.setText(`${hpAfter}`);
if (fire.target.currentHP <= 0 && !fire.targetIsCommander) {
this._playCardDeath(targetObj, fire.target, () => {
animateNext(idx + 1, targetObj.x, targetObj.y);
});
const hlEvent = this._pendingHiveLinks?.[fire.target.instanceId];
if (hlEvent) delete this._pendingHiveLinks[fire.target.instanceId];
this._playCardDeath(targetObj, fire.target,
hlEvent ? () => this._animateHiveLink(hlEvent, () => animateNext(idx + 1, targetObj.x, targetObj.y))
: () => animateNext(idx + 1, targetObj.x, targetObj.y)
);
} else if (fire.carapaceGain > 0 && targetObj.scene) {
this._animateCarapaceEffect(targetObj, fire.carapaceGain, () => {
animateNext(idx + 1, targetObj.x, targetObj.y);
@ -2307,7 +2321,10 @@ export class BattleScene extends Phaser.Scene {
anyDeath = true;
pending++;
this._playCardDeath(ac.obj, ac.data, () => {
if (--pending === 0) onComplete();
const hlEvent = this._preBattleHiveLinks?.[ac.data.instanceId];
if (hlEvent) delete this._preBattleHiveLinks[ac.data.instanceId];
const afterAll = () => { if (--pending === 0) onComplete(); };
hlEvent ? this._animateHiveLink(hlEvent, afterAll) : afterAll();
});
}
}