manabrew_engine/ability/effects/
tap_effect.rs1use forge_foundation::ZoneType;
2
3use super::EffectContext;
4use crate::ability::spell_ability_effect::get_target_cards;
5use crate::event::RunParams;
6use crate::ids::CardId;
7use crate::trigger::TriggerType;
8
9#[manabrew_engine_macros::spell_effect(TapEffect)]
24fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
25 let controller = sa.activating_player;
26 let etb = sa.ir.etb;
27 let remember_tapped = sa.ir.remember_tapped;
28 let always_remember = sa.ir.always_remember;
29
30 for target_card in get_target_cards(ctx.game, sa) {
33 tap_card(
34 ctx,
35 target_card,
36 controller,
37 etb,
38 remember_tapped,
39 always_remember,
40 sa.source,
41 );
42 }
43}
44
45fn tap_card(
46 ctx: &mut EffectContext,
47 card_id: CardId,
48 controller: crate::ids::PlayerId,
49 etb: bool,
50 remember_tapped: bool,
51 always_remember: bool,
52 source: Option<CardId>,
53) {
54 if etb {
55 ctx.game.card_mut(card_id).set_tapped(true);
58 return;
59 }
60
61 if ctx.game.card(card_id).zone != ZoneType::Battlefield {
62 return;
63 }
64
65 let was_untapped = !ctx.game.card(card_id).tapped;
66 let tapped = ctx.game.tap(card_id);
67 if tapped {
68 ctx.trigger_handler.run_trigger(
69 TriggerType::Taps,
70 RunParams {
71 card: Some(card_id),
72 player: Some(controller),
73 ..Default::default()
74 },
75 false,
76 );
77 }
78
79 if (remember_tapped && was_untapped) || always_remember {
81 if let Some(src) = source {
82 ctx.game.card_mut(src).add_remembered_card(card_id);
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use crate::ability::spell_ability_effect::SpellAbilityEffect;
90 use forge_foundation::{CardTypeLine, ColorSet, ManaCost, ZoneType};
91 use std::collections::HashMap;
92
93 use crate::ability::effects::EffectContext;
94 use crate::agent::PassAgent;
95 use crate::card::Card;
96 use crate::game::GameState;
97 use crate::ids::{CardId, PlayerId};
98 use crate::mana::ManaPool;
99 use crate::spellability::SpellAbility;
100 use crate::trigger::handler::TriggerHandler;
101
102 fn make_creature(game: &mut GameState, owner: PlayerId) -> CardId {
103 let c = Card::new(
104 CardId(0),
105 "Bear".into(),
106 owner,
107 CardTypeLine::parse("Creature - Bear"),
108 ManaCost::parse("1 G"),
109 ColorSet::GREEN,
110 Some(2),
111 Some(2),
112 vec![],
113 vec![],
114 );
115 game.create_card(c)
116 }
117
118 #[test]
119 fn tap_effect_taps_target() {
120 let mut game = GameState::new(&["Alice", "Bob"], 20);
121 let p0 = PlayerId(0);
122 let c1 = make_creature(&mut game, p0);
123 game.move_card(c1, ZoneType::Battlefield, p0);
124 assert!(!game.card(c1).tapped);
125
126 let mut sa = SpellAbility::new_simple(None, p0, "SP$ Tap | ValidTgts$ Creature");
127 sa.target_chosen.target_card = Some(c1);
128
129 let mut th = TriggerHandler::new();
130 let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
131 vec![Box::new(PassAgent), Box::new(PassAgent)];
132 let mut mp = vec![ManaPool::default(), ManaPool::default()];
133 let templates = HashMap::new();
134 let templates_variants = HashMap::new();
135 let token_fallback = HashMap::new();
136 let edition_dates: HashMap<String, String> = HashMap::new();
137 let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
138 let mut ctx = EffectContext {
139 game: &mut game,
140 combat: None,
141 agents: &mut agents,
142 trigger_handler: &mut th,
143 token_templates: &templates,
144 token_art_variants: &templates_variants,
145 token_fallback: &token_fallback,
146 edition_dates: &edition_dates,
147 mana_pools: &mut mp,
148 parent_target_card: None,
149 rng: &mut rng_adapter,
150 };
151 super::TapEffect::resolve(&mut ctx, &sa);
152
153 assert!(ctx.game.card(c1).tapped);
154 }
155}