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