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