manabrew_engine/ability/effects/
counters_remove_effect.rs1use forge_foundation::ZoneType;
2
3use super::{parse_counter_type, EffectContext};
4use crate::event::RunParams;
5use crate::ids::CardId;
6use crate::replacement::replacement_handler::{apply_replacements, ReplacementEvent};
7use crate::replacement::ReplacementResult;
8use crate::spellability::SpellAbility;
9use crate::trigger::TriggerType;
10
11#[manabrew_engine_macros::spell_effect(CountersRemoveEffect)]
39fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
40 let counter_type_str = sa.ir.counter_type_text.as_deref().unwrap_or("P1P1");
42 if counter_type_str.eq_ignore_ascii_case("Any") || counter_type_str.eq_ignore_ascii_case("All")
43 {
44 return;
46 }
47 let counter_type = parse_counter_type(counter_type_str);
48
49 let remove_all = sa
51 .ir
52 .counter_num_text
53 .as_deref()
54 .is_some_and(|s| s.eq_ignore_ascii_case("All"));
55 let requested_count = if remove_all {
56 i32::MAX } else {
58 sa.ir
59 .counter_num_text
60 .as_deref()
61 .and_then(|s| s.parse::<i32>().ok())
62 .unwrap_or(1)
63 };
64
65 let target_card = resolve_target_card(ctx, sa);
67 let Some(card_id) = target_card else {
68 return;
69 };
70
71 if ctx.game.card(card_id).zone != ZoneType::Battlefield {
73 return;
74 }
75
76 let mut event = ReplacementEvent::RemoveCounter {
78 target: card_id,
79 counter_type: counter_type.clone(),
80 count: requested_count,
81 };
82 let result = apply_replacements(ctx.game, &mut event);
83 if result == ReplacementResult::Skipped || result == ReplacementResult::Replaced {
84 return;
85 }
86 let requested_count = if let ReplacementEvent::RemoveCounter { count, .. } = event {
87 count
88 } else {
89 requested_count
90 };
91
92 let current = ctx.game.card(card_id).counter_count(&counter_type);
94 let actual = requested_count.min(current);
95 if actual <= 0 {
96 return;
97 }
98
99 ctx.game
101 .card_mut(card_id)
102 .remove_counter(&counter_type, actual);
103
104 ctx.trigger_handler.run_trigger(
106 TriggerType::CounterRemoved,
107 RunParams {
108 card: Some(card_id),
109 counter_type: Some(format!("{:?}", counter_type)),
110 counter_amount: Some(actual),
111 ..Default::default()
112 },
113 false,
114 );
115}
116
117fn resolve_target_card(ctx: &EffectContext, sa: &SpellAbility) -> Option<CardId> {
121 if let Some(card_id) = sa.target_chosen.target_card {
123 return Some(card_id);
124 }
125
126 let defined = sa.defined().unwrap_or("Self");
127
128 match defined {
129 "Self" => sa.source,
130 "ParentTarget" => ctx.parent_target_card,
131 "Targeted" => sa.target_chosen.target_card,
132 _ => sa.source, }
134}