manabrew_engine/ability/effects/
choose_generic_effect.rs1use super::EffectContext;
4use crate::ability::spell_ability_effect::get_defined_players_or_targeted;
5use crate::spellability::{build_spell_ability, SpellAbility};
6
7#[manabrew_engine_macros::spell_effect(ChooseGenericEffect)]
11fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
12 let source_id = match sa.source {
13 Some(id) => id,
14 None => return,
15 };
16
17 let player = sa.activating_player;
18
19 let choices_str = sa.ir.choices.clone().unwrap_or_default();
22 if choices_str.is_empty() {
23 return;
24 }
25 let choice_svars: Vec<&str> = choices_str.split(',').map(|s| s.trim()).collect();
26
27 let svars = ctx.game.card(source_id).svars.clone();
29 let choice_texts: Vec<String> = choice_svars
30 .iter()
31 .filter_map(|svar| svars.get(*svar).cloned())
32 .collect();
33
34 if choice_texts.is_empty() {
35 return;
36 }
37
38 let amount = sa
39 .ir
40 .amount
41 .as_deref()
42 .and_then(|s| s.parse::<usize>().ok())
43 .unwrap_or(1);
44 let temp_remember = sa.ir.temp_remember;
45 let choosers = get_defined_players_or_targeted(ctx.game, sa);
46 for chooser in choosers {
47 let mut abilities: Vec<SpellAbility> = choice_texts
48 .iter()
49 .map(|text| {
50 let mut choice_sa = build_spell_ability(ctx.game, source_id, text, player);
51 choice_sa.source = Some(source_id);
52 choice_sa.trigger_remembered_amount = sa.trigger_remembered_amount;
53 choice_sa
54 })
55 .collect();
56
57 if let Some(n_str) = sa.ir.num_random_choices.as_deref() {
58 let n = crate::ability::ability_utils::calculate_amount(n_str) as usize;
59 while abilities.len() > n {
60 let idx = ctx.rng.next_int(abilities.len() as i32) as usize;
61 abilities.remove(idx);
62 }
63 }
64
65 abilities.retain(|choice_sa| {
66 if let Some(unless_cost_str) = choice_sa.ir.unless_cost.as_deref() {
67 let cost = crate::cost::parse_cost(unless_cost_str);
68 crate::cost::can_pay_with_ability(
69 &cost,
70 ctx.game,
71 &ctx.mana_pools[chooser.index()],
72 source_id,
73 chooser,
74 Some(choice_sa),
75 )
76 } else {
77 true
78 }
79 });
80
81 let mut chosen_sas: Vec<SpellAbility>;
82
83 if sa.ir.at_random {
84 chosen_sas = Vec::new();
85 for _ in 0..amount.min(abilities.len()) {
86 if abilities.is_empty() {
87 break;
88 }
89 let idx = ctx.rng.next_int(abilities.len() as i32) as usize;
90 chosen_sas.push(abilities.remove(idx));
91 }
92
93 if sa.ir.at_random_text.as_deref() == Some("Urza") {
94 let mut i = 0;
95 while i < chosen_sas.len() {
96 if !chosen_sas[i].uses_targeting() {
97 i += 1;
98 } else if chosen_sas[i]
99 .target_restrictions
100 .as_ref()
101 .map(|tr| tr.has_candidates(ctx.game, chooser, Some(source_id)))
102 .unwrap_or(false)
103 {
104 ctx.agents[chooser.index()].choose_targets_for(
105 &mut chosen_sas[i],
106 ctx.game,
107 ctx.mana_pools,
108 );
109 i += 1;
110 } else if !abilities.is_empty() {
111 let idx = ctx.rng.next_int(abilities.len() as i32) as usize;
112 chosen_sas[i] = abilities.remove(idx);
113 } else {
114 i += 1;
115 }
116 }
117 }
118 } else if !abilities.is_empty() {
119 ctx.agents[chooser.index()].snapshot_state(ctx.game, ctx.mana_pools);
120 let chosen_indices = ctx.agents[chooser.index()]
121 .choose_spell_abilities_for_effect(chooser, &abilities, amount);
122
123 chosen_sas = chosen_indices
124 .into_iter()
125 .filter_map(|i| abilities.get(i).cloned())
126 .collect();
127 } else {
128 chosen_sas = Vec::new();
129 }
130
131 let prior_remembered_players = if temp_remember {
137 let prior = ctx.game.card(source_id).remembered_players.clone();
138 let card = ctx.game.card_mut(source_id);
139 card.remembered_players.clear();
140 card.add_remembered_player(chooser);
141 Some(prior)
142 } else {
143 None
144 };
145
146 if !chosen_sas.is_empty() {
147 for chosen_sa in chosen_sas {
148 super::resolve_effect_chain_with_parent(
149 ctx,
150 chosen_sa,
151 sa.target_chosen.target_card,
152 sa.target_chosen.target_player,
153 );
154 if ctx.game.game_over {
155 break;
156 }
157 }
158 } else if let Some(fallback_name) = sa.ir.fallback_ability.as_deref() {
159 if let Some(fallback_text) = svars.get(fallback_name) {
160 let mut fallback_sa =
161 build_spell_ability(ctx.game, source_id, fallback_text, player);
162 fallback_sa.source = Some(source_id);
163 super::resolve_effect(ctx, &fallback_sa);
164 }
165 }
166
167 if let Some(prior) = prior_remembered_players {
168 let card = ctx.game.card_mut(source_id);
169 card.remembered_players = prior;
170 }
171
172 if ctx.game.game_over {
173 break;
174 }
175 }
176}