manabrew_engine/ability/effects/
cast_from_effect.rs1use forge_foundation::ZoneType;
12
13use super::EffectContext;
14use crate::agent::GameLogEvent;
15use crate::event::RunParams;
16use crate::ids::{CardId, PlayerId};
17use crate::spellability::{build_spell_ability, SpellAbility, StackEntry};
18use crate::trigger::TriggerType;
19
20pub fn cast_card_from_effect(
34 ctx: &mut EffectContext,
35 card_id: CardId,
36 controller: PlayerId,
37 without_mana_cost: bool,
38 label: &str,
39) -> bool {
40 let abilities = ctx.game.card(card_id).abilities.clone();
42 let ability_text = match abilities.first() {
43 Some(text) => text.clone(),
44 None => return false,
45 };
46
47 let mut spell_sa = build_spell_ability(ctx.game, card_id, &ability_text, controller);
48 spell_sa.is_spell = true;
49
50 spell_sa.ir.cast_from_play_effect = true;
55
56 if without_mana_cost {
59 if let Some(ref mut cost) = spell_sa.pay_costs {
60 cost.parts
61 .retain(|part| !matches!(part, crate::cost::CostPart::Mana { .. }));
62 }
63 spell_sa.ir.without_mana_cost = true;
64 }
65
66 if let Some(ref mut cost) = spell_sa.pay_costs {
68 cost.mandatory = true;
69 }
70
71 spell_sa.setup_targets(ctx.game, ctx.agents, ctx.mana_pools);
73
74 push_spell_to_stack(ctx, card_id, spell_sa, label);
76 true
77}
78
79pub fn get_basic_spells(ctx: &EffectContext, card_id: CardId) -> Vec<String> {
84 let card = ctx.game.card(card_id);
85 let mut spells = Vec::new();
86
87 for ability_text in &card.abilities {
88 if ability_text.contains("AB$") || ability_text.contains("T$") {
90 continue;
91 }
92 if ability_text.contains("LandAbility") {
94 continue;
95 }
96 spells.push(ability_text.clone());
97 }
98
99 if spells.is_empty() && card.is_permanent() {
101 if let Some(first) = card.abilities.first() {
102 spells.push(first.clone());
103 }
104 }
105
106 spells
107}
108
109pub fn offer_cast_or_alternative(
113 ctx: &mut EffectContext,
114 card_id: CardId,
115 controller: PlayerId,
116 cast_label: &str,
117 alt_label: &str,
118) -> bool {
119 let card_name = ctx.game.card(card_id).card_name.clone();
120 ctx.agents[controller.index()].snapshot_state(ctx.game, ctx.mana_pools);
121 ctx.agents[controller.index()].confirm_action(
122 controller,
123 Some("CastFromEffect"),
124 &format!("{}: {} or {}?", card_name, cast_label, alt_label),
125 &[cast_label.to_string(), alt_label.to_string()],
126 Some(card_id),
127 None,
128 )
129}
130
131fn push_spell_to_stack(
136 ctx: &mut EffectContext,
137 card_id: CardId,
138 spell_sa: SpellAbility,
139 label: &str,
140) {
141 let controller = spell_sa.activating_player;
142 let is_creature = ctx.game.card(card_id).is_creature();
143 let is_permanent = ctx.game.card(card_id).is_permanent();
144 let cast_zone = Some(ctx.game.card(card_id).zone);
145 let card_name = ctx.game.card(card_id).card_name.clone();
146 let chosen_target = spell_sa.target_chosen.target_card;
147
148 let entry = StackEntry {
149 id: 0,
150 spell_ability: spell_sa,
151 is_pending_cast: false,
152 is_creature_spell: is_creature,
153 is_permanent_spell: is_permanent,
154 cast_from_zone: cast_zone,
155 optional_trigger_decider: None,
156 optional_trigger_description: None,
157 optional_trigger_source_name: None,
158 };
159 let trigger_sa = entry.spell_ability.clone();
160
161 ctx.game.stack.push(entry);
162 ctx.move_card(card_id, ZoneType::Stack, controller);
163 ctx.game.player_record_spell_cast(controller, card_id);
164
165 ctx.trigger_handler.run_trigger(
166 TriggerType::SpellCast,
167 RunParams {
168 spell_card: Some(card_id),
169 spell_controller: Some(controller),
170 source_sa: Some(trigger_sa.clone()),
171 ..Default::default()
172 },
173 false,
174 );
175 super::emit_targeting_triggers(ctx, card_id, &trigger_sa);
176
177 let mut event = GameLogEvent::stack(format!("{}: cast {}", label, card_name))
178 .with_player(controller)
179 .with_source_card(card_id);
180 if let Some(target_id) = chosen_target {
181 event = event.with_target_card(target_id);
182 }
183 crate::agent::notify_all_agents(ctx.agents, event);
184}