tyrants-edge/src/combat/SkillProcessor.js

167 lines
5.9 KiB
JavaScript

export class SkillProcessor {
constructor() {}
process(skill, attacker, defender, allies, enemies, context = {}) {
const handler = this[skill.name];
if (handler) handler.call(this, skill, attacker, defender, allies, enemies, context);
}
strike(skill, attacker, defender, allies, enemies, context) {
const target = defender || context?.enemyCommander;
if (!target) return;
const dmg = Math.max(0, skill.value - Math.max(0, target.currentArmor));
target.currentHP -= dmg;
attacker._lastSkillDmg = (attacker._lastSkillDmg || 0) + dmg;
if (context) {
context.strikeTarget = target;
context.strikeDamage = dmg;
}
}
swipe(skill, attacker, defender, allies, enemies) {
const baseDmg = attacker.currentAttack + skill.value;
for (const enemy of enemies) {
const dmg = Math.max(0, baseDmg - Math.max(0, enemy.currentArmor));
enemy.currentHP -= dmg;
}
}
pierce(skill, attacker, defender, allies, enemies, context) {
if (!defender) return;
context.pierceBonus = (context.pierceBonus || 0) + skill.value;
}
rupture(skill, attacker, defender) {
if (!defender) return;
defender.ruptureStacks = (defender.ruptureStacks || 0) + skill.value;
}
berserk(skill, attacker, defender, allies, enemies, context) {
if (context.trigger === 'on_attack' && context.damageDealt > 0) {
attacker.currentAttack += skill.value;
}
}
siege(skill, attacker, defender, allies, enemies, context) {
if (context.enemyCommander) {
const dmg = Math.max(0, skill.value - Math.max(0, context.enemyCommander.currentArmor));
context.enemyCommander.currentHP -= dmg;
}
}
rally(skill, attacker, defender, allies, enemies, context) {
const assaultAllies = allies.filter(a => a.type === 'assault' && a !== attacker && a.currentHP > 0);
if (assaultAllies.length === 0) return;
const rng = context?.rng;
const idx = rng
? rng.nextInt(0, assaultAllies.length - 1)
: Math.floor(Math.random() * assaultAllies.length);
const target = assaultAllies[idx];
target.currentAttack += skill.value;
// Track temp buff so postBattle can reverse it
target._tempBuffs = target._tempBuffs || [];
target._tempBuffs.push({ stat: 'currentAttack', amount: skill.value, source: 'rally' });
// Expose to caller for event / animation data
context.rallyTarget = target;
}
heal(skill, attacker, defender, allies) {
const damaged = allies
.filter(a => a.currentHP > 0 && a.currentHP < a.health)
.sort((a, b) => (a.currentHP / a.health) - (b.currentHP / b.health));
if (damaged.length === 0) return;
damaged[0].currentHP = Math.min(damaged[0].health, damaged[0].currentHP + skill.value);
}
protect(skill, attacker, defender, allies) {
const alive = allies.filter(a => a.currentHP > 0 && a !== attacker);
if (alive.length === 0) return;
const target = alive[Math.floor(Math.random() * alive.length)];
target.currentArmor += skill.value;
}
jam(skill, attacker, defender) {
if (!defender) return;
defender.jamTurns = (defender.jamTurns || 0) + skill.value;
}
enfeeble(skill, attacker, defender) {
if (!defender) return;
defender.currentAttack = Math.max(0, defender.currentAttack - skill.value);
}
weaken(skill, attacker, defender) {
if (!defender) return;
for (const s of defender.skills) {
s.value = Math.max(0, (s.value || 0) - skill.value);
}
}
tribute(skill, attacker, defender, allies, enemies, context) {
// Destroy weakest ally for +3 attack to attacker
const weakest = allies
.filter(a => a !== attacker && a.type === 'assault' && a.currentHP > 0)
.sort((a, b) => a.currentHP - b.currentHP)[0];
if (weakest) {
weakest.currentHP = 0;
attacker.currentAttack += 3;
}
}
evolve(skill, attacker, defender, allies, enemies, context) {
// Evolve to upgraded version — check for evolved card id convention: id + '_e'
const evolvedId = attacker.id + '_e';
if (context.cardManager && context.cardManager.getCard(evolvedId)) {
const evolved = context.cardManager.createInstance(evolvedId);
evolved.currentHP = attacker.currentHP;
evolved.instanceId = attacker.instanceId;
Object.assign(attacker, evolved);
}
}
mortar(skill, attacker, defender, allies, enemies, context) {
if (enemies.length === 0) return;
const rng = context?.rng;
const idx = rng ? rng.nextInt(0, enemies.length - 1) : Math.floor(Math.random() * enemies.length);
const target = enemies[idx];
const dmg = Math.max(0, skill.value - Math.max(0, target.currentArmor));
target.currentHP -= dmg;
if (context) {
context.mortarTarget = target;
context.mortarDamage = dmg;
}
}
flurry(skill, attacker, defender, allies, enemies, context) {
context.extraAttacks = (context.extraAttacks || 0) + skill.value;
}
counter(skill, attacker, defender, allies, enemies, context) {
// When defending — deal counter damage to attacker
if (context.attackingCard) {
const dmg = Math.max(0, skill.value - Math.max(0, context.attackingCard.currentArmor));
context.attackingCard.currentHP -= dmg;
}
}
wall(skill, attacker, defender, allies, enemies, context) {
context.damageAbsorb = (context.damageAbsorb || 0) + skill.value;
}
armored(skill, attacker, defender, allies, enemies, context) {
// Passive: reduce incoming damage — handled in combat engine when applying damage
context.armoredBonus = (context.armoredBonus || 0) + skill.value;
}
valor(skill, attacker, defender, allies, enemies) {
if (enemies.length > allies.length) {
attacker.currentAttack += skill.value;
}
}
legion(skill, attacker, defender, allies) {
const sameFaction = allies.filter(a => a.faction === attacker.faction && a !== attacker && a.currentHP > 0);
attacker.currentAttack += skill.value * sameFaction.length;
}
}