manabrew_engine/ability/effects/
power_exchange_effect.rs1use forge_foundation::ZoneType;
2
3use super::EffectContext;
4use crate::card::perpetual::perpetual_interface::PerpetualInterface;
5use crate::card::perpetual::perpetual_new_pt;
6
7pub fn run(
13 game: &mut crate::game::GameState,
14 card1: crate::ids::CardId,
15 card2: crate::ids::CardId,
16) {
17 if game.card(card1).zone == ZoneType::Battlefield {
19 game.card_mut(card1).set_power_modifier(0);
20 }
21 if game.card(card2).zone == ZoneType::Battlefield {
22 game.card_mut(card2).set_power_modifier(0);
23 }
24}
25
26#[manabrew_engine_macros::spell_effect(PowerExchangeEffect)]
38fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
39 let source = sa.source;
40 let target = sa.target_chosen.target_card;
41
42 let (c1, c2) = match (source, target) {
43 (Some(s), Some(t)) => (s, t),
44 _ => return,
45 };
46
47 if ctx.game.card(c1).zone != ZoneType::Battlefield
48 || ctx.game.card(c2).zone != ZoneType::Battlefield
49 {
50 return;
51 }
52
53 if !ctx.game.card(c1).is_creature() || !ctx.game.card(c2).is_creature() {
55 return;
56 }
57
58 let base_power = sa.ir.base_power;
59 let p1 = if base_power {
60 ctx.game.card(c1).base_power.unwrap_or(0)
61 } else {
62 ctx.game.card(c1).power()
63 };
64 let p2 = if base_power {
65 ctx.game.card(c2).base_power.unwrap_or(0)
66 } else {
67 ctx.game.card(c2).power()
68 };
69 let is_perpetual = matches!(
70 sa.ir.duration,
71 Some(crate::spellability::AbilityDuration::Perpetual)
72 );
73
74 if is_perpetual {
75 let ts = ctx.game.next_effect_timestamp();
76 perpetual_new_pt::PerpetualNewPt {
77 timestamp: ts,
78 power: Some(p2),
79 toughness: None,
80 }
81 .apply_effect(ctx.game.card_mut(c1));
82 perpetual_new_pt::PerpetualNewPt {
83 timestamp: ts,
84 power: Some(p1),
85 toughness: None,
86 }
87 .apply_effect(ctx.game.card_mut(c2));
88 return;
89 }
90
91 let c1_base = ctx
93 .game
94 .card(c1)
95 .static_set_power
96 .unwrap_or(ctx.game.card(c1).base_power.unwrap_or(0));
97 let c2_base = ctx
98 .game
99 .card(c2)
100 .static_set_power
101 .unwrap_or(ctx.game.card(c2).base_power.unwrap_or(0));
102
103 let c1_static = ctx.game.card(c1).static_power_modifier;
105 let c2_static = ctx.game.card(c2).static_power_modifier;
106 ctx.game
107 .card_mut(c1)
108 .set_power_modifier(p2 - c1_base - c1_static);
109 ctx.game
110 .card_mut(c2)
111 .set_power_modifier(p1 - c2_base - c2_static);
112}
113
114#[cfg(test)]
115mod tests {
116 use crate::ability::spell_ability_effect::SpellAbilityEffect;
117 use forge_foundation::{CardTypeLine, ColorSet, ManaCost, ZoneType};
118 use std::collections::HashMap;
119
120 use crate::ability::effects::EffectContext;
121 use crate::agent::PassAgent;
122 use crate::card::Card;
123 use crate::game::GameState;
124 use crate::ids::{CardId, PlayerId};
125 use crate::mana::ManaPool;
126 use crate::spellability::SpellAbility;
127 use crate::trigger::handler::TriggerHandler;
128
129 #[test]
130 fn power_exchange_swaps() {
131 let mut game = GameState::new(&["Alice", "Bob"], 20);
132 let p0 = PlayerId(0);
133
134 let c1 = game.create_card(Card::new(
135 CardId(0),
136 "Bear".into(),
137 p0,
138 CardTypeLine::parse("Creature - Bear"),
139 ManaCost::parse("1 G"),
140 ColorSet::GREEN,
141 Some(2),
142 Some(2),
143 vec![],
144 vec![],
145 ));
146 game.move_card(c1, ZoneType::Battlefield, p0);
147
148 let c2 = game.create_card(Card::new(
149 CardId(0),
150 "Dragon".into(),
151 p0,
152 CardTypeLine::parse("Creature - Dragon"),
153 ManaCost::parse("4 R R"),
154 ColorSet::RED,
155 Some(5),
156 Some(5),
157 vec![],
158 vec![],
159 ));
160 game.move_card(c2, ZoneType::Battlefield, p0);
161
162 assert_eq!(game.card(c1).power(), 2);
163 assert_eq!(game.card(c2).power(), 5);
164
165 let mut sa = SpellAbility::new_simple(Some(c1), p0, "SP$ PowerExchange");
166 sa.target_chosen.target_card = Some(c2);
167
168 let mut th = TriggerHandler::new();
169 let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
170 vec![Box::new(PassAgent), Box::new(PassAgent)];
171 let mut mp = vec![ManaPool::default(), ManaPool::default()];
172 let templates = HashMap::new();
173 let templates_variants = HashMap::new();
174 let token_fallback = HashMap::new();
175 let edition_dates: HashMap<String, String> = HashMap::new();
176 let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
177 let mut ctx = EffectContext {
178 game: &mut game,
179 combat: None,
180 agents: &mut agents,
181 trigger_handler: &mut th,
182 token_templates: &templates,
183 token_art_variants: &templates_variants,
184 token_fallback: &token_fallback,
185 edition_dates: &edition_dates,
186 mana_pools: &mut mp,
187 parent_target_card: None,
188 rng: &mut rng_adapter,
189 };
190 super::PowerExchangeEffect::resolve(&mut ctx, &sa);
191
192 assert_eq!(ctx.game.card(c1).power(), 5);
193 assert_eq!(ctx.game.card(c2).power(), 2);
194 }
195}