feat(combat): Add pre-battle strike resolution for commanders

- Extend `CombatEngine._resolvePreBattleSkills` to capture `strike` and `strike-all` skills from commanders during the `preBattle` phase.
- Introduce `strikeFires` to track strike hits, including damage, targets, and commander status.
- Update `BattleScene._processPreBattleSequence` to handle and animate `strikeFires` alongside other pre-battle effects.
- Ensure death events are correctly processed for non-commander targets destroyed by pre-battle strikes.
This commit is contained in:
Brian Fertig 2026-03-23 22:54:39 -06:00
parent 313f14b956
commit 547196f709
2 changed files with 50 additions and 8 deletions

View File

@ -546,13 +546,16 @@ export class CombatEngine {
const sanctifyFires = []; const sanctifyFires = [];
const overchargeFires = []; const overchargeFires = [];
const fortifyFires = []; const fortifyFires = [];
const strikeFires = [];
const liveAllies = allies.filter(c => c.currentHP > 0); const liveAllies = allies.filter(c => c.currentHP > 0);
const liveEnemies = enemies.filter(c => c.currentHP > 0); const liveEnemies = enemies.filter(c => c.currentHP > 0);
for (const card of cards) { for (const card of cards) {
if (card.currentHP <= 0) continue; if (card.currentHP <= 0) continue;
if (card.currentDelay > 0) continue; if (card.currentDelay > 0) continue;
for (const s of card.skills) { for (const s of card.skills) {
if (s.trigger !== 'preBattle') continue; // Commanders fire their strike/strike-all during preBattle instead of preAttack
const isCommanderStrike = card.type === 'commander' && s.name === 'strike' && s.trigger === 'preAttack';
if (s.trigger !== 'preBattle' && !isCommanderStrike) continue;
// For drain, resolve the card directly across as defender // For drain, resolve the card directly across as defender
const cardIdx = cards.indexOf(card); const cardIdx = cards.indexOf(card);
const acrossCard = enemyLanes[cardIdx]; const acrossCard = enemyLanes[cardIdx];
@ -676,9 +679,36 @@ export class CombatEngine {
fortifyFires.push({ skill: 'fortify', source: card, target: ctx.fortifyTarget, amount: ctx.fortifyAmount }); fortifyFires.push({ skill: 'fortify', source: card, target: ctx.fortifyTarget, amount: ctx.fortifyAmount });
this._log(`${card.name} fortify: +${ctx.fortifyAmount} ARM to self`); this._log(`${card.name} fortify: +${ctx.fortifyAmount} ARM to self`);
} }
if (s.name === 'strike' && ctx.strikeAllTargets) {
const isPlayerCard = this.playerLanes.includes(card) || card === this.playerCommander;
for (const t of ctx.strikeAllTargets) {
const targetIsCommander = t.target === enemyCommander;
strikeFires.push({ skill: 'strike', source: card, target: t.target, damage: t.damage, hpBefore: t.hpBefore, targetIsCommander, isAll: true });
this._log(`${card.name} strike all hits ${t.target.name} for ${t.damage}`);
if (!targetIsCommander && t.target.currentHP <= 0) {
this._log(`${t.target.name} is destroyed by strike all`);
const deadSide = isPlayerCard ? 'opponent' : 'player';
const deadAllies = isPlayerCard ? this.opponentLanes : this.playerLanes;
this.events.push({ type: 'death', card: t.target, side: deadSide });
this._processOnDeath(t.target, deadAllies, deadSide);
}
}
} else if (s.name === 'strike' && ctx.strikeTarget) {
const isPlayerCard = this.playerLanes.includes(card) || card === this.playerCommander;
const targetIsCommander = ctx.strikeTarget === enemyCommander;
strikeFires.push({ skill: 'strike', source: card, target: ctx.strikeTarget, damage: ctx.strikeDamage, hpBefore: ctx.strikeTarget.currentHP + ctx.strikeDamage, targetIsCommander });
this._log(`${card.name} strike hits ${ctx.strikeTarget.name} for ${ctx.strikeDamage}`);
if (!targetIsCommander && ctx.strikeTarget.currentHP <= 0) {
this._log(`${ctx.strikeTarget.name} is destroyed by strike`);
const deadSide = isPlayerCard ? 'opponent' : 'player';
const deadAllies = isPlayerCard ? this.opponentLanes : this.playerLanes;
this.events.push({ type: 'death', card: ctx.strikeTarget, side: deadSide });
this._processOnDeath(ctx.strikeTarget, deadAllies, deadSide);
}
}
} }
} }
return { buffs, siegeFires, protectFires, enfeebeFires, jamFires, weakenFires, drainFires, healFires, sanctifyFires, overchargeFires, fortifyFires }; return { buffs, siegeFires, protectFires, enfeebeFires, jamFires, weakenFires, drainFires, healFires, sanctifyFires, overchargeFires, fortifyFires, strikeFires };
} }
// Emit the 8-step pre-battle sequence and process preBattle skills. // Emit the 8-step pre-battle sequence and process preBattle skills.

