Skip to main content

manabrew_engine/ability/effects/
destroy_effect.rs

1use forge_foundation::ZoneType;
2
3use super::{emit_zone_trigger_with_lki_counters, EffectContext};
4use crate::card::card_util;
5use crate::event::RunParams;
6use crate::trigger::TriggerType;
7
8/// Struct form of this effect so it can participate in the
9/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
10/// `DestroyEffect` class extending `SpellAbilityEffect`.
11#[manabrew_engine_macros::spell_effect(DestroyEffect)]
12fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
13    let mut targets = Vec::new();
14    if let Some(target_card) = sa.target_chosen.target_card {
15        targets.push(target_card);
16    }
17    targets.extend(card_util::get_radiance(ctx.game, sa).iter().copied());
18    targets.sort_unstable_by_key(|cid| cid.0);
19    targets.dedup();
20
21    let no_regen = sa.ir.no_regen;
22    let remember_destroyed = sa.ir.remember_destroyed;
23    let always_remember = sa.ir.always_remember;
24
25    for target_card in targets {
26        if ctx.game.card(target_card).zone == ZoneType::Battlefield {
27            let is_indestructible = ctx.game.card(target_card).has_indestructible();
28            let has_regen_shield = ctx.game.card(target_card).regeneration_shields > 0;
29
30            // Indestructible prevents destruction (CR 702.12)
31            if is_indestructible {
32                // `AlwaysRemember` remembers the target even when destruction is
33                // prevented (Java `AbilityUtils.setCauseSA` path for "that card"
34                // references in chained subs).
35                if always_remember {
36                    if let Some(sid) = sa.source {
37                        ctx.game.card_mut(sid).add_remembered_card(target_card);
38                    }
39                }
40                continue;
41            }
42
43            // Regeneration (CR 701.15): consume a shield instead of destroying,
44            // unless `NoRegen$ True` suppresses the replacement.
45            if has_regen_shield && !no_regen {
46                ctx.game.card_mut(target_card).regeneration_shields -= 1;
47                // Regenerating taps the creature and removes it from combat.
48                ctx.game.card_mut(target_card).tapped = true;
49                if always_remember {
50                    if let Some(sid) = sa.source {
51                        ctx.game.card_mut(sid).add_remembered_card(target_card);
52                    }
53                }
54                continue;
55            }
56            let owner = ctx.game.card(target_card).owner;
57
58            // Capture +1/+1 counter count before move (for Modular death triggers)
59            let lki_p1p1 = *ctx
60                .game
61                .card(target_card)
62                .counters
63                .get(&crate::card::CounterType::P1P1)
64                .unwrap_or(&0);
65
66            // Capture LKI counters for death triggers (e.g. Servant of the Scale)
67            {
68                let lki_counters = ctx.game.card(target_card).counters.clone();
69                let lki_power = ctx.game.card(target_card).power();
70                let lki_toughness = ctx.game.card(target_card).toughness();
71                ctx.game.card_mut(target_card).lki_counters = Some(lki_counters);
72                ctx.game
73                    .card_mut(target_card)
74                    .set_lki_power_toughness(Some(lki_power), Some(lki_toughness));
75            }
76            // Fire Destroyed trigger before moving to graveyard
77            ctx.trigger_handler.run_trigger(
78                TriggerType::Destroyed,
79                RunParams {
80                    card: Some(target_card),
81                    causer: sa.source,
82                    cause_card: sa.source,
83                    cause_player: Some(sa.activating_player),
84                    ..Default::default()
85                },
86                false,
87            );
88
89            ctx.move_card(target_card, ZoneType::Graveyard, owner);
90
91            emit_zone_trigger_with_lki_counters(
92                ctx.trigger_handler,
93                target_card,
94                ZoneType::Battlefield,
95                ZoneType::Graveyard,
96                lki_p1p1,
97                ctx.game
98                    .card(target_card)
99                    .lki_power
100                    .unwrap_or_else(|| ctx.game.card(target_card).power()),
101                ctx.game
102                    .card(target_card)
103                    .lki_toughness
104                    .unwrap_or_else(|| ctx.game.card(target_card).toughness()),
105            );
106
107            // Track the destroyed card on the source so chained sub-abilities
108            // (`Destroyed` triggers in `EffectEffect`, "that card" references)
109            // can find it.
110            if remember_destroyed || always_remember {
111                if let Some(sid) = sa.source {
112                    ctx.game.card_mut(sid).add_remembered_card(target_card);
113                }
114            }
115        }
116    }
117}