manabrew_engine/ability/effects/
skip_phase_effect.rs1use super::{resolve_defined_players, EffectContext};
2
3pub fn create_skip_phase_effect(
10 game: &mut crate::game::GameState,
11 player: crate::ids::PlayerId,
12 phase: &str,
13) {
14 match phase {
15 "Draw" => game.player_mut(player).skip_next_draw = true,
16 "Combat" | "BeginCombat" => game.player_mut(player).skip_next_combat = true,
17 "Untap" => game.player_mut(player).skip_next_untap = true,
18 _ => {
19 eprintln!("SkipPhaseEffect: Unknown phase to skip: {phase:?}");
20 }
21 }
22}
23
24pub fn run(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
29 use crate::ability::spell_ability_effect::SpellAbilityEffect;
30 SkipPhaseEffect::resolve(ctx, sa);
31}
32
33#[manabrew_engine_macros::spell_effect(SkipPhaseEffect)]
49fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
50 let controller = sa.activating_player;
51
52 let phase = sa
53 .ir
54 .phase_text
55 .as_deref()
56 .or(sa.ir.step_text.as_deref())
57 .unwrap_or("");
58
59 let defined = sa.defined().unwrap_or("You");
60
61 let targets = resolve_defined_players(defined, controller, ctx.game);
62 for target in targets {
63 if !ctx.game.player(target).is_alive() {
64 continue;
65 }
66 match phase {
67 "Draw" => ctx.game.player_set_skip_draw(target),
68 "Combat" | "BeginCombat" => ctx.game.player_set_skip_combat(target),
69 "Untap" => ctx.game.player_set_skip_untap(target),
70 _ => {
71 let err = crate::ability::IllegalAbilityException::new(format!(
72 "Unknown phase to skip: {phase:?}"
73 ));
74 eprintln!("{err}");
75 }
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use std::collections::HashMap;
83
84 use super::SkipPhaseEffect;
85 use crate::ability::effects::EffectContext;
86 use crate::ability::spell_ability_effect::SpellAbilityEffect;
87 use crate::agent::PassAgent;
88 use crate::game::GameState;
89 use crate::ids::PlayerId;
90 use crate::mana::ManaPool;
91 use crate::spellability::SpellAbility;
92 use crate::trigger::handler::TriggerHandler;
93
94 #[test]
95 fn skip_draw_phase() {
96 let mut game = GameState::new(&["Alice", "Bob"], 20);
97 let p0 = PlayerId(0);
98 let p1 = PlayerId(1);
99
100 let sa =
101 SpellAbility::new_simple(None, p0, "SP$ SkipPhase | Defined$ Opponent | Phase$ Draw");
102
103 let mut th = TriggerHandler::new();
104 let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
105 vec![Box::new(PassAgent), Box::new(PassAgent)];
106 let mut mp = vec![ManaPool::default(), ManaPool::default()];
107 let templates = HashMap::new();
108 let templates_variants = HashMap::new();
109 let token_fallback = HashMap::new();
110 let edition_dates: HashMap<String, String> = HashMap::new();
111 let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
112 let mut ctx = EffectContext {
113 game: &mut game,
114 combat: None,
115 agents: &mut agents,
116 trigger_handler: &mut th,
117 token_templates: &templates,
118 token_art_variants: &templates_variants,
119 token_fallback: &token_fallback,
120 edition_dates: &edition_dates,
121 mana_pools: &mut mp,
122 parent_target_card: None,
123 rng: &mut rng_adapter,
124 };
125 super::SkipPhaseEffect::resolve(&mut ctx, &sa);
126
127 assert!(ctx.game.player(p1).skip_next_draw);
128 assert!(!ctx.game.player(p1).skip_next_combat);
129 }
130
131 #[test]
132 fn skip_combat_phase() {
133 let mut game = GameState::new(&["Alice", "Bob"], 20);
134 let p0 = PlayerId(0);
135
136 let sa = SpellAbility::new_simple(None, p0, "SP$ SkipPhase | Defined$ You | Phase$ Combat");
137
138 let mut th = TriggerHandler::new();
139 let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
140 vec![Box::new(PassAgent), Box::new(PassAgent)];
141 let mut mp = vec![ManaPool::default(), ManaPool::default()];
142 let templates = HashMap::new();
143 let templates_variants = HashMap::new();
144 let token_fallback = HashMap::new();
145 let edition_dates: HashMap<String, String> = HashMap::new();
146 let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
147 let mut ctx = EffectContext {
148 game: &mut game,
149 combat: None,
150 agents: &mut agents,
151 trigger_handler: &mut th,
152 token_templates: &templates,
153 token_art_variants: &templates_variants,
154 token_fallback: &token_fallback,
155 edition_dates: &edition_dates,
156 mana_pools: &mut mp,
157 parent_target_card: None,
158 rng: &mut rng_adapter,
159 };
160 super::SkipPhaseEffect::resolve(&mut ctx, &sa);
161
162 assert!(ctx.game.player(p0).skip_next_combat);
163 }
164}