View File

@ -2260,19 +2260,22 @@ export class BattleScene extends Phaser.Scene {
const hasSanctify = event.sanctifyFires?.length > 0; const hasSanctify = event.sanctifyFires?.length > 0;
const hasOvercharge = event.overchargeFires?.length > 0; const hasOvercharge = event.overchargeFires?.length > 0;
const hasFortify = event.fortifyFires?.length > 0; const hasFortify = event.fortifyFires?.length > 0;
const hasStrike = event.strikeFires?.length > 0;
if (hasBuffs || hasSiege || hasProtect || hasEnfeeble || hasJam || hasWeaken || hasDrain || hasHeal || hasSanctify || hasOvercharge || hasFortify) { if (hasBuffs || hasSiege || hasProtect || hasEnfeeble || hasJam || hasWeaken || hasDrain || hasHeal || hasSanctify || hasOvercharge || hasFortify || hasStrike) {
this._processSanctifyFires(event.sanctifyFires || [], () => { this._processSanctifyFires(event.sanctifyFires || [], () => {
this._processDrainFires(event.drainFires || [], () => { this._processDrainFires(event.drainFires || [], () => {
this._processOverchargeFires(event.overchargeFires || [], () => { this._processOverchargeFires(event.overchargeFires || [], () => {
this._processBuffAnimations(event.buffs || [], () => { this._processBuffAnimations(event.buffs || [], () => {
this._processFortifyFires(event.fortifyFires || [], () => { this._processFortifyFires(event.fortifyFires || [], () => {
this._processSiegeFires(event.siegeFires || [], () => { this._processSiegeFires(event.siegeFires || [], () => {
this._processProtectFires(event.protectFires || [], () => { this._processPreBattleStrikeFires(event.strikeFires || [], () => {
this._processEnfeebeFires(event.enfeebeFires || [], () => { this._processProtectFires(event.protectFires || [], () => {
this._processJamFires(event.jamFires || [], () => { this._processEnfeebeFires(event.enfeebeFires || [], () => {
this._processWeakenFires(event.weakenFires || [], () => { this._processJamFires(event.jamFires || [], () => {
this._processHealFires(event.healFires || [], onComplete); this._processWeakenFires(event.weakenFires || [], () => {
this._processHealFires(event.healFires || [], onComplete);
});
}); });
}); });
}); });
@ -2288,6 +2291,15 @@ export class BattleScene extends Phaser.Scene {
} }
} }
_processPreBattleStrikeFires(fires, onComplete) {
const next = (idx) => {
if (idx >= fires.length) { onComplete(); return; }
const fire = fires[idx];
this._animateStrikeFire(fire.source, fire, () => next(idx + 1));
};
next(0);
}
_processSiegeFires(fires, onComplete) { _processSiegeFires(fires, onComplete) {
const next = (idx) => { const next = (idx) => {
if (idx >= fires.length) { onComplete(); return; } if (idx >= fires.length) { onComplete(); return; }