Skip to main content

Zagan

Transmuter of Reality

Unit #024

HPPWRCPSpeedRangeTier
621FastAnyC

Abilities

Passive

After an action is declared that can be paid for (including its potential targets), you may move Zagan to Any Lane.
Engine Implementation
def _zagan_reposition_trigger(
state: GameState, event: GameEvent, demon: DemonInstance, depth: int
):
"""#024 Zagan — Passive trigger: ACTION_DECLARED
After any action is declared that can be paid for, Zagan may move to Any Lane.

Fires on ACTION_DECLARED for every action declared on the board.
Zagan can reposition to any of the 3 lanes (no AP cost, no exhaust).
This is Zagan's signature repositioning — free tactical relocation.

For deterministic testing, defaults to lane in event.lane metadata if present
and different from Zagan's current lane. Otherwise picks next lane numerically.
A production engine would present a lane-choice prompt to the player.

Re-fetches Zagan from state to check fatally_wounded before acting.
"""
from engine.constants import LANE_COUNT
import copy as _copy

# Re-fetch Zagan from state — state may have changed from prior handlers
zagan_current = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
if zagan_current is None or zagan_current.fatally_wounded:
return None # Zagan dead or dying — no reposition

# Determine destination lane:
# If the event carries a lane hint (from choices in the triggering action),
# and that lane differs from Zagan's current lane, use it.
# Otherwise, pick the next numerically distinct lane (deterministic default).
if event.lane is not None and event.lane != zagan_current.lane:
dest_lane = event.lane
else:
# Pick the first lane that differs from Zagan's current lane
dest_lane = next(
(ln for ln in range(LANE_COUNT) if ln != zagan_current.lane),
zagan_current.lane, # fallback if no other lane exists (shouldn't happen)
)

if dest_lane == zagan_current.lane:
return None # Already in the destination lane — no change needed

# Teleport Zagan to dest_lane (Any Lane — not restricted to adjacent)
new_state = _copy.deepcopy(state)
zagan_in_new = next(
(d for d in new_state.demons if d.instance_id == demon.instance_id), None
)
if zagan_in_new is None:
return None
zagan_in_new.lane = dest_lane
return new_state

register_trigger("024", 0, _zagan_reposition_trigger)
Zagan