Skip to main content

manabrew_engine/ability/effects/
mod.rs

1//! Effect resolution system.
2//!
3//! Each effect type lives in its own file, mirroring the Java Forge
4//! `ability/effects/` package (204 files). Effects are dispatched by
5//! API type string extracted from the ability text.
6
7pub mod abandon_effect;
8pub mod activate_ability_effect;
9pub mod add_phase_effect;
10pub mod add_turn_effect;
11pub mod advance_crank_effect;
12pub mod airbend_effect;
13pub mod alter_attribute_effect;
14pub mod amass_effect;
15pub mod animate_all_effect;
16pub mod animate_effect;
17pub mod ascend_effect;
18pub mod assemble_contraption_effect;
19pub mod assign_group_effect;
20pub mod attach_effect;
21pub mod balance_effect;
22pub mod become_monarch_effect;
23pub mod becomes_blocked_effect;
24pub mod bid_life_effect;
25pub mod blank_line_effect;
26pub mod blight_effect;
27pub mod block_effect;
28pub mod bond_effect;
29pub mod branch_effect;
30pub mod camouflage_effect;
31pub mod cast_from_effect;
32pub mod change_combatants_effect;
33pub mod change_speed_effect;
34pub mod change_targets_effect;
35pub mod change_text_effect;
36pub mod change_x_effect;
37pub mod change_zone_all_effect;
38pub mod change_zone_effect;
39pub mod change_zone_resolve_effect;
40pub mod chaos_ensues_effect;
41pub mod charm_effect;
42pub mod choose_card_effect;
43pub mod choose_card_name_effect;
44pub mod choose_color_effect;
45pub mod choose_direction_effect;
46pub mod choose_even_odd_effect;
47pub mod choose_generic_effect;
48pub mod choose_number_effect;
49pub mod choose_player_effect;
50pub mod choose_sector_effect;
51pub mod choose_source_effect;
52pub mod choose_type_effect;
53pub mod claim_the_prize_effect;
54pub mod clash_effect;
55pub mod class_level_up_effect;
56pub mod clean_up_effect;
57pub mod cleanup_effect;
58pub mod cloak_effect;
59pub mod clone_effect;
60pub mod connive_effect;
61pub mod control_exchange_effect;
62pub mod control_exchange_variant_effect;
63pub mod control_gain_effect;
64pub mod control_gain_variant_effect;
65pub mod control_player_effect;
66pub mod control_spell_effect;
67pub mod copy_permanent_effect;
68pub mod copy_spell_ability_effect;
69pub mod counter_effect;
70pub mod counters_move_effect;
71pub mod counters_multiply_effect;
72pub mod counters_note_effect;
73pub mod counters_proliferate_effect;
74pub mod counters_put_all_effect;
75pub mod counters_put_effect;
76pub mod counters_put_or_remove_effect;
77pub mod counters_remove_all_effect;
78pub mod counters_remove_effect;
79pub mod damage_all_effect;
80pub mod damage_base_effect;
81pub mod damage_deal_effect;
82pub mod damage_each_effect;
83pub mod damage_prevent_effect;
84pub mod damage_resolve_effect;
85pub mod day_time_effect;
86pub mod debuff_effect;
87pub mod delayed_trigger_effect;
88pub mod destroy_all_effect;
89pub mod destroy_effect;
90pub mod detached_card_effect;
91pub mod detain_effect;
92pub mod dig_effect;
93pub mod dig_multiple_effect;
94pub mod dig_until_effect;
95pub mod discard_effect;
96pub mod discover_effect;
97pub mod draft_effect;
98pub mod drain_mana_effect;
99pub mod draw_effect;
100pub mod earthbend_effect;
101pub mod effect_effect;
102pub mod encode_effect;
103pub mod end_combat_phase_effect;
104pub mod end_turn_effect;
105pub mod endure_effect;
106pub mod explore_effect;
107pub mod fight_effect;
108pub mod flip_coin_effect;
109pub mod flip_onto_battlefield_effect;
110pub mod fog_effect;
111pub mod game_draw_effect;
112pub mod game_loss_effect;
113pub mod game_win_effect;
114pub mod goad_effect;
115pub mod haunt_effect;
116pub mod heist_effect;
117pub mod immediate_trigger_effect;
118pub mod incubate_effect;
119pub mod intensify_effect;
120pub mod internal_radiation_effect;
121pub mod investigate_effect;
122pub mod learn_effect;
123pub mod life_exchange_effect;
124pub mod life_exchange_variant_effect;
125pub mod life_gain_effect;
126pub mod life_lose_effect;
127pub mod life_set_effect;
128pub mod look_at_effect;
129pub mod lose_perpetual_effect;
130pub mod make_card_effect;
131pub mod mana_effect;
132pub mod mana_reflected_effect;
133pub mod manifest_base_effect;
134pub mod manifest_dread_effect;
135pub mod manifest_effect;
136pub mod meld_effect;
137pub mod mill_effect;
138pub mod move_counter_effect;
139pub mod multiple_piles_effect;
140pub mod must_block_effect;
141pub mod mutate_effect;
142pub mod name_card_effect;
143pub mod open_attraction_effect;
144pub mod ownership_gain_effect;
145pub mod peek_and_reveal_effect;
146pub mod permanent_creature_effect;
147pub mod permanent_effect;
148pub mod permanent_noncreature_effect;
149pub mod phases_effect;
150pub mod planeswalk_effect;
151pub mod play_effect;
152pub mod play_land_variant_effect;
153pub mod plot_effect;
154pub mod poison_effect;
155pub mod power_exchange_effect;
156pub mod prevent_damage_effect;
157pub mod protect_all_effect;
158pub mod protect_effect;
159pub mod pump_all_effect;
160pub mod pump_effect;
161pub mod radiation_effect;
162pub mod rearrange_top_of_library_effect;
163pub mod regenerate_effect;
164pub mod regeneration_effect;
165pub mod remove_from_combat_effect;
166pub mod remove_from_game_effect;
167pub mod remove_from_match_effect;
168pub mod reorder_zone_effect;
169pub mod repeat_each_effect;
170pub mod repeat_effect;
171pub mod replace_counter_effect;
172pub mod replace_damage_effect;
173pub mod replace_effect;
174pub mod replace_mana_effect;
175pub mod replace_split_damage_effect;
176pub mod replace_token_effect;
177pub mod restart_game_effect;
178pub mod reveal_effect;
179pub mod reveal_hand_effect;
180pub mod reverse_turn_order_effect;
181pub mod ring_tempts_you_effect;
182pub mod roll_dice_effect;
183pub mod roll_planar_dice_effect;
184pub mod run_chaos_effect;
185pub mod sacrifice_all_effect;
186pub mod sacrifice_effect;
187pub mod scry_effect;
188pub mod seek_effect;
189pub mod set_in_motion_effect;
190pub mod set_state_effect;
191pub mod shuffle_effect;
192pub mod skip_phase_effect;
193pub mod skip_turn_effect;
194pub mod store_s_var_effect;
195pub mod subgame_effect;
196pub mod surveil_effect;
197pub mod switch_block_effect;
198pub mod take_initiative_effect;
199pub mod tap_all_effect;
200pub mod tap_effect;
201pub mod tap_or_untap_all_effect;
202pub mod tap_or_untap_effect;
203pub mod text_box_exchange_effect;
204pub mod time_travel_effect;
205pub mod token_effect;
206pub mod token_effect_base;
207pub mod trait_animate_effect;
208pub mod two_piles_effect;
209pub mod unattach_effect;
210pub mod unlock_door_effect;
211pub mod untap_all_effect;
212pub mod untap_effect;
213pub mod venture_effect;
214pub mod villainous_choice_effect;
215pub mod vote_effect;
216pub mod zone_exchange_effect;
217
218// ── Infrastructure split out of this file for readability ─────────
219pub mod combat_helpers;
220pub mod condition;
221pub mod cost_payment;
222pub mod effect_context;
223pub mod effect_resolver;
224pub mod helpers;
225pub mod targeting_triggers;
226pub mod zone_triggers;
227
228// ── Re-exports so external callers keep the `effects::X` path ──────
229pub(crate) use combat_helpers::add_to_combat;
230pub(crate) use cost_payment::{try_pay_cumulative_upkeep, try_pay_echo, try_pay_unless_cost};
231pub use effect_context::EffectContext;
232pub(crate) use effect_resolver::{build_spell_ability_for_api, sub_ability_handled_internally};
233pub use effect_resolver::{
234    resolve_effect, resolve_effect_chain, resolve_effect_chain_with_parent, IMPLEMENTED_API_TYPES,
235};
236pub(crate) use targeting_triggers::{emit_targeting_triggers, emit_targeting_triggers_for_sa};
237
238// Re-exports so effect files can use short paths like `super::parse_counter_type()`.
239pub use crate::mana::mana_atom_from_produced;
240pub use crate::svar::{
241    evaluate_svar, resolve_count_svar, resolve_count_svar_for_sa, resolve_numeric_svar,
242    resolve_numeric_value,
243};
244pub use helpers::*;
245pub use zone_triggers::*;
246
247/// Split a scry/surveil agent decision (one ordered pile per zone) into
248/// `(top, other)`, validated against the looked-at cards. Any looked-at card
249/// not assigned anywhere is kept on top, preserving the original order.
250pub(crate) fn split_scry_piles(
251    looked_at: &[crate::ids::CardId],
252    piles: &[Vec<crate::ids::CardId>],
253) -> (Vec<crate::ids::CardId>, Vec<crate::ids::CardId>) {
254    let other: Vec<crate::ids::CardId> = piles
255        .get(1)
256        .map(|v| {
257            v.iter()
258                .copied()
259                .filter(|id| looked_at.contains(id))
260                .collect()
261        })
262        .unwrap_or_default();
263    let mut top: Vec<crate::ids::CardId> = piles
264        .first()
265        .map(|v| {
266            v.iter()
267                .copied()
268                .filter(|id| looked_at.contains(id) && !other.contains(id))
269                .collect()
270        })
271        .unwrap_or_default();
272    for &id in looked_at {
273        if !other.contains(&id) && !top.contains(&id) {
274            top.push(id);
275        }
276    }
277    (top, other)
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283
284    #[test]
285    fn parse_num_dmg_test() {
286        assert_eq!(
287            helpers::parse_num_dmg(
288                "SP$ DealDamage | ValidTgts$ Any | NumDmg$ 3 | SpellDescription$ test"
289            ),
290            3
291        );
292    }
293
294    #[test]
295    fn parse_param_test() {
296        assert_eq!(
297            helpers::parse_param("SP$ Pump | NumAtt$ 3 | NumDef$ 3", "NumAtt$ "),
298            Some(3)
299        );
300        assert_eq!(
301            helpers::parse_param("SP$ Pump | NumAtt$ 3 | NumDef$ 3", "NumDef$ "),
302            Some(3)
303        );
304        assert_eq!(
305            helpers::parse_param("SP$ Draw | NumCards$ 2", "NumCards$ "),
306            Some(2)
307        );
308    }
309}