Implement casting layer: attack stack, counteraction chain, first effects
The attack/counteraction stack: casting an attack (or punching) opens a stack; the defender counteracts or passes, the attacker may respond (ANTI-ANTI nullifies a counter), and resolution runs the damage pipeline in play order. First effect wave, encoded from verified card text: Fireball (flat 5 + destroys carried magic stones if damage gets through), Lightning Blast (number damage + stun unless fully stopped), Powerthrust (2 + optional number), Waterbolt (caster-chosen damage/knockback split with push-away movement), Blunt (halve, round result up), Absorb (-3), Full Shield (spell-only stop), Reflection (half to both), Full Reflection (redirect), Absorb Spell (nullify and steal the attack card), Create/Destroy Wall (dynamic edge overrides layered over the board; collapse deals 4 to adjacent squares), Speed (extra turn), and TRAP! on draw (lose next turn, redraw). Lost turns skip on advance; unimplemented cards refuse to cast with a clear error. 35 tests passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a8884592a4
commit
7c607ad5c7
@@ -115,17 +115,17 @@ describe("turns and combat", () => {
|
||||
const attacker = activePlayer(state);
|
||||
const victim = state.players.find((p) => p.id !== attacker.id)!;
|
||||
victim.position = { ...attacker.position };
|
||||
const afterPunch = applyCommand(state, attacker.id, { type: "punch", targetId: victim.id });
|
||||
expect(afterPunch.ok).toBe(true);
|
||||
if (afterPunch.ok) {
|
||||
const v = afterPunch.state.players.find((p) => p.id === victim.id)!;
|
||||
expect(v.life).toBe(STARTING_LIFE - 1);
|
||||
const secondPunch = applyCommand(afterPunch.state, attacker.id, {
|
||||
type: "punch",
|
||||
targetId: victim.id,
|
||||
});
|
||||
expect(secondPunch.ok).toBe(false);
|
||||
}
|
||||
state = must(state, attacker.id, { type: "punch", targetId: victim.id });
|
||||
// Punches enter the counteraction stack (BLUNT/ABSORB work on physical
|
||||
// damage); the defender passes and the punch resolves.
|
||||
state = must(state, victim.id, { type: "pass" });
|
||||
const v = state.players.find((p) => p.id === victim.id)!;
|
||||
expect(v.life).toBe(STARTING_LIFE - 1);
|
||||
const secondPunch = applyCommand(state, attacker.id, {
|
||||
type: "punch",
|
||||
targetId: victim.id,
|
||||
});
|
||||
expect(secondPunch.ok).toBe(false);
|
||||
});
|
||||
|
||||
it("cannot punch yourself", () => {
|
||||
@@ -145,11 +145,10 @@ describe("turns and combat", () => {
|
||||
const victim = state.players.find((p) => p.id !== attacker.id)!;
|
||||
victim.position = { ...attacker.position };
|
||||
victim.life = 1; // test surgery: one punch kills
|
||||
const result = applyCommand(state, attacker.id, { type: "punch", targetId: victim.id });
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
state = must(state, attacker.id, { type: "punch", targetId: victim.id });
|
||||
state = must(state, victim.id, { type: "pass" });
|
||||
|
||||
const s = result.state;
|
||||
const s = state;
|
||||
expect(s.players.find((p) => p.id === victim.id)!.alive).toBe(false);
|
||||
const a = s.players.find((p) => p.id === attacker.id)!;
|
||||
expect(a.hand.length).toBe(14);
|
||||
|
||||
Reference in New Issue
Block a user