Skip to main content

Foras

A Shadow

Unit #072

HPPWRCPSpeedRangeTier
9 (+3)3 (+1)3 (+2)NormalLocalC

Abilities

Passive

After Foras successfully pays for an action with a a cost (even if the a cost is ignored) and targets at least 1 Demon, you may Exhaust the targeted Demons within the action's range.
Engine Implementation
def _foras_exhaust_targets(
state,
event,
demon,
depth: int,
):
"""After Foras uses an exhaust-cost action targeting demons, exhaust the targets.

Fires on ABILITY_USED. If Foras is the source, and the event carries a target,
exhaust the target demon.

event.source = Foras (must be this instance)
event.target = the targeted demon (first target; multi-target not yet handled)

Note: We check if Foras itself was exhausted as a proxy for the action having
had a tap_required cost (exhaust cost). Foras's abilities with exhaust cost
will have exhausted Foras as part of action resolution.
"""
if event.source is None:
return None
if event.source.instance_id != demon.instance_id:
return None
if event.target is None:
# No target — cannot exhaust
return None

# Check if Foras is now EXHAUSTED (proxy for action having had exhaust cost)
foras_in_state = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
if foras_in_state is None:
return None
if foras_in_state.state != DemonState.EXHAUSTED:
# Foras was not exhausted → action did not have exhaust cost
return None

# Exhaust the target
target_in_state = next(
(d for d in state.demons if d.instance_id == event.target.instance_id), None
)
if target_in_state is None:
return None
if target_in_state.state == DemonState.EXHAUSTED:
# Already exhausted — no change needed
return None

new_state = copy.deepcopy(state)
target_new = next(
(d for d in new_state.demons if d.instance_id == event.target.instance_id), None
)
if target_new is not None:
target_new.state = DemonState.EXHAUSTED
return new_state

register_trigger("072", 0, _foras_exhaust_targets)

Shadow Step — 0 AP

g: Shadow Step: 0 AP - 1x: Move Foras 1 Lane.
Engine Implementation
def _foras_shadow_step(
state: GameState, demon: DemonInstance, targets, choices, rng
) -> GameState:
"""#072 Foras — Shadow Step
Quick, 0 AP, 1x: Move Foras 1 Lane.

Quick timing, no exhaust/ready requirement.
1x per turn (confusion #20).
Target lane from choices["lane"] (1 adjacent lane).
Note: This action has no (exhaust) or (ready) cost.

Targets: None
Choices: {"lane": int}
"""
return _move_to_lane(state, demon, choices["lane"])

register_ability("072", 1, _foras_shadow_step)
Foras