refactor: rename "rupture" to "wound" and "sanctify" to "cleanse"
- Renamed skill name from rupture → wound throughout data/skills.json and data/cards.json (120+ occurrences) - Updated all related logic in CombatEngine.js and SkillProcessor.js: * Phase renamed _rupturePhase → _woundPhase, event types updated * Tracker renamed ruptureTracker → woundTracker * Card property ruptureStacks → woundStacks - Renamed skill name from sanctify → cleanse, changed trigger to postBattle and removed all-target support (now always cleanses all allies) - Updated visual assets: badge text, sound keys (sfx_rupture → sfx_wound, sfx_sanctify → sfx_cleanse), status messages - Adjusted strike skill values in cards.json (5→8, 7→14, 10→20, 15→30)
This commit is contained in:
parent
2ea8857f91
commit
2cfba2a893
163
data/cards.json
163
data/cards.json
|
|
@ -2233,7 +2233,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 2,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -2252,7 +2252,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 4,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -2271,7 +2271,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 6,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -2290,7 +2290,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 9,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -2309,7 +2309,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 13,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3072,7 +3072,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 1,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3090,7 +3090,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 2,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3108,7 +3108,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 2,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3126,7 +3126,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 3,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3144,7 +3144,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 5,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3456,7 +3456,7 @@
|
|||
"trigger": "passive"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 2,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3474,7 +3474,7 @@
|
|||
"trigger": "passive"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 3,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3492,7 +3492,7 @@
|
|||
"trigger": "passive"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 5,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3510,7 +3510,7 @@
|
|||
"trigger": "passive"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 7,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3528,7 +3528,7 @@
|
|||
"trigger": "passive"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 10,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3658,7 +3658,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 3,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3676,7 +3676,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 5,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3694,7 +3694,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 8,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3712,7 +3712,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 11,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -3730,7 +3730,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 15,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -4583,7 +4583,7 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 1,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -4596,7 +4596,7 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 2,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -4609,7 +4609,7 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 3,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -4622,7 +4622,7 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 4,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -4635,7 +4635,7 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 5,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5068,7 +5068,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 2,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5086,7 +5086,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 3,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5104,7 +5104,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 6,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5122,7 +5122,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 10,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5140,7 +5140,7 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 15,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5472,7 +5472,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 2,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5490,7 +5490,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 3,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5508,7 +5508,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 5,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5526,7 +5526,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 7,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -5544,7 +5544,7 @@
|
|||
"trigger": "preAttack"
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"name": "wound",
|
||||
"value": 10,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
|
|
@ -7831,10 +7831,9 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 1,
|
||||
"all": true,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -7845,10 +7844,9 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 2,
|
||||
"all": true,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -7859,10 +7857,9 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 3,
|
||||
"all": true,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -7873,10 +7870,9 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 5,
|
||||
"all": true,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -7887,10 +7883,9 @@
|
|||
"delay": 0,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 7,
|
||||
"all": true,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -9030,9 +9025,9 @@
|
|||
"delay": 2,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 1,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
},
|
||||
{
|
||||
"name": "strike",
|
||||
|
|
@ -9048,9 +9043,9 @@
|
|||
"delay": 2,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 2,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
},
|
||||
{
|
||||
"name": "strike",
|
||||
|
|
@ -9066,9 +9061,9 @@
|
|||
"delay": 2,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 3,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
},
|
||||
{
|
||||
"name": "strike",
|
||||
|
|
@ -9084,9 +9079,9 @@
|
|||
"delay": 2,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 5,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
},
|
||||
{
|
||||
"name": "strike",
|
||||
|
|
@ -9102,9 +9097,9 @@
|
|||
"delay": 2,
|
||||
"skills": [
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 7,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
},
|
||||
{
|
||||
"name": "strike",
|
||||
|
|
@ -9343,9 +9338,9 @@
|
|||
"trigger": "on_attack"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 1,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9366,9 +9361,9 @@
|
|||
"trigger": "on_attack"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 2,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9389,9 +9384,9 @@
|
|||
"trigger": "on_attack"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 3,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9412,9 +9407,9 @@
|
|||
"trigger": "on_attack"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 5,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9435,9 +9430,9 @@
|
|||
"trigger": "on_attack"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 7,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -9493,7 +9488,7 @@
|
|||
},
|
||||
{
|
||||
"name": "strike",
|
||||
"value": 5,
|
||||
"value": 8,
|
||||
"trigger": "preAttack"
|
||||
}
|
||||
]
|
||||
|
|
@ -9516,7 +9511,7 @@
|
|||
},
|
||||
{
|
||||
"name": "strike",
|
||||
"value": 7,
|
||||
"value": 14,
|
||||
"trigger": "preAttack"
|
||||
}
|
||||
]
|
||||
|
|
@ -9539,7 +9534,7 @@
|
|||
},
|
||||
{
|
||||
"name": "strike",
|
||||
"value": 10,
|
||||
"value": 20,
|
||||
"trigger": "preAttack"
|
||||
}
|
||||
]
|
||||
|
|
@ -9562,7 +9557,7 @@
|
|||
},
|
||||
{
|
||||
"name": "strike",
|
||||
"value": 15,
|
||||
"value": 30,
|
||||
"trigger": "preAttack"
|
||||
}
|
||||
]
|
||||
|
|
@ -9596,9 +9591,9 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 1,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9620,9 +9615,9 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 2,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9644,9 +9639,9 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 3,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9668,9 +9663,9 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 5,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -9692,9 +9687,9 @@
|
|||
"trigger": "preBattle"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"name": "cleanse",
|
||||
"value": 7,
|
||||
"trigger": "preBattle"
|
||||
"trigger": "postBattle"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
"supportsAll": true
|
||||
},
|
||||
{
|
||||
"name": "rupture",
|
||||
"description": "Apply rupture stacks to target. Each stack deals value damage at the start of the turn.",
|
||||
"allDescription": "Apply rupture stacks to ALL enemy cards. Each stack deals value damage at the start of the turn.",
|
||||
"name": "wound",
|
||||
"description": "Apply wound stacks to target. Each stack deals value damage at the start of the turn.",
|
||||
"allDescription": "Apply wound stacks to ALL enemy cards. Each stack deals value damage at the start of the turn.",
|
||||
"trigger": "on_attack",
|
||||
"category": "offense",
|
||||
"supportsAll": true
|
||||
|
|
@ -210,8 +210,8 @@
|
|||
"category": "offense"
|
||||
},
|
||||
{
|
||||
"name": "sanctify",
|
||||
"description": "Before battle: cleanse all negative effects (venom, smite, enfeeble, jam, rupture) from self.",
|
||||
"name": "cleanse",
|
||||
"description": "Before battle: cleanse all negative effects (venom, smite, enfeeble, jam, wound) from self.",
|
||||
"allDescription": "Before battle: cleanse all negative effects from ALL allied cards.",
|
||||
"trigger": "preBattle",
|
||||
"category": "support",
|
||||
|
|
|
|||
|
|
@ -71,28 +71,28 @@ export class CombatEngine {
|
|||
tick(this.opponentLanes);
|
||||
}
|
||||
|
||||
// Apply rupture damage to all cards, emitting ruptureTick events (mirrors venomPhase)
|
||||
_rupturePhase() {
|
||||
const applyRupture = (cards, alliedLanes, side) => {
|
||||
// Apply wound damage to all cards, emitting woundTick events (mirrors venomPhase)
|
||||
_woundPhase() {
|
||||
const applyWound = (cards, alliedLanes, side) => {
|
||||
for (const c of cards) {
|
||||
if (c.ruptureStacks > 0) {
|
||||
const dmg = c.ruptureStacks;
|
||||
if (c.woundStacks > 0) {
|
||||
const dmg = c.woundStacks;
|
||||
c.currentHP -= dmg;
|
||||
this._log(`${c.name} takes ${dmg} rupture damage`);
|
||||
this._log(`${c.name} takes ${dmg} wound damage`);
|
||||
const killed = c.currentHP <= 0;
|
||||
if (killed) {
|
||||
this._log(`${c.name} is destroyed by rupture`);
|
||||
this._log(`${c.name} is destroyed by wound`);
|
||||
this.events.push({ type: 'death', card: c, side });
|
||||
this._processOnDeath(c, alliedLanes, side);
|
||||
}
|
||||
this.events.push({ type: 'ruptureTick', card: c, damage: dmg, killed });
|
||||
this.events.push({ type: 'woundTick', card: c, damage: dmg, killed });
|
||||
this._tryCarapace(c, dmg);
|
||||
c.ruptureStacks--;
|
||||
c.woundStacks--;
|
||||
}
|
||||
}
|
||||
};
|
||||
applyRupture(this.playerLanes, this.playerLanes, 'player');
|
||||
applyRupture(this.opponentLanes, this.opponentLanes, 'opponent');
|
||||
applyWound(this.playerLanes, this.playerLanes, 'player');
|
||||
applyWound(this.opponentLanes, this.opponentLanes, 'opponent');
|
||||
}
|
||||
|
||||
// If the card has carapace and just took damage, apply armor gain (with cap).
|
||||
|
|
@ -292,8 +292,8 @@ export class CombatEngine {
|
|||
}
|
||||
|
||||
const extraAttacks = (context.extraAttacks || 0) + 1;
|
||||
// Track rupture stacks applied this attack sequence so flurry can't stack beyond skill.value
|
||||
context.ruptureTracker = { applied: 0 };
|
||||
// Track wound stacks applied this attack sequence so flurry can't stack beyond skill.value
|
||||
context.woundTracker = { applied: 0 };
|
||||
for (let a = 0; a < extraAttacks; a++) {
|
||||
// Skip a target that was already killed (e.g. by mortar during preAttack)
|
||||
const currentTarget = (target && target.currentHP > 0) ? target : null;
|
||||
|
|
@ -318,11 +318,11 @@ export class CombatEngine {
|
|||
alliedLanes.filter(c => c.currentHP > 0),
|
||||
enemyLanes.filter(c => c.currentHP > 0),
|
||||
onAtkCtx);
|
||||
if (onAtkCtx.ruptureAllTargets) {
|
||||
this.events.push({ type: 'ruptureAll', attacker, targets: onAtkCtx.ruptureAllTargets, side });
|
||||
if (onAtkCtx.woundAllTargets) {
|
||||
this.events.push({ type: 'woundAll', attacker, targets: onAtkCtx.woundAllTargets, side });
|
||||
}
|
||||
if (onAtkCtx.ruptureTarget) {
|
||||
this.events.push({ type: 'ruptureApply', attacker, target: onAtkCtx.ruptureTarget, stacks: onAtkCtx.ruptureStacks, side });
|
||||
if (onAtkCtx.woundTarget) {
|
||||
this.events.push({ type: 'woundApply', attacker, target: onAtkCtx.woundTarget, stacks: onAtkCtx.woundStacks, side });
|
||||
}
|
||||
if (onAtkCtx.weakenAllTargets) {
|
||||
this.events.push({ type: 'weakenAll', attacker, targets: onAtkCtx.weakenAllTargets, side });
|
||||
|
|
@ -479,7 +479,7 @@ export class CombatEngine {
|
|||
for (const c of this.ai.hand) { if (c.currentDelay > 0) c.currentDelay--; }
|
||||
this._commanderSkillPhase();
|
||||
this._cardOnTurnStartPhase();
|
||||
this._rupturePhase();
|
||||
this._woundPhase();
|
||||
this._venomPhase();
|
||||
this._smitePhase();
|
||||
this._moltPhase();
|
||||
|
|
@ -526,7 +526,7 @@ export class CombatEngine {
|
|||
this._commanderSkillPhase();
|
||||
this._cardOnTurnStartPhase();
|
||||
this._jamPhase();
|
||||
this._preBattlePhase(); // _rupturePhase() and _venomPhase() are called first inside _preBattlePhase
|
||||
this._preBattlePhase(); // _woundPhase() and _venomPhase() are called first inside _preBattlePhase
|
||||
// NOTE: No _deathCheck() here — dead cards stay in lanes so the scene can
|
||||
// animate their death during preBattle (e.g. drain kills). _buildPendingAttacks
|
||||
// already skips dead cards via currentHP <= 0 checks. finalizeCommit() calls
|
||||
|
|
@ -546,7 +546,6 @@ export class CombatEngine {
|
|||
const weakenFires = [];
|
||||
const drainFires = [];
|
||||
const healFires = [];
|
||||
const sanctifyFires = [];
|
||||
const overchargeFires = [];
|
||||
const fortifyFires = [];
|
||||
const strikeFires = [];
|
||||
|
|
@ -652,13 +651,6 @@ export class CombatEngine {
|
|||
healFires.push({ skill: 'heal', source: card, target: ctx.healTarget, healAmount: ctx.healAmount, hpBefore: ctx.healHpBefore });
|
||||
this._log(`${card.name} heal: restored ${ctx.healAmount} HP to ${ctx.healTarget.name}`);
|
||||
}
|
||||
if (s.name === 'sanctify' && ctx.sanctifyAllTargets) {
|
||||
sanctifyFires.push({ skill: 'sanctify', source: card, isAll: true, targets: ctx.sanctifyAllTargets });
|
||||
this._log(`${card.name} sanctify all: cleansed ${ctx.sanctifyAllTargets.length} allies`);
|
||||
} else if (s.name === 'sanctify' && ctx.sanctifyCleansed?.length) {
|
||||
sanctifyFires.push({ skill: 'sanctify', source: card, target: ctx.sanctifyTarget, cleansed: ctx.sanctifyCleansed });
|
||||
this._log(`${card.name} sanctify: cleansed self`);
|
||||
}
|
||||
if (s.name === 'overcharge' && ctx.overchargeSource) {
|
||||
const selfKilled = ctx.overchargeSelfKilled;
|
||||
overchargeFires.push({
|
||||
|
|
@ -711,13 +703,13 @@ export class CombatEngine {
|
|||
}
|
||||
}
|
||||
}
|
||||
return { buffs, siegeFires, protectFires, enfeebeFires, jamFires, weakenFires, drainFires, healFires, sanctifyFires, overchargeFires, fortifyFires, strikeFires };
|
||||
return { buffs, siegeFires, protectFires, enfeebeFires, jamFires, weakenFires, drainFires, healFires, overchargeFires, fortifyFires, strikeFires };
|
||||
}
|
||||
|
||||
// Emit the 8-step pre-battle sequence and process preBattle skills.
|
||||
_preBattlePhase() {
|
||||
// Rupture/venom/smite ticks first, then molt heals (both before buff skills).
|
||||
this._rupturePhase();
|
||||
// Wound/venom/smite ticks first, then molt heals (both before buff skills).
|
||||
this._woundPhase();
|
||||
this._venomPhase();
|
||||
this._smitePhase();
|
||||
this._moltPhase();
|
||||
|
|
@ -732,8 +724,8 @@ export class CombatEngine {
|
|||
const otherAllies = [otherCmd, ...otherLanes];
|
||||
|
||||
// Steps 1–2: commander defensive buffs (placeholder — no defensive skills yet)
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'commander', side: firstSide, card: firstCmd, buffs: [], siegeFires: [], protectFires: [], sanctifyFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'commander', side: otherSide, card: otherCmd, buffs: [], siegeFires: [], protectFires: [], sanctifyFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'commander', side: firstSide, card: firstCmd, buffs: [], siegeFires: [], protectFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'commander', side: otherSide, card: otherCmd, buffs: [], siegeFires: [], protectFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
|
||||
// Steps 3–4: commander offensive buffs (pass actual enemy lanes so all-targeting skills work)
|
||||
const firstCmdFires = this._collectPreBattleFires([firstCmd], firstAllies, otherAllies, otherCmd, otherLanes);
|
||||
|
|
@ -742,8 +734,8 @@ export class CombatEngine {
|
|||
this.events.push({ type: 'preBattle', phase: 'offensive', target: 'commander', side: otherSide, card: otherCmd, ...otherCmdFires });
|
||||
|
||||
// Steps 5–6: lane defensive buffs (placeholder)
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'lanes', side: firstSide, cards: firstLanes, buffs: [], siegeFires: [], protectFires: [], enfeebeFires: [], jamFires: [], healFires: [], sanctifyFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'lanes', side: otherSide, cards: otherLanes, buffs: [], siegeFires: [], protectFires: [], enfeebeFires: [], jamFires: [], healFires: [], sanctifyFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'lanes', side: firstSide, cards: firstLanes, buffs: [], siegeFires: [], protectFires: [], enfeebeFires: [], jamFires: [], healFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
this.events.push({ type: 'preBattle', phase: 'defensive', target: 'lanes', side: otherSide, cards: otherLanes, buffs: [], siegeFires: [], protectFires: [], enfeebeFires: [], jamFires: [], healFires: [], overchargeFires: [], fortifyFires: [] });
|
||||
|
||||
// Steps 7–8: lane offensive buffs + all skill fires
|
||||
const firstLaneFires = this._collectPreBattleFires(firstLanes, firstAllies, otherAllies, otherCmd, otherLanes);
|
||||
|
|
@ -1110,6 +1102,26 @@ export class CombatEngine {
|
|||
return debuffs;
|
||||
}
|
||||
|
||||
// Collect cleanse fires from all live cards on a side for the postBattle phase.
|
||||
_collectPostBattleCleanseFires(allSideCards) {
|
||||
const cleanseFires = [];
|
||||
const liveCards = allSideCards.filter(c => c && c.currentHP > 0);
|
||||
for (const card of liveCards) {
|
||||
for (const s of card.skills) {
|
||||
if (s.name === 'cleanse' && s.trigger === 'postBattle') {
|
||||
const ctx = { rng: this.rng };
|
||||
const otherAllies = liveCards.filter(c => c !== card);
|
||||
this.skillProcessor.process(s, card, null, otherAllies, [], ctx);
|
||||
if (ctx.cleanseTargets?.length) {
|
||||
cleanseFires.push({ skill: 'cleanse', source: card, targets: ctx.cleanseTargets });
|
||||
this._log(`${card.name} cleanse: cleansed ${ctx.cleanseTargets.length} cards`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cleanseFires;
|
||||
}
|
||||
|
||||
// Emit the 8-step post-battle sequence and remove all temp buffs.
|
||||
// playerGoesFirst was already flipped in beginCommit, so !playerGoesFirst = who went first this turn.
|
||||
_postBattlePhase() {
|
||||
|
|
@ -1135,11 +1147,13 @@ export class CombatEngine {
|
|||
this.events.push({ type: 'postBattle', phase: 'defensive', target: 'lanes', side: firstSide, cards: firstLanes, debuffs: [] });
|
||||
this.events.push({ type: 'postBattle', phase: 'defensive', target: 'lanes', side: otherSide, cards: otherLanes, debuffs: [] });
|
||||
|
||||
// Steps 7–8: lane offensive debuffs
|
||||
// Steps 7–8: lane offensive debuffs + cleanse
|
||||
const firstLaneDebuffs = this._removeAndCollectDebuffs(firstLanes);
|
||||
const otherLaneDebuffs = this._removeAndCollectDebuffs(otherLanes);
|
||||
this.events.push({ type: 'postBattle', phase: 'offensive', target: 'lanes', side: firstSide, cards: firstLanes, debuffs: firstLaneDebuffs });
|
||||
this.events.push({ type: 'postBattle', phase: 'offensive', target: 'lanes', side: otherSide, cards: otherLanes, debuffs: otherLaneDebuffs });
|
||||
const firstCleanseFires = this._collectPostBattleCleanseFires([...firstLanes, firstCmd]);
|
||||
const otherCleanseFires = this._collectPostBattleCleanseFires([...otherLanes, otherCmd]);
|
||||
this.events.push({ type: 'postBattle', phase: 'offensive', target: 'lanes', side: firstSide, cards: firstLanes, debuffs: firstLaneDebuffs, cleanseFires: firstCleanseFires });
|
||||
this.events.push({ type: 'postBattle', phase: 'offensive', target: 'lanes', side: otherSide, cards: otherLanes, debuffs: otherLaneDebuffs, cleanseFires: otherCleanseFires });
|
||||
|
||||
// Decrement burrow stacks now that the full combat round has resolved.
|
||||
this._burrowPhase();
|
||||
|
|
@ -1156,7 +1170,7 @@ export class CombatEngine {
|
|||
this._activationPhase();
|
||||
this._commanderSkillPhase();
|
||||
this._cardOnTurnStartPhase();
|
||||
this._rupturePhase();
|
||||
this._woundPhase();
|
||||
this._jamPhase();
|
||||
this._venomPhase();
|
||||
this._smitePhase();
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ export class SkillProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
rupture(skill, attacker, defender, allies, enemies, context) {
|
||||
const tracker = context?.ruptureTracker;
|
||||
wound(skill, attacker, defender, allies, enemies, context) {
|
||||
const tracker = context?.woundTracker;
|
||||
const alreadyApplied = tracker?.applied || 0;
|
||||
const canApply = Math.max(0, skill.value - alreadyApplied);
|
||||
if (canApply <= 0) return;
|
||||
|
|
@ -84,18 +84,18 @@ export class SkillProcessor {
|
|||
if (targets.length === 0) return;
|
||||
const allTargets = [];
|
||||
for (const t of targets) {
|
||||
t.ruptureStacks = (t.ruptureStacks || 0) + canApply;
|
||||
t.woundStacks = (t.woundStacks || 0) + canApply;
|
||||
allTargets.push({ target: t, stacks: canApply });
|
||||
}
|
||||
if (tracker) tracker.applied += canApply;
|
||||
if (context) context.ruptureAllTargets = allTargets;
|
||||
if (context) context.woundAllTargets = allTargets;
|
||||
return;
|
||||
}
|
||||
if (!defender || defender.currentHP <= 0) return;
|
||||
if (defender.burrowTurns > 0) return;
|
||||
defender.ruptureStacks = (defender.ruptureStacks || 0) + canApply;
|
||||
defender.woundStacks = (defender.woundStacks || 0) + canApply;
|
||||
if (tracker) tracker.applied += canApply;
|
||||
if (context) { context.ruptureTarget = defender; context.ruptureStacks = canApply; }
|
||||
if (context) { context.woundTarget = defender; context.woundStacks = canApply; }
|
||||
}
|
||||
|
||||
berserk(skill, attacker, defender, allies, enemies, context) {
|
||||
|
|
@ -577,8 +577,11 @@ export class SkillProcessor {
|
|||
if (context) { context.smiteTarget = defender; context.smiteStacks = skill.value; }
|
||||
}
|
||||
|
||||
sanctify(skill, attacker, defender, allies, enemies, context) {
|
||||
const cleanse = (card) => {
|
||||
cleanse(skill, attacker, defender, allies, enemies, context) {
|
||||
// Always cleanses all friendly cards including self
|
||||
const allAllies = [attacker, ...allies].filter(c => c.currentHP > 0);
|
||||
const targets = [];
|
||||
for (const card of allAllies) {
|
||||
const cleansed = [];
|
||||
if (card.venomStacks > 0) {
|
||||
const removed = Math.min(skill.value, card.venomStacks);
|
||||
|
|
@ -590,29 +593,14 @@ export class SkillProcessor {
|
|||
card.smiteStacks = Math.max(0, card.smiteStacks - removed);
|
||||
cleansed.push({ effect: 'smite', removed });
|
||||
}
|
||||
if (card.ruptureStacks > 0) {
|
||||
const removed = Math.min(skill.value, card.ruptureStacks);
|
||||
card.ruptureStacks = Math.max(0, card.ruptureStacks - removed);
|
||||
cleansed.push({ effect: 'rupture', removed });
|
||||
if (card.woundStacks > 0) {
|
||||
const removed = Math.min(skill.value, card.woundStacks);
|
||||
card.woundStacks = Math.max(0, card.woundStacks - removed);
|
||||
cleansed.push({ effect: 'wound', removed });
|
||||
}
|
||||
return cleansed;
|
||||
};
|
||||
|
||||
if (skill.all) {
|
||||
const allTargets = [];
|
||||
for (const a of allies) {
|
||||
if (a.currentHP <= 0) continue;
|
||||
const cleansed = cleanse(a);
|
||||
if (cleansed.length > 0) allTargets.push({ target: a, cleansed });
|
||||
}
|
||||
if (context && allTargets.length > 0) context.sanctifyAllTargets = allTargets;
|
||||
return;
|
||||
}
|
||||
const cleansed = cleanse(attacker);
|
||||
if (context && cleansed.length > 0) {
|
||||
context.sanctifyTarget = attacker;
|
||||
context.sanctifyCleansed = cleansed;
|
||||
if (cleansed.length > 0) targets.push({ target: card, cleansed });
|
||||
}
|
||||
if (context && targets.length > 0) context.cleanseTargets = targets;
|
||||
}
|
||||
|
||||
hack(skill, attacker, defender, allies, enemies, context) {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export class CardManager {
|
|||
currentArmor: stats.armor,
|
||||
baseArmor: stats.armor || 0,
|
||||
currentDelay: stats.delay,
|
||||
ruptureStacks: 0,
|
||||
woundStacks: 0,
|
||||
venomStacks: 0,
|
||||
smiteStacks: 0,
|
||||
burrowTurns: (() => { const bs = stats.skills.find(s => s.name === 'burrow'); return bs ? bs.value : 0; })(),
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ export class CardObject extends Phaser.GameObjects.Container {
|
|||
this.add(this.burrowBadge);
|
||||
this._updateBurrowBadge();
|
||||
|
||||
// Rupture stack indicator (below burrow badge, red)
|
||||
this.ruptureBadge = this.scene.add.text(
|
||||
// Wound stack indicator (below burrow badge, red)
|
||||
this.woundBadge = this.scene.add.text(
|
||||
w / 2 - Math.round(4 * scale),
|
||||
-h / 2 + bannerH + Math.round(45 * scale),
|
||||
'',
|
||||
|
|
@ -166,9 +166,9 @@ export class CardObject extends Phaser.GameObjects.Container {
|
|||
fontFamily: 'Audiowide'
|
||||
}
|
||||
).setOrigin(1, 0);
|
||||
this.ruptureBadge.setVisible(false);
|
||||
this.add(this.ruptureBadge);
|
||||
this._updateRuptureBadge();
|
||||
this.woundBadge.setVisible(false);
|
||||
this.add(this.woundBadge);
|
||||
this._updateWoundBadge();
|
||||
|
||||
// Flash overlay — transparent red rect; .bg is targeted by _animateAttack
|
||||
this.bg = this.scene.add.rectangle(0, imageCY, w, imageH, 0xff2200, 0);
|
||||
|
|
@ -288,14 +288,14 @@ export class CardObject extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
|
||||
_updateRuptureBadge() {
|
||||
if (!this.ruptureBadge) return;
|
||||
const stacks = this.cardData.ruptureStacks || 0;
|
||||
_updateWoundBadge() {
|
||||
if (!this.woundBadge) return;
|
||||
const stacks = this.cardData.woundStacks || 0;
|
||||
if (stacks > 0) {
|
||||
this.ruptureBadge.setText(`⚔${stacks}`);
|
||||
this.ruptureBadge.setVisible(true);
|
||||
this.woundBadge.setText(`⚔${stacks}`);
|
||||
this.woundBadge.setVisible(true);
|
||||
} else {
|
||||
this.ruptureBadge.setVisible(false);
|
||||
this.woundBadge.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ export class CardObject extends Phaser.GameObjects.Container {
|
|||
this._updateVenomBadge();
|
||||
this._updateSmiteBadge();
|
||||
this._updateBurrowBadge();
|
||||
this._updateRuptureBadge();
|
||||
this._updateWoundBadge();
|
||||
if (this.skillText && this.cardData.skills?.length) {
|
||||
const skillStr = this.cardData.skills
|
||||
.map(s => {
|
||||
|
|
|
|||
|
|
@ -809,7 +809,7 @@ export class BattleScene extends Phaser.Scene {
|
|||
// Deploy + pre-attack phases (NO damage applied yet)
|
||||
// On player-first turns, beginCommit also deploys the AI card
|
||||
const commitEvents = this.engine.beginCommit(chosenCard);
|
||||
const preBattleEvents = commitEvents.filter(e => e.type === 'preBattle' || e.type === 'ruptureTick' || e.type === 'venomTick' || e.type === 'smiteTick' || e.type === 'carapace' || e.type === 'molt' || e.type === 'hive_link');
|
||||
const preBattleEvents = commitEvents.filter(e => e.type === 'preBattle' || e.type === 'woundTick' || e.type === 'venomTick' || e.type === 'smiteTick' || e.type === 'carapace' || e.type === 'molt' || e.type === 'hive_link');
|
||||
|
||||
// Build preBattle hive_link map for venom kills: dead card instanceId → hive_link event
|
||||
this._preBattleHiveLinks = {};
|
||||
|
|
@ -826,7 +826,6 @@ export class BattleScene extends Phaser.Scene {
|
|||
this._restoreDrainForDisplay(preBattleEvents);
|
||||
this._restoreMoltForDisplay(preBattleEvents);
|
||||
this._restoreHealForDisplay(preBattleEvents);
|
||||
this._restoreSanctifyForDisplay(preBattleEvents);
|
||||
this._restoreOverchargeForDisplay(preBattleEvents);
|
||||
this._restoreFortifyForDisplay(preBattleEvents);
|
||||
// Render the field with all newly deployed cards but pre-combat stats
|
||||
|
|
@ -837,7 +836,6 @@ export class BattleScene extends Phaser.Scene {
|
|||
this._reapplyDrainDamage(preBattleEvents);
|
||||
this._reapplyMolt(preBattleEvents);
|
||||
this._reapplyHeal(preBattleEvents);
|
||||
this._reapplySanctify(preBattleEvents);
|
||||
this._reapplyOvercharge(preBattleEvents);
|
||||
this._reapplyFortify(preBattleEvents);
|
||||
|
||||
|
|
@ -918,7 +916,7 @@ export class BattleScene extends Phaser.Scene {
|
|||
} else if (currentRound) {
|
||||
if (e.type === 'berserk') currentRound.berserkEvent = e;
|
||||
else if (e.type === 'counter') currentRound.counterEvent = e;
|
||||
else if (['ruptureAll', 'ruptureApply', 'weakenAll', 'venomApply', 'venomApplyAll', 'smiteApply', 'smiteApplyAll', 'carapace'].includes(e.type))
|
||||
else if (['woundAll', 'woundApply', 'weakenAll', 'venomApply', 'venomApplyAll', 'smiteApply', 'smiteApplyAll', 'carapace'].includes(e.type))
|
||||
currentRound.onAttackAllEvents.push(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -1000,7 +998,7 @@ export class BattleScene extends Phaser.Scene {
|
|||
?? this.commanderObjects?.get(cardData.instanceId);
|
||||
if (!obj) return;
|
||||
obj.refresh();
|
||||
// Fallback death path for cards killed by counter/rupture without an HP loss animation
|
||||
// Fallback death path for cards killed by counter/wound without an HP loss animation
|
||||
if (cardData.currentHP <= 0 && !obj.isCommander) {
|
||||
this._playCardDeath(obj, cardData);
|
||||
}
|
||||
|
|
@ -1013,8 +1011,8 @@ export class BattleScene extends Phaser.Scene {
|
|||
if (idx >= events.length) { onComplete(); return; }
|
||||
const event = events[idx];
|
||||
const cb = () => next(idx + 1);
|
||||
if (event.type === 'ruptureAll') this._animateRuptureAll(event, cb);
|
||||
else if (event.type === 'ruptureApply') this._animateRuptureApply(event, cb);
|
||||
if (event.type === 'woundAll') this._animateWoundAll(event, cb);
|
||||
else if (event.type === 'woundApply') this._animateWoundApply(event, cb);
|
||||
else if (event.type === 'weakenAll') this._animateWeakenAll(event, cb);
|
||||
else if (event.type === 'venomApply') this._animateVenomApply(event, cb);
|
||||
else if (event.type === 'venomApplyAll') this._animateVenomApplyAll(event, cb);
|
||||
|
|
@ -1027,12 +1025,12 @@ export class BattleScene extends Phaser.Scene {
|
|||
next(0);
|
||||
}
|
||||
|
||||
_animateRuptureAll(event, onComplete) {
|
||||
_animateWoundAll(event, onComplete) {
|
||||
if (!event.targets?.length) { onComplete(); return; }
|
||||
const _lookup = id => id && (this.cardObjects.get(id) || this.commanderObjects?.get(id));
|
||||
const sourceObj = _lookup(event.attacker.instanceId);
|
||||
this.statusText.setText(`${event.attacker.name} ruptures all enemies!`);
|
||||
this.sound.play('sfx_rupture', { volume: 0.8 });
|
||||
this.statusText.setText(`${event.attacker.name} wounds all enemies!`);
|
||||
this.sound.play('sfx_wound', { volume: 0.8 });
|
||||
|
||||
let remaining = event.targets.length;
|
||||
const done = () => { if (--remaining <= 0) onComplete(); };
|
||||
|
|
@ -1043,7 +1041,7 @@ export class BattleScene extends Phaser.Scene {
|
|||
|
||||
if (!sourceObj?.scene) {
|
||||
targetObj.refresh();
|
||||
this._showRuptureStackGain(targetObj, t.stacks);
|
||||
this._showWoundStackGain(targetObj, t.stacks);
|
||||
this.time.delayedCall(400, done);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1064,7 +1062,7 @@ export class BattleScene extends Phaser.Scene {
|
|||
if (knife.scene) knife.destroy();
|
||||
if (targetObj.scene) {
|
||||
targetObj.refresh();
|
||||
this._showRuptureStackGain(targetObj, t.stacks);
|
||||
this._showWoundStackGain(targetObj, t.stacks);
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
|
@ -1072,18 +1070,18 @@ export class BattleScene extends Phaser.Scene {
|
|||
}
|
||||
}
|
||||
|
||||
_animateRuptureApply(event, onComplete) {
|
||||
_animateWoundApply(event, onComplete) {
|
||||
const _lookup = id => id && (this.cardObjects.get(id) || this.commanderObjects?.get(id));
|
||||
const sourceObj = _lookup(event.attacker.instanceId);
|
||||
const targetObj = _lookup(event.target.instanceId);
|
||||
if (!targetObj?.scene) { onComplete(); return; }
|
||||
|
||||
this.statusText.setText(`${event.attacker.name} ruptures ${event.target.name}! [${event.stacks} stacks]`);
|
||||
this.sound.play('sfx_rupture', { volume: 0.8 });
|
||||
this.statusText.setText(`${event.attacker.name} wounds ${event.target.name}! [${event.stacks} stacks]`);
|
||||
this.sound.play('sfx_wound', { volume: 0.8 });
|
||||
|
||||
if (!sourceObj?.scene) {
|
||||
targetObj.refresh();
|
||||
this._showRuptureStackGain(targetObj, event.stacks);
|
||||
this._showWoundStackGain(targetObj, event.stacks);
|
||||
this.time.delayedCall(400, onComplete);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1104,18 +1102,18 @@ export class BattleScene extends Phaser.Scene {
|
|||
if (knife.scene) knife.destroy();
|
||||
if (!targetObj.scene) { onComplete(); return; }
|
||||
targetObj.refresh();
|
||||
this._showRuptureStackGain(targetObj, event.stacks);
|
||||
this._showWoundStackGain(targetObj, event.stacks);
|
||||
this.time.delayedCall(400, onComplete);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_animateRuptureTick(event, onComplete) {
|
||||
_animateWoundTick(event, onComplete) {
|
||||
const obj = this.cardObjects.get(event.card.instanceId) || this.commanderObjects?.get(event.card.instanceId);
|
||||
if (!obj?.scene) { onComplete(); return; }
|
||||
|
||||
this.statusText.setText(`${event.card.name} suffers ${event.damage} rupture damage!`);
|
||||
this.sound.play('sfx_rupture_effect', { volume: 0.8 });
|
||||
this.statusText.setText(`${event.card.name} suffers ${event.damage} wound damage!`);
|
||||
this.sound.play('sfx_wound_effect', { volume: 0.8 });
|
||||
|
||||
// Sprite 45 (heart) pulses larger/smaller over the card for ~2 seconds
|
||||
const effectSprite = this.add.sprite(obj.x, obj.y, 'attacks', 45)
|
||||
|
|
@ -1152,9 +1150,9 @@ export class BattleScene extends Phaser.Scene {
|
|||
});
|
||||
}
|
||||
|
||||
_showRuptureStackGain(targetObj, stacks) {
|
||||
_showWoundStackGain(targetObj, stacks) {
|
||||
if (!targetObj?.scene || stacks <= 0) return;
|
||||
const gainText = this.add.text(targetObj.x, targetObj.y - 20, `+${stacks} rupture`, {
|
||||
const gainText = this.add.text(targetObj.x, targetObj.y - 20, `+${stacks} wound`, {
|
||||
fontSize: '12px', color: '#ff4444', fontStyle: 'bold',
|
||||
stroke: '#000000', strokeThickness: 3, fontFamily: 'Audiowide'
|
||||
}).setOrigin(0.5).setDepth(35);
|
||||
|
|
@ -2711,8 +2709,8 @@ export class BattleScene extends Phaser.Scene {
|
|||
_processPreBattle(events, onComplete) {
|
||||
const processStep = (idx) => {
|
||||
if (idx >= events.length) { onComplete(); return; }
|
||||
if (events[idx].type === 'ruptureTick') {
|
||||
this._animateRuptureTick(events[idx], () => processStep(idx + 1));
|
||||
if (events[idx].type === 'woundTick') {
|
||||
this._animateWoundTick(events[idx], () => processStep(idx + 1));
|
||||
return;
|
||||
}
|
||||
if (events[idx].type === 'venomTick') {
|
||||
|
|
@ -2751,13 +2749,11 @@ export class BattleScene extends Phaser.Scene {
|
|||
const hasWeaken = event.weakenFires?.length > 0;
|
||||
const hasDrain = event.drainFires?.length > 0;
|
||||
const hasHeal = event.healFires?.length > 0;
|
||||
const hasSanctify = event.sanctifyFires?.length > 0;
|
||||
const hasOvercharge = event.overchargeFires?.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 || hasStrike) {
|
||||
this._processSanctifyFires(event.sanctifyFires || [], () => {
|
||||
if (hasBuffs || hasSiege || hasProtect || hasEnfeeble || hasJam || hasWeaken || hasDrain || hasHeal || hasOvercharge || hasFortify || hasStrike) {
|
||||
this._processDrainFires(event.drainFires || [], () => {
|
||||
this._processOverchargeFires(event.overchargeFires || [], () => {
|
||||
this._processBuffAnimations(event.buffs || [], () => {
|
||||
|
|
@ -2779,7 +2775,6 @@ export class BattleScene extends Phaser.Scene {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
onComplete();
|
||||
}
|
||||
|
|
@ -4365,7 +4360,7 @@ export class BattleScene extends Phaser.Scene {
|
|||
processStep(0);
|
||||
}
|
||||
|
||||
// Called once per postBattle step. Refreshes cards whose temp buffs were removed.
|
||||
// Called once per postBattle step. Refreshes cards whose temp buffs were removed, then plays cleanse.
|
||||
_onPostBattleStep(event, onComplete) {
|
||||
if (event.debuffs?.length > 0) {
|
||||
for (const debuff of event.debuffs) {
|
||||
|
|
@ -4374,6 +4369,10 @@ export class BattleScene extends Phaser.Scene {
|
|||
obj?.refresh();
|
||||
}
|
||||
}
|
||||
if (event.cleanseFires?.length > 0) {
|
||||
this._processPostBattleCleanseFires(event.cleanseFires, onComplete);
|
||||
return;
|
||||
}
|
||||
onComplete();
|
||||
}
|
||||
|
||||
|
|
@ -4841,64 +4840,61 @@ export class BattleScene extends Phaser.Scene {
|
|||
}
|
||||
}
|
||||
|
||||
// ── Sanctify animations ──────────────────────────────────────────────────
|
||||
// ── Cleanse animations ───────────────────────────────────────────────────
|
||||
|
||||
_processSanctifyFires(fires, onComplete) {
|
||||
if (!fires || fires.length === 0) { onComplete(); return; }
|
||||
_processPostBattleCleanseFires(cleanseFires, onComplete) {
|
||||
if (!cleanseFires?.length) { onComplete(); return; }
|
||||
const next = (idx) => {
|
||||
if (idx >= fires.length) { onComplete(); return; }
|
||||
const fire = fires[idx];
|
||||
if (fire.isAll) {
|
||||
this._animateSanctifyAll(fire, () => next(idx + 1));
|
||||
} else {
|
||||
this._animateSanctifySingle(fire, () => next(idx + 1));
|
||||
}
|
||||
if (idx >= cleanseFires.length) { onComplete(); return; }
|
||||
this._animatePostBattleCleanseFireSet(cleanseFires[idx], () => next(idx + 1));
|
||||
};
|
||||
next(0);
|
||||
}
|
||||
|
||||
_animateSanctifySingle(fire, onComplete) {
|
||||
const sourceObj = this.cardObjects.get(fire.source.instanceId) || this.commanderObjects?.get(fire.source.instanceId);
|
||||
const targetObj = this.cardObjects.get(fire.target.instanceId) || this.commanderObjects?.get(fire.target.instanceId);
|
||||
if (!targetObj?.scene) { onComplete(); return; }
|
||||
|
||||
const effects = fire.cleansed.map(c => c.effect).join(', ');
|
||||
this.statusText.setText(`${fire.source.name} sanctifies self! Cleansed: ${effects}`);
|
||||
this.sound.play('sfx_sanctify', { volume: 0.8 });
|
||||
|
||||
const startX = sourceObj?.scene ? sourceObj.x : targetObj.x;
|
||||
const startY = sourceObj?.scene ? sourceObj.y : targetObj.y;
|
||||
this._doSanctifySpriteTo(startX, startY, targetObj, () => {
|
||||
if (targetObj.scene) targetObj.refresh();
|
||||
onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
_animateSanctifyAll(fire, onComplete) {
|
||||
_animatePostBattleCleanseFireSet(fire, onComplete) {
|
||||
if (!fire.targets?.length) { onComplete(); return; }
|
||||
this.statusText.setText(`${fire.source.name} sanctifies all allies!`);
|
||||
this.sound.play('sfx_sanctify', { volume: 0.8 });
|
||||
|
||||
const sourceObj = this.cardObjects.get(fire.source.instanceId) || this.commanderObjects?.get(fire.source.instanceId);
|
||||
const startX = sourceObj?.scene ? sourceObj.x : 0;
|
||||
const startY = sourceObj?.scene ? sourceObj.y : 0;
|
||||
this.statusText.setText(`${fire.source.name} cleanses the team!`);
|
||||
this.sound.play('sfx_cleanse', { volume: 0.8 });
|
||||
|
||||
const startX = sourceObj?.scene ? sourceObj.x : this.cameras.main.centerX;
|
||||
const startY = sourceObj?.scene ? sourceObj.y : this.cameras.main.centerY;
|
||||
|
||||
let remaining = fire.targets.length;
|
||||
const done = () => { if (--remaining <= 0) onComplete(); };
|
||||
|
||||
for (const t of fire.targets) {
|
||||
const targetObj = this.cardObjects.get(t.target.instanceId) || this.commanderObjects?.get(t.target.instanceId);
|
||||
if (!targetObj?.scene) { done(); continue; }
|
||||
const obj = this.cardObjects.get(t.target.instanceId) || this.commanderObjects?.get(t.target.instanceId);
|
||||
if (!obj?.scene) { done(); continue; }
|
||||
|
||||
this._doSanctifySpriteTo(startX, startY, targetObj, () => {
|
||||
if (targetObj.scene) targetObj.refresh();
|
||||
this._doCleanseSpriteTo(startX, startY, obj, () => {
|
||||
for (const c of t.cleansed) {
|
||||
this._showCleanseReduction(obj, c.effect, c.removed);
|
||||
}
|
||||
if (obj.scene) obj.refresh();
|
||||
done();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_showCleanseReduction(targetObj, effect, removed) {
|
||||
if (!targetObj?.scene || removed <= 0) return;
|
||||
const colors = { venom: '#88ff44', smite: '#ffcc00', wound: '#ff4444' };
|
||||
const color = colors[effect] || '#44ffff';
|
||||
const gainText = this.add.text(targetObj.x, targetObj.y - 20, `-${removed} ${effect}`, {
|
||||
fontSize: '12px', color, fontStyle: 'bold',
|
||||
stroke: '#000000', strokeThickness: 3, fontFamily: 'Audiowide'
|
||||
}).setOrigin(0.5).setDepth(35);
|
||||
this.tweens.add({
|
||||
targets: gainText,
|
||||
alpha: 0, y: targetObj.y - 55,
|
||||
duration: 800, ease: 'Power2',
|
||||
onComplete: () => { if (gainText.scene) gainText.destroy(); }
|
||||
});
|
||||
}
|
||||
|
||||
// Fly sprite 35 from (startX, startY) to targetObj, shake it, swap to sprite 36, fade out.
|
||||
_doSanctifySpriteTo(startX, startY, targetObj, onComplete) {
|
||||
_doCleanseSpriteTo(startX, startY, targetObj, onComplete) {
|
||||
const sprite = this.add.sprite(startX, startY, 'attacks', 35)
|
||||
.setDisplaySize(100, 100)
|
||||
.setDepth(30)
|
||||
|
|
@ -5002,22 +4998,51 @@ export class BattleScene extends Phaser.Scene {
|
|||
|
||||
_animateOverchargeBuffs(fire, onComplete) {
|
||||
if (!fire.targets?.length) { onComplete(); return; }
|
||||
|
||||
const sourceObj = this.cardObjects.get(fire.source.instanceId) || this.commanderObjects?.get(fire.source.instanceId);
|
||||
|
||||
let remaining = fire.targets.length;
|
||||
const done = () => { if (--remaining <= 0) onComplete(); };
|
||||
|
||||
for (const t of fire.targets) {
|
||||
const obj = this.cardObjects.get(t.target.instanceId) || this.commanderObjects?.get(t.target.instanceId);
|
||||
for (const tgt of fire.targets) {
|
||||
const obj = this.cardObjects.get(tgt.target.instanceId) || this.commanderObjects?.get(tgt.target.instanceId);
|
||||
if (!obj?.scene) { done(); continue; }
|
||||
|
||||
// Sprite 41 pulses over each buffed ally while ATK gain animates
|
||||
const buffSprite = this.add.sprite(obj.x, obj.y, 'attacks', 41)
|
||||
const startX = sourceObj ? sourceObj.x : obj.x;
|
||||
const startY = sourceObj ? sourceObj.y : obj.y;
|
||||
const endX = obj.x;
|
||||
const endY = obj.y;
|
||||
// Control point arches upward above the midpoint
|
||||
const ctrlX = (startX + endX) / 2;
|
||||
const ctrlY = Math.min(startY, endY) - 120;
|
||||
|
||||
// Sprite 41 originates at source and arches to each buffed ally
|
||||
const buffSprite = this.add.sprite(startX, startY, 'attacks', 41)
|
||||
.setDisplaySize(130, 130)
|
||||
.setDepth(30)
|
||||
.setAlpha(0.9);
|
||||
|
||||
const progress = { t: 0 };
|
||||
this.tweens.add({
|
||||
targets: progress,
|
||||
t: 1,
|
||||
duration: 380,
|
||||
scaleX: 1.5,
|
||||
scaleY: 1.5,
|
||||
ease: 'Sine.easeIn',
|
||||
onUpdate: () => {
|
||||
const p = progress.t;
|
||||
const bx = (1 - p) * (1 - p) * startX + 2 * (1 - p) * p * ctrlX + p * p * endX;
|
||||
const by = (1 - p) * (1 - p) * startY + 2 * (1 - p) * p * ctrlY + p * p * endY;
|
||||
if (buffSprite.scene) { buffSprite.x = bx; buffSprite.y = by; }
|
||||
},
|
||||
onComplete: () => {
|
||||
if (!buffSprite.scene) { done(); return; }
|
||||
// Arrived — pulse at target then destroy
|
||||
this.tweens.add({
|
||||
targets: buffSprite,
|
||||
scaleX: { from: 0.8, to: 1.5 },
|
||||
scaleY: { from: 0.8, to: 1.5 },
|
||||
scaleX: { from: 0.8, to: 1.2 },
|
||||
scaleY: { from: 0.8, to: 1.2 },
|
||||
duration: 220,
|
||||
yoyo: true,
|
||||
repeat: 1,
|
||||
|
|
@ -5029,6 +5054,8 @@ export class BattleScene extends Phaser.Scene {
|
|||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fortify animations ───────────────────────────────────────────────────
|
||||
|
|
@ -5105,7 +5132,7 @@ export class BattleScene extends Phaser.Scene {
|
|||
if (obj.armText) obj.armText.setText(`${(obj.cardData.currentArmor || 0) - amount}`);
|
||||
|
||||
const sprite = this.add.sprite(obj.x, obj.y, 'attacks', 37)
|
||||
.setDisplaySize(110, 110)
|
||||
.setDisplaySize(90, 90)
|
||||
.setDepth(30)
|
||||
.setAlpha(0.9);
|
||||
|
||||
|
|
@ -5198,49 +5225,6 @@ export class BattleScene extends Phaser.Scene {
|
|||
|
||||
// ── Restore/reapply for new preBattle skills ─────────────────────────────
|
||||
|
||||
_restoreSanctifyForDisplay(preBattleEvents) {
|
||||
for (const event of preBattleEvents) {
|
||||
for (const fire of event.sanctifyFires || []) {
|
||||
if (fire.isAll) {
|
||||
for (const t of fire.targets || []) {
|
||||
for (const c of t.cleansed) {
|
||||
if (c.effect === 'venom') t.target.venomStacks += c.removed;
|
||||
if (c.effect === 'smite') t.target.smiteStacks += c.removed;
|
||||
if (c.effect === 'rupture') t.target.ruptureStacks += c.removed;
|
||||
}
|
||||
}
|
||||
} else if (fire.cleansed?.length) {
|
||||
for (const c of fire.cleansed) {
|
||||
if (c.effect === 'venom') fire.target.venomStacks += c.removed;
|
||||
if (c.effect === 'smite') fire.target.smiteStacks += c.removed;
|
||||
if (c.effect === 'rupture') fire.target.ruptureStacks += c.removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_reapplySanctify(preBattleEvents) {
|
||||
for (const event of preBattleEvents) {
|
||||
for (const fire of event.sanctifyFires || []) {
|
||||
if (fire.isAll) {
|
||||
for (const t of fire.targets || []) {
|
||||
for (const c of t.cleansed) {
|
||||
if (c.effect === 'venom') t.target.venomStacks -= c.removed;
|
||||
if (c.effect === 'smite') t.target.smiteStacks -= c.removed;
|
||||
if (c.effect === 'rupture') t.target.ruptureStacks -= c.removed;
|
||||
}
|
||||
}
|
||||
} else if (fire.cleansed?.length) {
|
||||
for (const c of fire.cleansed) {
|
||||
if (c.effect === 'venom') fire.target.venomStacks -= c.removed;
|
||||
if (c.effect === 'smite') fire.target.smiteStacks -= c.removed;
|
||||
if (c.effect === 'rupture') fire.target.ruptureStacks -= c.removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_restoreOverchargeForDisplay(preBattleEvents) {
|
||||
for (const event of preBattleEvents) {
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ export class BootScene extends Phaser.Scene {
|
|||
this.load.audio('sfx_hive_link', 'assets/audio/fx/hive_link.mp3');
|
||||
this.load.audio('sfx_swarm', 'assets/audio/fx/swarm.mp3');
|
||||
this.load.audio('sfx_heal', 'assets/audio/fx/heal.mp3');
|
||||
this.load.audio('sfx_sanctify', 'assets/audio/fx/sanctify.mp3');
|
||||
this.load.audio('sfx_cleanse', 'assets/audio/fx/sanctify.mp3');
|
||||
this.load.audio('sfx_hack', 'assets/audio/fx/hack.mp3');
|
||||
this.load.audio('sfx_smite_cast', 'assets/audio/fx/smite-cast.mp3');
|
||||
this.load.audio('sfx_smite_damage', 'assets/audio/fx/smite-damage.mp3');
|
||||
this.load.audio('sfx_overcharge', 'assets/audio/fx/overcharge.mp3');
|
||||
this.load.audio('sfx_weaken', 'assets/audio/fx/weaken.mp3');
|
||||
this.load.audio('sfx_rupture', 'assets/audio/fx/rupture.mp3');
|
||||
this.load.audio('sfx_rupture_effect', 'assets/audio/fx/rupture-effect.mp3');
|
||||
this.load.audio('sfx_wound', 'assets/audio/fx/rupture.mp3');
|
||||
this.load.audio('sfx_wound_effect', 'assets/audio/fx/rupture-effect.mp3');
|
||||
this.load.audio('sfx_fortify', 'assets/audio/fx/fortify.mp3');
|
||||
this.load.audio('sfx_store_buy', 'assets/audio/fx/buy.mp3');
|
||||
this.load.audio('sfx_store_card', 'assets/audio/fx/card.mp3');
|
||||
|
|
|
|||
Loading…
Reference in New Issue