✅ Snake bind exhausts target
| Category | Regression |
| Status | Passing |
| Test | tests/test_abilities_familiars.py::test_snake_bind_exhausts_target |
Snake A's Bind ability applies cannot_act status to target enemy demon.
Preconditions
-
Lane 0: P1's Snake A (#036_1) — familiar, READIED, 0 AP cost, 1x.
-
Lane 0: P2's Duban (#001) — 6 HP, 0 damage, READIED.
-
P1 AP: 3.
-
Start of turn window is open.
Action
- Execute Snake A's Bind (ability 0, Start of Turn, 0 AP, ready, 1x)
from engine.abilities import execute_ability
from engine.status_effects import get_active_effects_on
state = make_game_state()
state.players[Side.PLAYER_1].ap = 3
state.current_player = Side.PLAYER_1
snake = make_familiar_demon("036_1", lane=0, owner=Side.PLAYER_1)
state = place_demon(state, snake)
snake = state.demons[-1]
duban = make_demon("001", lane=0, owner=Side.PLAYER_2)
state = place_demon(state, duban)
duban = state.demons[-1]
new_state = execute_ability(state, snake, 0, targets=[duban])
duban_after = next(d for d in new_state.demons if d.unit_id == "001")
effects = get_active_effects_on(new_state, duban_after)
cannot_act_effects = [e for e in effects if e.stat == "cannot_act"]
Expected Postconditions
-
Duban has "cannot_act" status effect (value=1).
-
Snake A still READIED (Bind does not exhaust).
-
P1 AP: 3 (0 AP cost).
-
Note: Familiars ARE demons (confusion #2) — Snake A is a demon and can
-
target other demons including enemy demons.
Assertions
assert len(cannot_act_effects) >= 1, (
f"Duban should have 'cannot_act' status after Snake Bind. "
f"Active effects: {[(e.stat, e.value) for e in effects]}"
)
# Snake is still READIED (bind does not exhaust)
snake_after = next(d for d in new_state.demons if d.unit_id == "036_1")
assert snake_after.state == DemonState.READIED, (
f"Snake A should remain READIED after Bind (no exhaust cost). "
f"Got {snake_after.state}"
)
# AP unchanged
assert new_state.players[Side.PLAYER_1].ap == 3, (
f"Bind costs 0 AP — P1 AP should still be 3. Got {new_state.players[Side.PLAYER_1].ap}"
)