Skip to main content

manabrew_engine/ability/effects/
haunt_effect.rs

1//! Haunt effect — exile card haunting a creature.
2//!
3//! Ported 1:1 from Java's `HauntEffect.java`.
4//! When a creature with Haunt dies (or spell resolves), exile it haunting
5//! target creature. When the haunted creature dies, the haunt triggers.
6
7use forge_foundation::ZoneType;
8
9use super::{emit_zone_trigger, EffectContext};
10use crate::ids::CardId;
11
12/// Struct form of this effect so it can participate in the
13/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
14/// `HauntEffect` class extending `SpellAbilityEffect`.
15#[manabrew_engine_macros::spell_effect(HauntEffect)]
16fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
17    let Some(source_id) = sa.source else { return };
18    let controller = sa.activating_player;
19
20    // Find what to haunt (target creature or trigger source)
21    let haunt_target: Option<CardId> = if sa.uses_targeting() {
22        sa.target_chosen.target_card
23    } else {
24        // Default: choose a creature on the battlefield
25        let creatures: Vec<CardId> = ctx
26            .game
27            .cards
28            .iter()
29            .filter(|c| {
30                c.zone == ZoneType::Battlefield
31                    && c.type_line
32                        .core_types
33                        .iter()
34                        .any(|ct| matches!(ct, forge_foundation::CoreType::Creature))
35            })
36            .map(|c| c.id)
37            .collect();
38        if creatures.is_empty() {
39            None
40        } else {
41            ctx.agents[controller.index()].snapshot_state(ctx.game, ctx.mana_pools);
42            ctx.agents[controller.index()].choose_single_card_for_zone_change(
43                ctx.game,
44                controller,
45                &creatures,
46                "Choose a creature to haunt",
47                false,
48            )
49        }
50    };
51
52    let Some(target_id) = haunt_target else {
53        return;
54    };
55
56    // Verify target is still on battlefield
57    if ctx.game.card(target_id).zone != ZoneType::Battlefield {
58        return;
59    }
60
61    // Exile the haunting card
62    let old_zone = ctx.game.card(source_id).zone;
63    if old_zone != ZoneType::Exile {
64        ctx.game
65            .move_card(source_id, ZoneType::Exile, ctx.game.card(source_id).owner);
66        emit_zone_trigger(ctx.trigger_handler, source_id, old_zone, ZoneType::Exile);
67    }
68
69    // Link: set exiled_by to the haunted creature
70    // When the haunted creature dies, triggers check for cards with exiled_by pointing to it
71    ctx.game.card_mut(source_id).set_exiled_by(Some(target_id));
72
73    // Remember the haunted creature on the source
74    ctx.game.card_mut(source_id).add_remembered_card(target_id);
75}