feat(combat): rename "valor" to "legion" and add parallel "all" weaken animation

- Updated `data/cards.json` to rename the `valor` skill to `legion`, change its trigger to `preBattle`, and adjust `strike` values across multiple card tiers.
- Reduced the `enfeeble` skill value from 3 to 1 in `data/cards.json`.
- Enhanced `weaken` skill tracking in `CombatEngine.js` and `SkillProcessor.js` by adding an `isAll` flag to distinguish area-of-effect weaken effects.
- Refactored `BattleScene.js` to group consecutive "all" weaken fires and animate them in parallel via a new `_animateWeakenFireAll` method, improving visual feedback and performance for multi-target weaken effects.
This commit is contained in:
Brian Fertig 2026-03-24 18:46:16 -06:00
parent b2c55d903a
commit 52808806a8
4 changed files with 108 additions and 20 deletions

View File

@ -1071,13 +1071,13 @@
"delay": 1,
"skills": [
{
"name": "valor",
"name": "legion",
"value": 2,
"trigger": "passive"
"trigger": "preBattle"
},
{
"name": "strike",
"value": 1,
"value": 2,
"trigger": "preAttack"
}
]
@ -1089,13 +1089,13 @@
"delay": 1,
"skills": [
{
"name": "valor",
"name": "legion",
"value": 3,
"trigger": "passive"
"trigger": "preBattle"
},
{
"name": "strike",
"value": 2,
"value": 6,
"trigger": "preAttack"
}
]
@ -1107,13 +1107,13 @@
"delay": 1,
"skills": [
{
"name": "valor",
"name": "legion",
"value": 5,
"trigger": "passive"
"trigger": "preBattle"
},
{
"name": "strike",
"value": 3,
"value": 12,
"trigger": "preAttack"
}
]
@ -1125,13 +1125,13 @@
"delay": 1,
"skills": [
{
"name": "valor",
"name": "legion",
"value": 7,
"trigger": "passive"
"trigger": "preBattle"
},
{
"name": "strike",
"value": 5,
"value": 24,
"trigger": "preAttack"
}
]
@ -1143,13 +1143,13 @@
"delay": 1,
"skills": [
{
"name": "valor",
"name": "legion",
"value": 10,
"trigger": "passive"
"trigger": "preBattle"
},
{
"name": "strike",
"value": 7,
"value": 48,
"trigger": "preAttack"
}
]
@ -2228,7 +2228,7 @@
"skills": [
{
"name": "enfeeble",
"value": 3,
"value": 1,
"all": true,
"trigger": "preBattle"
},

View File

@ -605,7 +605,7 @@ export class CombatEngine {
}
if (s.name === 'weaken' && ctx.weakenFires?.length) {
for (const wf of ctx.weakenFires) {
weakenFires.push({ skill: 'weaken', source: wf.source, target: wf.target, armorAmount: wf.armorAmount, attackAmount: wf.attackAmount });
weakenFires.push({ skill: 'weaken', source: wf.source, target: wf.target, armorAmount: wf.armorAmount, attackAmount: wf.attackAmount, isAll: wf.isAll });
this._log(`${card.name} weaken reduces ${wf.target.name} ARM by ${wf.armorAmount} and ATK by ${wf.attackAmount}`);
}
}

View File

@ -302,7 +302,7 @@ export class SkillProcessor {
if (targets.length === 0) return;
for (const t of targets) {
const { armorAmt, attackAmt } = _applyWeaken(t);
weakenFires.push({ source: attacker, target: t, armorAmount: armorAmt, attackAmount: attackAmt });
weakenFires.push({ source: attacker, target: t, armorAmount: armorAmt, attackAmount: attackAmt, isAll: true });
}
if (context) context.weakenFires = weakenFires;
return;

View File

@ -2353,9 +2353,28 @@ export class BattleScene extends Phaser.Scene {
}
_processWeakenFires(fires, onComplete) {
// Group consecutive "all" weaken fires for parallel animation
const groups = [];
for (const fire of fires) {
if (fire.isAll) {
const last = groups[groups.length - 1];
if (last?.isAll) {
last.items.push(fire);
} else {
groups.push({ isAll: true, items: [fire] });
}
} else {
groups.push(fire);
}
}
const next = (idx) => {
if (idx >= fires.length) { onComplete(); return; }
this._animateWeakenFire(fires[idx], () => next(idx + 1));
if (idx >= groups.length) { onComplete(); return; }
const group = groups[idx];
if (group.isAll) {
this._animateWeakenFireAll(group.items, () => next(idx + 1));
} else {
this._animateWeakenFire(group, () => next(idx + 1));
}
};
next(0);
}
@ -3592,6 +3611,75 @@ export class BattleScene extends Phaser.Scene {
});
}
_animateWeakenFireAll(fires, onComplete) {
if (!fires?.length) { onComplete(); return; }
const _lookup = id => id && (this.cardObjects.get(id) || this.commanderObjects?.get(id));
const sourceObj = _lookup(fires[0].source.instanceId);
this.statusText.setText(`${fires[0].source.name} weakens all enemies!`);
this.sound.play('sfx_weaken', { volume: 0.8 });
const BASE_SCALE = 160 / 460;
let remaining = fires.length;
const done = () => { if (--remaining <= 0) onComplete(); };
for (const fire of fires) {
const targetObj = _lookup(fire.target.instanceId);
if (!targetObj?.scene) { done(); continue; }
const armorAmt = fire.armorAmount;
const attackAmt = fire.attackAmount;
if (armorAmt <= 0 && attackAmt <= 0) { done(); continue; }
if (!sourceObj?.scene) {
targetObj.refresh();
targetObj.flash(0x8800ff);
this.time.delayedCall(400, done);
continue;
}
const weakenSprite = this.add.sprite(sourceObj.x, sourceObj.y, 'attacks', 43)
.setScale(BASE_SCALE)
.setDepth(30);
this.tweens.add({
targets: weakenSprite,
x: targetObj.x,
y: targetObj.y,
duration: 1350,
ease: 'Cubic.easeIn',
onComplete: () => {
if (!targetObj.scene) { if (weakenSprite.scene) weakenSprite.destroy(); done(); return; }
this.tweens.add({
targets: weakenSprite,
alpha: 0,
scaleX: BASE_SCALE * 1.4,
scaleY: BASE_SCALE * 1.4,
duration: 300,
ease: 'Power2',
onComplete: () => { if (weakenSprite.scene) weakenSprite.destroy(); }
});
let pending = (armorAmt > 0 ? 1 : 0) + (attackAmt > 0 ? 1 : 0);
if (pending === 0) { done(); return; }
const statDone = () => { if (--pending === 0) done(); };
if (armorAmt > 0) {
const fromARM = fire.target.currentArmor + armorAmt;
const toARM = fire.target.currentArmor;
targetObj.animateArmorLoss(armorAmt, statDone, fromARM, toARM);
}
if (attackAmt > 0) {
const fromATK = fire.target.currentAttack + attackAmt;
const toATK = fire.target.currentAttack;
this._animateAttackLoss(targetObj, attackAmt, fromATK, toATK, statDone);
}
}
});
}
}
// Re-apply jam reductions after _renderState() so combat math stays correct.
_reapplyJamSkillReductions(preBattleEvents) {
for (const event of preBattleEvents) {