slaywithfriends/src/effects.js

112 lines
2.8 KiB
JavaScript

import { drawCards } from "./state.js";
export function calculateHitDamage(
base,
strength,
targetVulnerable,
attackerWeak,
) {
if (targetVulnerable && attackerWeak) {
return base + strength;
}
let damage = base + strength;
if (targetVulnerable) {
damage *= 2;
}
if (attackerWeak) {
damage -= 1;
}
return Math.max(0, damage);
}
export function resolveEffects(state, effects, source, target) {
let current = state;
for (const effect of effects) {
current = resolveSingleEffect(current, effect, source, target);
}
return current;
}
function resolveSingleEffect(state, effect, source, target) {
switch (effect.type) {
case "hit":
return resolveHit(state, effect.value, source, target);
case "block":
return resolveBlock(state, effect.value, source);
case "vulnerable":
return applyStatus(state, target, "vulnerable", effect.value, 3);
case "weak":
return applyStatus(state, target, "weak", effect.value, 3);
case "strength":
return applyStatus(state, source, "strength", effect.value, 8);
case "draw":
return drawCards(state, effect.value);
case "lose_hp":
return directDamage(state, source, effect.value);
case "exhaust":
// handled by playCard — card goes to exhaustPile instead of discardPile
return state;
default:
console.debug(`unhandled effect type: ${effect.type}`);
return state;
}
}
function resolveHit(state, baseValue, source, target) {
const attacker = state[source];
const defender = state[target];
const damage = calculateHitDamage(
baseValue,
attacker.strength,
defender.vulnerable > 0,
attacker.weak > 0,
);
let block = defender.block;
let hp = defender.hp;
let remaining = damage;
if (block > 0) {
const absorbed = Math.min(block, remaining);
block -= absorbed;
remaining -= absorbed;
}
hp = Math.max(0, hp - remaining);
const vulnerable = Math.max(0, defender.vulnerable - 1);
const attackerWeak = Math.max(0, state[source].weak - 1);
return {
...state,
[target]: { ...defender, hp, block, vulnerable },
[source]: { ...state[source], weak: attackerWeak },
};
}
function resolveBlock(state, value, source) {
const entity = state[source];
const maxBlock = source === "player" ? 20 : 999;
const block = Math.min(entity.block + value, maxBlock);
return {
...state,
[source]: { ...entity, block },
};
}
function applyStatus(state, target, status, value, max) {
const entity = state[target];
const current = entity[status] || 0;
return {
...state,
[target]: { ...entity, [status]: Math.min(current + value, max) },
};
}
function directDamage(state, target, value) {
const entity = state[target];
return {
...state,
[target]: { ...entity, hp: Math.max(0, entity.hp - value) },
};
}