manabrew_engine/ability/effects/
set_state_effect.rs1use super::EffectContext;
2use crate::ids::CardId;
3use crate::parsing::keys;
4use crate::replacement::replacement_handler::{apply_replacements, ReplacementEvent};
5use crate::replacement::ReplacementResult;
6use crate::spellability::SpellAbilityMode;
7
8#[manabrew_engine_macros::spell_effect(SetStateEffect)]
20fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
21 let mode = sa.ir.mode.as_ref();
22
23 let source_id = match sa.source {
24 Some(id) => id,
25 None => return,
26 };
27
28 match mode {
29 Some(SpellAbilityMode::Transform) => {
30 if let Some(cond_defined) = sa.ir.condition_defined.as_ref() {
32 if cond_defined.refs.first().is_some_and(|defined| {
33 matches!(defined, crate::ability::ability_ir::DefinedRef::Remembered)
34 }) {
35 let cond_present = sa.ir.condition_present.clone().unwrap_or_default();
36 let cond_compare = sa.ir.condition_compare.clone().unwrap_or_default();
37
38 let remembered: Vec<CardId> = ctx.game.card(source_id).remembered_cards.clone();
39 let match_count = remembered
40 .iter()
41 .filter(|&&cid| matches_type_filter(ctx, cid, &cond_present))
42 .count();
43
44 if !evaluate_compare(&cond_compare, match_count) {
45 return; }
47 }
48 }
49
50 let mut transform_event = ReplacementEvent::Transform { card: source_id };
52 let transform_result = apply_replacements(ctx.game, &mut transform_event);
53 if transform_result == ReplacementResult::Skipped
54 || transform_result == ReplacementResult::Replaced
55 {
56 return;
57 }
58
59 ctx.game.card_mut(source_id).transform();
61
62 ctx.trigger_handler.run_trigger(
64 crate::trigger::TriggerType::Transformed,
65 crate::event::RunParams {
66 card: Some(source_id),
67 ..Default::default()
68 },
69 false,
70 );
71
72 ctx.trigger_handler.reset_active_triggers(ctx.game);
74 }
75 Some(SpellAbilityMode::Flip) => {
76 let card = ctx.game.card_mut(source_id);
78 card.set_flipped(!card.flipped);
79 }
80 Some(SpellAbilityMode::TurnFaceUp) => {
81 let mut faceup_event = ReplacementEvent::TurnFaceUp { card: source_id };
83 let faceup_result = apply_replacements(ctx.game, &mut faceup_event);
84 if faceup_result == ReplacementResult::Skipped
85 || faceup_result == ReplacementResult::Replaced
86 {
87 return;
88 }
89
90 let card = ctx.game.card_mut(source_id);
91 if card.face_down {
92 card.set_face_down(false);
93 card.set_static_set_pt(None, None);
95
96 card.activated_abilities
98 .retain(|ab| !ab.ability_text.contains("Mode$ TurnFaceUp"));
99
100 if sa.param_is_true(keys::MEGA) {
102 ctx.add_counter(
103 source_id,
104 &crate::card::CounterType::P1P1,
105 1,
106 sa,
107 crate::event::RunParams::default(),
108 );
109 }
110
111 ctx.trigger_handler.run_trigger(
113 crate::trigger::TriggerType::TurnFaceUp,
114 crate::event::RunParams {
115 card: Some(source_id),
116 ..Default::default()
117 },
118 false,
119 );
120
121 ctx.trigger_handler.reset_active_triggers(ctx.game);
123 }
124 }
125 Some(SpellAbilityMode::TurnFaceDown) => {
126 let card = ctx.game.card_mut(source_id);
127 if !card.face_down {
128 card.set_face_down(true);
129 }
130 }
131 _ => {
132 let err = crate::ability::IllegalAbilityException::new(format!(
133 "Unknown SetState mode: {:?}",
134 mode.map(ToString::to_string)
135 ));
136 eprintln!("{err}");
137 }
138 }
139}
140
141fn matches_type_filter(ctx: &EffectContext, card_id: CardId, filter: &str) -> bool {
144 if filter.is_empty() {
145 return true;
146 }
147 for part in filter.split(',') {
148 let type_name = part.trim().strip_prefix("Card.").unwrap_or(part.trim());
149 let card = ctx.game.card(card_id);
150 if card
151 .type_line
152 .core_types
153 .iter()
154 .any(|t| t.name().eq_ignore_ascii_case(type_name))
155 {
156 return true;
157 }
158 }
159 false
160}
161
162fn evaluate_compare(compare: &str, count: usize) -> bool {
164 if compare.len() < 3 {
165 return true;
166 }
167 let op = &compare[..2];
168 let num: usize = compare[2..].parse().unwrap_or(0);
169 match op {
170 "EQ" => count == num,
171 "GT" => count > num,
172 "GE" => count >= num,
173 "LT" => count < num,
174 "LE" => count <= num,
175 "NE" => count != num,
176 _ => true,
177 }
178}