Skip to main content

Iskandar

The Arrogant Warrior

Unit #104

HPPWRCPSpeedRangeTier
12 (+6)23 (+2)FastLocalB

Abilities

Passive

When an Enemy Demon would deal at least 1 damage to Iskandar (before n), Iskandar may perform an Attack basic action that targets that demon Without Cost. (Range is not ignored.)
Engine Implementation
def _iskandar_counter_trigger(
state: GameState, event: GameEvent, demon: DemonInstance, depth: int
):
"""#104 Iskandar — Passive trigger: DAMAGE_RECEIVED
When an Enemy Demon would deal at least 1 damage to Iskandar (before DEF),
Iskandar may perform an Attack basic action targeting that demon Without Cost.
Range is not ignored.

Fires on DAMAGE_RECEIVED when Iskandar is the TARGET and the SOURCE is an
enemy demon. event.value = raw damage before DEF.

"Without Cost" = 0 AP, Iskandar does NOT exhaust.
"Range is not ignored" = source demon must be in range of Iskandar's range.
Iskandar's range is "Local" by default — see units.json.

Re-fetches Iskandar from state to check fatally_wounded before acting.
"""
from engine.operations import get_effective_pwr, deal_damage
import copy as _copy

# Only fire if Iskandar is the target of this damage
if event.target is None:
return None
if event.target.instance_id != demon.instance_id:
return None

# Only fire if an enemy is the source (not self-damage or allied damage)
if event.source is None:
return None
if event.source.owner == demon.owner:
return None # Allied source — not an enemy attack

# Only fire if raw damage >= 1 (before DEF)
if event.value is None or event.value < 1:
return None

# Re-fetch Iskandar from state
iskandar_current = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
if iskandar_current is None or iskandar_current.fatally_wounded:
return None

# Re-fetch the attacker from current state
attacker_current = next(
(d for d in state.demons if d.instance_id == event.source.instance_id), None
)
if attacker_current is None or attacker_current.fatally_wounded:
return None

# Range check: Iskandar's range must reach the attacker.
# Iskandar's range_enum determines if the attacker is in range.
# Local: same lane. We check against attacker's current lane.
from engine.data_loader import UNITS
from engine.constants import Range

iskandar_unit = UNITS.get(iskandar_current.unit_id)
if iskandar_unit is not None and iskandar_unit.range_enum == Range.LOCAL:
if attacker_current.lane != iskandar_current.lane:
return None # Out of range — no counter

# Counter-attack Without Cost: 0 AP, no exhaust
pwr = get_effective_pwr(state, iskandar_current)
return deal_damage(state, iskandar_current, attacker_current, pwr)

register_trigger("104", 0, _iskandar_counter_trigger)

Taunt — 1 AP

Taunt: 1 AP - a: e: Iskandar gains +2n. Target Demon performs an Attack basic action Without Cost, targeting Iskandar, and ignoring range. Exhaust the targeted Demon.
Engine Implementation
def _iskandar_taunt(
state: GameState, demon: DemonInstance, targets, choices, rng
) -> GameState:
"""#104 Iskandar — Taunt
Action, 1 AP, (exhaust): Status: Iskandar gains +2 DEF. Target Demon performs
an Attack basic action Without Cost, targeting Iskandar, and ignoring range.
Exhaust the targeted Demon.

Effect 1: Apply +2 DEF status to Iskandar (expires end of main phase).
Effect 2: Target demon attacks Iskandar without AP cost (range ignored).
The attack is a basic Attack — Iskandar's DEF applies (including +2 buff).
Effect 3: Exhaust the target demon.

CRITICAL: The +2 DEF status is applied FIRST, so Iskandar benefits from the
buff when taking the forced attack.
Status expires end of current main phase (confusion #9 — NOT permanent).
"""
from engine.operations import get_effective_pwr, deal_damage, exhaust_demon
from engine.status_effects import apply_status

if not targets:
return state

target = targets[0]

# Re-fetch Iskandar from state (demon is always Iskandar, idx=1)
iskandar_in_state = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
if iskandar_in_state is None:
return state

# Effect 1: Apply +2 DEF status to Iskandar (BEFORE the forced attack)
state = apply_status(state, iskandar_in_state, iskandar_in_state, "def", 2)

# Re-fetch Iskandar and target after deepcopy from apply_status
iskandar_in_state = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
target_in_state = next(
(d for d in state.demons if d.instance_id == target.instance_id), None
)
if iskandar_in_state is None or target_in_state is None:
return state

# Effect 2: Target performs a basic Attack on Iskandar without cost, ignoring range
# This is regular damage from the target's PWR (DEF applies)
target_pwr = get_effective_pwr(state, target_in_state)
state = deal_damage(state, target_in_state, iskandar_in_state, target_pwr)

# Re-fetch target after deal_damage
target_in_state = next(
(d for d in state.demons if d.instance_id == target.instance_id), None
)
if target_in_state is None:
return state

# Effect 3: Exhaust the target demon
state = exhaust_demon(state, target_in_state)

return state

register_ability("104", 1, _iskandar_taunt)
Iskandar