112 lines
2.8 KiB
JavaScript
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) },
|
|
};
|
|
}
|