manabrew_engine/ability/effects/
effect_context.rs1use std::collections::HashMap;
10
11use forge_foundation::ZoneType;
12
13use crate::agent::GameEntity;
14use crate::agent::PlayerAgent;
15use crate::card::{Card, CounterType};
16use crate::event::RunParams;
17use crate::game::GameState;
18use crate::game_entity_counter_table::GameEntityCounterTable;
19use crate::ids::{CardId, PlayerId};
20use crate::mana::ManaPool;
21use crate::spellability::SpellAbility;
22use crate::trigger::handler::TriggerHandler;
23
24pub struct EffectContext<'a> {
26 pub game: &'a mut GameState,
27 pub combat: Option<&'a mut crate::combat::CombatState>,
28 pub agents: &'a mut [Box<dyn PlayerAgent>],
29 pub trigger_handler: &'a mut TriggerHandler,
30 pub token_templates: &'a HashMap<String, Card>,
31 pub token_art_variants: &'a HashMap<(String, String), usize>,
33 pub token_fallback: &'a HashMap<String, String>,
35 pub edition_dates: &'a HashMap<String, String>,
38 pub mana_pools: &'a mut Vec<ManaPool>,
39 pub parent_target_card: Option<CardId>,
43 pub rng: &'a mut dyn crate::game_rng::GameRng,
47}
48
49pub(crate) fn add_counter_with_context(
50 game: &mut GameState,
51 trigger_handler: Option<&mut TriggerHandler>,
52 agents: Option<&mut [Box<dyn PlayerAgent>]>,
53 card_id: CardId,
54 counter_type: &CounterType,
55 amount: i32,
56 params: RunParams,
57 is_effect: bool,
58) -> i32 {
59 let source = params.source_player.or(params.cause_player);
60 let cause = params.cause.clone();
61 let mut table = GameEntityCounterTable::default();
62 table.put(
63 source,
64 GameEntity::Card(card_id),
65 counter_type.clone(),
66 amount,
67 );
68 table
69 .replace_counter_effect(
70 game,
71 trigger_handler,
72 agents,
73 cause.as_ref(),
74 is_effect,
75 params,
76 )
77 .get(source, GameEntity::Card(card_id), counter_type)
78}
79
80impl EffectContext<'_> {
81 pub fn token_art_variant_count(&self, token_script: &str, edition_code: &str) -> usize {
86 let script_lower = token_script.to_lowercase();
87 if !edition_code.is_empty() {
88 let key = (script_lower.clone(), edition_code.to_uppercase());
89 if let Some(&count) = self.token_art_variants.get(&key) {
90 return count;
91 }
92 if let Some(fallback) = self.token_fallback.get(&edition_code.to_uppercase()) {
93 return self.token_art_variant_count(token_script, fallback);
94 }
95 }
96 1
102 }
103
104 pub fn sync_token_art_rng(&mut self, token_script: &str, sa: &SpellAbility) {
108 let host_edition = sa
112 .source
113 .and_then(|cid| self.game.card(cid).set_code.as_deref())
114 .unwrap_or("");
115 let art_count = self.token_art_variant_count(token_script, host_edition);
116 for _ in 0..art_count {
120 self.rng.next_int(1);
121 }
122 self.rng.next_int(1);
124 }
125
126 pub fn move_card(&mut self, card_id: CardId, dest_zone: ZoneType, dest_owner: PlayerId) {
127 let mut runtime = crate::replacement::replacement_handler::ReplacementRuntime {
128 trigger_handler: self.trigger_handler,
129 token_templates: self.token_templates,
130 token_art_variants: self.token_art_variants,
131 token_fallback: self.token_fallback,
132 edition_dates: self.edition_dates,
133 mana_pools: self.mana_pools,
134 rng: self.rng,
135 };
136 self.game.move_card_with_agents_and_replacement_runtime(
137 card_id,
138 dest_zone,
139 dest_owner,
140 self.agents,
141 &mut runtime,
142 );
143 }
144
145 pub(crate) fn add_counter(
146 &mut self,
147 card_id: CardId,
148 counter_type: &CounterType,
149 amount: i32,
150 sa: &SpellAbility,
151 mut params: RunParams,
152 ) -> i32 {
153 params.source_player.get_or_insert(sa.activating_player);
154 params.cause.get_or_insert_with(|| sa.clone());
155 add_counter_with_context(
156 self.game,
157 Some(self.trigger_handler),
158 Some(self.agents),
159 card_id,
160 counter_type,
161 amount,
162 params,
163 true,
164 )
165 }
166
167 pub(crate) fn add_player_counter(
168 &mut self,
169 player: PlayerId,
170 counter_type: &CounterType,
171 amount: i32,
172 sa: &SpellAbility,
173 mut params: RunParams,
174 ) -> i32 {
175 params.source_player.get_or_insert(sa.activating_player);
176 params.cause.get_or_insert_with(|| sa.clone());
177 let source = params.source_player;
178 let mut table = GameEntityCounterTable::default();
179 table.put(
180 source,
181 GameEntity::Player(player),
182 counter_type.clone(),
183 amount,
184 );
185 table
186 .replace_counter_effect(
187 self.game,
188 Some(self.trigger_handler),
189 Some(self.agents),
190 Some(sa),
191 true,
192 params,
193 )
194 .get(source, GameEntity::Player(player), counter_type)
195 }
196}