manabrew_engine/ability/effects/
phases_effect.rs1use forge_foundation::ZoneType;
2
3use super::EffectContext;
4use crate::ability::ability_ir::DefinedRef;
5use crate::event::{AbilityValue, RunParams};
6use crate::trigger::TriggerType;
7
8#[manabrew_engine_macros::spell_effect(PhasesEffect)]
23fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
24 let phase_mode = sa.ir.phase_in_or_out_text.as_deref().unwrap_or("Out");
25
26 match sa.defined_ref() {
30 Some(
31 DefinedRef::DelayTriggerRememberedLki
32 | DefinedRef::DelayTriggerRemembered
33 | DefinedRef::Remembered,
34 ) => {
35 let ids: Vec<crate::ids::CardId> = sa
36 .trigger_remembered
37 .iter()
38 .filter_map(|v| match v {
39 AbilityValue::Card(cid) => Some(*cid),
40 _ => None,
41 })
42 .collect();
43 for cid in ids {
44 if ctx.game.card(cid).zone == ZoneType::Battlefield {
45 apply_phase(ctx, cid, phase_mode);
46 }
47 }
48 return;
49 }
50 _ => {}
51 }
52
53 if let Some(target_card) = sa.target_chosen.target_card {
55 if ctx.game.card(target_card).zone == ZoneType::Battlefield {
56 apply_phase(ctx, target_card, phase_mode);
57 }
58 return;
59 }
60
61 if let Some(source) = sa.source {
63 if ctx.game.card(source).zone == ZoneType::Battlefield {
64 apply_phase(ctx, source, phase_mode);
65 }
66 }
67}
68
69fn apply_phase(ctx: &mut EffectContext, card_id: crate::ids::CardId, mode: &str) {
70 match mode {
71 "In" => {
72 if ctx.game.card(card_id).phased_out {
73 ctx.game.card_mut(card_id).set_phased_out(false);
74 ctx.trigger_handler.run_trigger(
75 TriggerType::PhasedIn,
76 RunParams {
77 card: Some(card_id),
78 ..Default::default()
79 },
80 false,
81 );
82 }
83 }
84 _ => {
85 if !ctx.game.card(card_id).phased_out {
86 ctx.game.card_mut(card_id).set_phased_out(true);
87 ctx.trigger_handler.run_trigger(
88 TriggerType::PhasedOut,
89 RunParams {
90 card: Some(card_id),
91 ..Default::default()
92 },
93 false,
94 );
95 }
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use crate::ability::spell_ability_effect::SpellAbilityEffect;
103 use forge_foundation::{CardTypeLine, ColorSet, ManaCost, ZoneType};
104 use std::collections::HashMap;
105
106 use crate::ability::effects::EffectContext;
107 use crate::agent::PassAgent;
108 use crate::card::Card;
109 use crate::game::GameState;
110 use crate::ids::{CardId, PlayerId};
111 use crate::mana::ManaPool;
112 use crate::spellability::SpellAbility;
113 use crate::trigger::handler::TriggerHandler;
114
115 fn make_creature(game: &mut GameState, owner: PlayerId) -> CardId {
116 let c = Card::new(
117 CardId(0),
118 "Bear".into(),
119 owner,
120 CardTypeLine::parse("Creature - Bear"),
121 ManaCost::parse("1 G"),
122 ColorSet::GREEN,
123 Some(2),
124 Some(2),
125 vec![],
126 vec![],
127 );
128 game.create_card(c)
129 }
130
131 #[test]
132 fn phase_out_target() {
133 let mut game = GameState::new(&["Alice", "Bob"], 20);
134 let p0 = PlayerId(0);
135 let c1 = make_creature(&mut game, p0);
136 game.move_card(c1, ZoneType::Battlefield, p0);
137 assert!(!game.card(c1).phased_out);
138
139 let mut sa = SpellAbility::new_simple(None, p0, "SP$ Phases | PhaseInOrOut$ Out");
140 sa.target_chosen.target_card = Some(c1);
141
142 let mut th = TriggerHandler::new();
143 let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
144 vec![Box::new(PassAgent), Box::new(PassAgent)];
145 let mut mp = vec![ManaPool::default(), ManaPool::default()];
146 let templates = HashMap::new();
147 let templates_variants = HashMap::new();
148 let token_fallback = HashMap::new();
149 let edition_dates: HashMap<String, String> = HashMap::new();
150 let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
151 let mut ctx = EffectContext {
152 game: &mut game,
153 combat: None,
154 agents: &mut agents,
155 trigger_handler: &mut th,
156 token_templates: &templates,
157 token_art_variants: &templates_variants,
158 token_fallback: &token_fallback,
159 edition_dates: &edition_dates,
160 mana_pools: &mut mp,
161 parent_target_card: None,
162 rng: &mut rng_adapter,
163 };
164 super::PhasesEffect::resolve(&mut ctx, &sa);
165
166 assert!(ctx.game.card(c1).phased_out);
167 }
168
169 #[test]
170 fn phase_in_target() {
171 let mut game = GameState::new(&["Alice", "Bob"], 20);
172 let p0 = PlayerId(0);
173 let c1 = make_creature(&mut game, p0);
174 game.move_card(c1, ZoneType::Battlefield, p0);
175 game.card_mut(c1).set_phased_out(true);
176
177 let mut sa = SpellAbility::new_simple(None, p0, "SP$ Phases | PhaseInOrOut$ In");
178 sa.target_chosen.target_card = Some(c1);
179
180 let mut th = TriggerHandler::new();
181 let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
182 vec![Box::new(PassAgent), Box::new(PassAgent)];
183 let mut mp = vec![ManaPool::default(), ManaPool::default()];
184 let templates = HashMap::new();
185 let templates_variants = HashMap::new();
186 let token_fallback = HashMap::new();
187 let edition_dates: HashMap<String, String> = HashMap::new();
188 let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
189 let mut ctx = EffectContext {
190 game: &mut game,
191 combat: None,
192 agents: &mut agents,
193 trigger_handler: &mut th,
194 token_templates: &templates,
195 token_art_variants: &templates_variants,
196 token_fallback: &token_fallback,
197 edition_dates: &edition_dates,
198 mana_pools: &mut mp,
199 parent_target_card: None,
200 rng: &mut rng_adapter,
201 };
202 super::PhasesEffect::resolve(&mut ctx, &sa);
203
204 assert!(!ctx.game.card(c1).phased_out);
205 }
206}