Asmodeus
Demon of Lust
Unit #090
| HP | PWR | CP | Speed | Range | Tier |
|---|---|---|---|---|---|
| 9 (+3) | 3 (+1) | 3 (+2) | Fast | Local | B |
Abilities
Passive
Once per Main Phase, when Asmodues moves at least 1 Lane, you may move 1 Local Demon with Asmodeus.
Engine Implementation
def _asmodeus_drag_ally_trigger(
state: GameState, event: GameEvent, demon: DemonInstance, depth: int
):
"""#090 Asmodeus — Passive trigger: DEMON_MARCHED
Once per Main Phase, when Asmodeus moves at least 1 Lane, may move
1 Local Demon with Asmodeus.
Fires on DEMON_MARCHED when the marching demon is this Asmodeus instance.
"Local Demon" = a demon that was in Asmodeus's ORIGIN lane (event.lane).
Asmodeus is now at his new lane after the march.
"Once per Main Phase" — checked via abilities_used_this_turn.
If already used this turn, returns None.
For deterministic testing, moves the first eligible local demon found
(excluding Asmodeus itself) from the origin lane to Asmodeus's new lane.
A production engine would present a choice to the player.
Re-fetches Asmodeus from state to check fatally_wounded before acting.
"""
import copy as _copy
# Only fire when Asmodeus himself is the demon that marched
if event.source is None:
return None
if event.source.instance_id != demon.instance_id:
return None # A different demon marched — not Asmodeus
# Re-fetch Asmodeus from state
asmodeus_current = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
if asmodeus_current is None or asmodeus_current.fatally_wounded:
return None
# "Once per Main Phase" — check usage tracking
ability_key = "0" # ability index 0
use_count = asmodeus_current.abilities_used_this_turn.get(ability_key, 0)
if use_count >= 1:
return None # Already used once this main phase
# Origin lane = event.lane (where Asmodeus was before the march)
origin_lane = event.lane
if origin_lane is None:
return None
# Find a local demon in the origin lane (excluding Asmodeus himself)
eligible = [
d for d in state.demons
if d.lane == origin_lane
and d.instance_id != asmodeus_current.instance_id
]
if not eligible:
return None # No demon to drag
# Auto-select the first eligible demon (production: player choice)
drag_target = eligible[0]
if drag_target.fatally_wounded:
return None # Cannot move a fatally wounded demon
new_state = _copy.deepcopy(state)
# Move drag_target to Asmodeus's new lane
drag_in_new = next(
(d for d in new_state.demons if d.instance_id == drag_target.instance_id), None
)
if drag_in_new is None:
return None
drag_in_new.lane = asmodeus_current.lane
# Record usage: "once per main phase"
asmodeus_in_new = next(
(d for d in new_state.demons if d.instance_id == demon.instance_id), None
)
if asmodeus_in_new is not None:
asmodeus_in_new.abilities_used_this_turn[ability_key] = use_count + 1
return new_state
register_trigger("090", 0, _asmodeus_drag_ally_trigger)
Temptation — 2 AP
Temptation: 2 AP - a, 1x: Exhaust Target Local Demon. If the targeted Demon has no Local Allied Demons (other than themselves), gain 2 AP and Ready Asmodeus.
Engine Implementation
def _asmodeus_temptation(
state: GameState, demon: DemonInstance, targets, choices, rng
) -> GameState:
"""#090 Asmodeus — Temptation
Action, 2 AP, (exhaust), 1x: Exhaust Target Local Demon.
If the targeted Demon has no Local Allied Demons (other than themselves),
gain 2 AP and Ready Asmodeus.
"Local Allied Demons (other than themselves)" = target's own allied demons
in target's lane, excluding itself. If zero such demons exist, Asmodeus's
controller gains 2 AP and Asmodeus is readied.
CRITICAL: Asmodeus is exhausted by execute_ability (tap_required=True) BEFORE
this handler is called. The "Ready Asmodeus" effect re-readies Asmodeus here.
"""
from engine.operations import exhaust_demon, ready_demon
import copy as _copy
if not targets:
return state
target = targets[0]
# Exhaust the target demon
state = exhaust_demon(state, target)
# Re-fetch target after state change
target_refreshed = next(
(d for d in state.demons if d.instance_id == target.instance_id), None
)
if target_refreshed is None:
return state
# Check if target has NO Local Allied Demons (other than itself)
# "Allied" = same owner as target demon
local_allies_of_target = [
d for d in state.demons
if d.lane == target_refreshed.lane
and d.owner == target_refreshed.owner
and d.instance_id != target_refreshed.instance_id
]
if len(local_allies_of_target) == 0:
# Target is alone — trigger bonus: gain 2 AP and Ready Asmodeus
new_state = _copy.deepcopy(state)
new_state.players[demon.owner].ap += 2
# Ready Asmodeus (he was exhausted by execute_ability tap_required)
asmodeus_in_new = next(
(d for d in new_state.demons if d.instance_id == demon.instance_id), None
)
if asmodeus_in_new is not None:
asmodeus_in_new.state = DemonState.READIED
return new_state
return state
register_ability("090", 1, _asmodeus_temptation)