Gamigin
The Eternal Sinner
Unit #031
| HP | PWR | CP | Speed | Range | Tier |
|---|---|---|---|---|---|
| 6 | 6 (+4) | 2 (+1) | Slow | Local | C |
Abilities
Passive
After Gamigin dies, your opponent must Fuse and Exhaust Gamigin's demon card onto a Demon they control if they control any Fusible Demons. (Fused Demons have both names.)
Engine Implementation
def _gamigin_forced_fusion_trigger(
state: GameState, event: GameEvent, demon: DemonInstance, depth: int
) -> GameState | None:
"""#031 Gamigin — Passive trigger: After Gamigin dies, force-fuse onto opponent's demon.
Fires on FATALLY_WOUNDED when Gamigin IS the target (dying demon).
Gamigin itself IS fatally_wounded — this is intentional. The trigger's
effect is applied to the OPPONENT's board, not Gamigin.
Effect: Pick the first fusible enemy demon. Fuse Gamigin's card as the
bottom card and exhaust the resulting demon.
"Fusible Demons" = non-fused (or Dantalion) non-familiar regular unit demons.
Auto-selects first eligible target (deterministic; a full UI implementation
would let the opponent choose which demon to receive Gamigin).
CRITICAL: Gamigin's card data fuses onto top demon → top.fused_bottom = "031",
top.current_hp += Gamigin's fhp (0), top.is_fused = True, top.state = EXHAUSTED.
The fused demon retains the top card's speed, range, and lane.
"""
from engine.data_loader import UNITS
from engine.constants import DemonState
# Only fire if Gamigin (this demon) is the dying target
if event.target is None:
return None
if event.target.instance_id != demon.instance_id:
return None # A different demon is dying — not Gamigin's trigger
# Gamigin IS fatally_wounded here — this is expected and allowed for this trigger
# (the effect applies to the opponent, not Gamigin performing anything)
# Find the opponent's side
opponent = Side.PLAYER_2 if demon.owner == Side.PLAYER_1 else Side.PLAYER_1
# Find the first fusible opponent demon (non-fused OR Dantalion)
# "Fusible" = can accept a bottom card = not already fused (except Dantalion),
# not a familiar (cannot_fuse_familiars = true)
DANTALION_ID = "106"
fusible_target = None
for d in state.demons:
if d.owner != opponent:
continue # must be opponent's demon
if d.is_familiar:
continue # cannot fuse familiars
if d.is_fused and d.unit_id != DANTALION_ID:
continue # already fused (exception: Dantalion)
# This demon can receive Gamigin as bottom card
fusible_target = d
break
if fusible_target is None:
return None # No fusible demons — trigger does nothing
# Apply the forced fusion: Gamigin becomes bottom card of fusible_target
gamigin_data = UNITS["031"]
new_state = copy.deepcopy(state)
# Find the top demon in the new state
top_in_state = next(
(d for d in new_state.demons if d.instance_id == fusible_target.instance_id),
None,
)
if top_in_state is None:
return None
top_unit = UNITS[top_in_state.unit_id]
# Apply fusion bonus: top.current_hp = top base hp + Gamigin fhp (0)
top_in_state.current_hp = top_unit.hp + (gamigin_data.fhp or 0)
top_in_state.fused_bottom = "031"
top_in_state.is_fused = True
# "Fuse and Exhaust" — the resulting demon is EXHAUSTED
top_in_state.state = DemonState.EXHAUSTED
# Existing damage preserved (lane preserved — no change needed)
# Gamigin is NOT removed here — death resolution happens after triggers
# The fusible_target now embeds Gamigin as bottom card
return new_state
register_trigger("031", 0, _gamigin_forced_fusion_trigger)