diff --git a/src/combat.test.js b/src/combat.test.js index c887572..996c755 100644 --- a/src/combat.test.js +++ b/src/combat.test.js @@ -1,6 +1,7 @@ import { beforeAll, describe, expect, test } from "bun:test"; import { initCards } from "./cards.js"; import { checkCombatEnd, resolveEnemyTurn, startTurn } from "./combat.js"; +import { resolveEffects } from "./effects.js"; import { initEnemies } from "./enemies.js"; import { createCombatState } from "./state.js"; @@ -144,3 +145,80 @@ describe("checkCombatEnd - multiplayer", () => { expect(checkCombatEnd(state)).toBe("defeat"); }); }); + +describe("poison effect", () => { + test("applying poison adds poison to target enemy", () => { + let state = createCombatState("ironclad", "jaw_worm"); + state = { + ...state, + enemies: [{ ...state.enemies[0], poison: 0 }], + enemy: { ...state.enemy, poison: 0 }, + }; + const next = resolveEffects( + state, + [{ type: "poison", value: 3 }], + { type: "player", index: 0 }, + { type: "enemy", index: 0 }, + ); + expect(next.enemies[0].poison).toBe(3); + }); + + test("poison stacks additively up to 30", () => { + let state = createCombatState("ironclad", "jaw_worm"); + state = { + ...state, + enemies: [{ ...state.enemies[0], poison: 0 }], + enemy: { ...state.enemy, poison: 0 }, + }; + let next = resolveEffects( + state, + [{ type: "poison", value: 20 }], + { type: "player", index: 0 }, + { type: "enemy", index: 0 }, + ); + next = resolveEffects( + next, + [{ type: "poison", value: 15 }], + { type: "player", index: 0 }, + { type: "enemy", index: 0 }, + ); + expect(next.enemies[0].poison).toBe(30); + }); +}); + +describe("poison tick", () => { + test("poison damages enemy and decrements at start of enemy turn", () => { + let state = createCombatState("ironclad", "jaw_worm"); + const poisonedEnemy = { ...state.enemies[0], poison: 5, hp: 10 }; + state = { + ...state, + enemies: [poisonedEnemy], + enemy: poisonedEnemy, + combat: { ...state.combat, dieResult: 1 }, + }; + const next = resolveEnemyTurn(state); + expect(next.enemies[0].hp).toBe(5); + expect(next.enemies[0].poison).toBe(4); + }); + + test("enemy dies from poison", () => { + let state = createCombatState("ironclad", "jaw_worm"); + const poisonedEnemy = { ...state.enemies[0], poison: 10, hp: 3 }; + state = { + ...state, + enemies: [poisonedEnemy], + enemy: poisonedEnemy, + combat: { ...state.combat, dieResult: 1 }, + }; + const next = resolveEnemyTurn(state); + expect(next.enemies[0].hp).toBe(0); + expect(next.enemies[0].poison).toBe(9); + }); + + test("zero poison does not tick", () => { + let state = createCombatState("ironclad", "jaw_worm"); + state = { ...state, combat: { ...state.combat, dieResult: 1 } }; + const next = resolveEnemyTurn(state); + expect(next.enemies[0].poison).toBe(0); + }); +}); diff --git a/src/effects.js b/src/effects.js index be32590..bb6520a 100644 --- a/src/effects.js +++ b/src/effects.js @@ -71,6 +71,8 @@ function resolveSingleEffect(state, effect, source, target) { return applyStatus(state, target, "vulnerable", effect.value, 3); case "weak": return applyStatus(state, target, "weak", effect.value, 3); + case "poison": + return applyStatus(state, target, "poison", effect.value, 30); case "strength": return applyStatus(state, source, "strength", effect.value, 8); case "draw": { diff --git a/src/state.js b/src/state.js index 7b87730..2a42331 100644 --- a/src/state.js +++ b/src/state.js @@ -14,6 +14,7 @@ function makePlayer(character, index) { strength: 0, vulnerable: 0, weak: 0, + poison: 0, drawPile: shuffle([...getStarterDeck(character)]), hand: [], discardPile: [], @@ -34,6 +35,7 @@ function makeEnemy(enemyId, index) { strength: 0, vulnerable: 0, weak: 0, + poison: 0, actionType: enemy.actionType, actions: enemy.actions, actionTrack: enemy.actionTrack || null, @@ -88,6 +90,7 @@ export function createCombatFromRun(run, enemyIdOrIds) { strength: 0, vulnerable: 0, weak: 0, + poison: 0, drawPile: shuffle([...run.deck]), hand: [], discardPile: [],