Skip to main content

Vapula

Sovereign of Beasts

Unit #100

HPPWRCPSpeedRangeTier
12 (+6)4 (+2)3 (+2)NormalLocalC

Abilities

Passive

Whenever Vapula would deal X damage (before n) to Any Other Demon, you may remove X damage instead.
Engine Implementation
def _vapula_damage_to_healing(
state: GameState, event: GameEvent, demon: DemonInstance, depth: int
):
"""#100 Vapula idx=0 — Field Passive: Damage-to-Healing Conversion.

Whenever Vapula deals X damage (before DEF) to Any Other Demon, may instead
remove X damage from that target.

Fires on DAMAGE_DEALT. Checks event.source is this Vapula instance.

"Remove X damage instead" interpretation:
- The X damage was already applied at event fire time.
- "Instead" means: undo the dealt damage AND apply the heal.
- Net effect on target's damage counter: remove X (undo) + remove X (heal) = -2X.
- Implemented as: remove_damage(state, target, 2 * X)

Re-fetches Vapula from state to check fatally_wounded before resolving.
"""
from engine.operations import remove_damage

# Only fire if THIS Vapula is the damage source
if event.source is None or event.source.instance_id != demon.instance_id:
return None

x = event.value if event.value is not None else 0
if x <= 0:
return None

# Re-fetch Vapula from current state
vapula_current = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
if vapula_current is None or vapula_current.fatally_wounded:
return None # Vapula dead or dying — cannot resolve passive

# Target is the demon that received Vapula's damage
if event.target is None:
return None

target_current = next(
(d for d in state.demons if d.instance_id == event.target.instance_id), None
)
if target_current is None:
return None # Target already gone — nothing to heal

# Remove 2X damage: X to undo the dealt damage + X as the healing
return remove_damage(state, target_current, 2 * x)

register_trigger("100", 0, _vapula_damage_to_healing)

Cycle of Life — 3 AP

h: Cycle of Life: 3 AP - a: e: All Local Demons cannot move or be moved. At the end of this Main Phase, if Vapula is alive, deal k damage to All Other Local Demons.
Engine Implementation
def _vapula_cycle_of_life(
state: GameState, demon: DemonInstance, targets, choices, rng
) -> GameState:
"""#100 Vapula — Cycle of Life
Start of Turn, 3 AP, (exhaust): Status: All Local Demons cannot move or be moved.
At the end of this Main Phase, if Vapula is alive, deal PWR damage to All Other
Local Demons.

CRITICAL (confusion #21): "All Other Local Demons" includes allied demons.
Applies "cannot_move" status to all local demons.
Applies "cycle_of_life_pending" marker to Vapula for end-of-phase damage.
"""
from engine.status_effects import apply_status

# Status: all local demons cannot move or be moved
local_demons = [d for d in state.demons if d.lane == demon.lane]
for d in local_demons:
state = apply_status(state, demon, d, "cannot_move", 1)

# Marker for end-of-phase damage
vapula_current = next(
(d for d in state.demons if d.instance_id == demon.instance_id), None
)
if vapula_current is not None:
state = apply_status(state, demon, vapula_current, "cycle_of_life_pending", 1)

return state

register_ability("100", 1, _vapula_cycle_of_life)
Vapula