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 card.add_counter(&crate::card::CounterType::P1P1, 1);
103 }
104
105 ctx.trigger_handler.run_trigger(
107 crate::trigger::TriggerType::TurnFaceUp,
108 crate::event::RunParams {
109 card: Some(source_id),
110 ..Default::default()
111 },
112 false,
113 );
114
115 ctx.trigger_handler.reset_active_triggers(ctx.game);
117 }
118 }
119 Some(SpellAbilityMode::TurnFaceDown) => {
120 let card = ctx.game.card_mut(source_id);
121 if !card.face_down {
122 card.set_face_down(true);
123 }
124 }
125 _ => {
126 let err = crate::ability::IllegalAbilityException::new(format!(
127 "Unknown SetState mode: {:?}",
128 mode.map(ToString::to_string)
129 ));
130 eprintln!("{}", err);
131 }
132 }
133}
134
135fn matches_type_filter(ctx: &EffectContext, card_id: CardId, filter: &str) -> bool {
138 if filter.is_empty() {
139 return true;
140 }
141 for part in filter.split(',') {
142 let type_name = part.trim().strip_prefix("Card.").unwrap_or(part.trim());
143 let card = ctx.game.card(card_id);
144 if card
145 .type_line
146 .core_types
147 .iter()
148 .any(|t| t.name().eq_ignore_ascii_case(type_name))
149 {
150 return true;
151 }
152 }
153 false
154}
155
156fn evaluate_compare(compare: &str, count: usize) -> bool {
158 if compare.len() < 3 {
159 return true;
160 }
161 let op = &compare[..2];
162 let num: usize = compare[2..].parse().unwrap_or(0);
163 match op {
164 "EQ" => count == num,
165 "GT" => count > num,
166 "GE" => count >= num,
167 "LT" => count < num,
168 "LE" => count <= num,
169 "NE" => count != num,
170 _ => true,
171 }
172}