manabrew_engine/ability/effects/
draw_effect.rs1use super::{resolve_numeric_svar, EffectContext};
2use crate::ability::ability_ir::EffectIr;
3use crate::ability::spell_ability_effect::get_target_players;
4use crate::event::RunParams;
5use crate::parsing::keys;
6use crate::replacement::replacement_handler::{apply_replacements_with_agents, ReplacementEvent};
7use crate::replacement::ReplacementResult;
8use crate::spellability::SpellAbility;
9use crate::trigger::TriggerType;
10
11#[manabrew_engine_macros::spell_effect(DrawEffect)]
15fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
16 let num = resolve_draw_amount(ctx, sa);
17 let targets = get_target_players(ctx.game, sa);
18
19 for target in targets {
20 draw_for_player(ctx, sa, target, num);
21 }
22}
23
24fn draw_for_player(
25 ctx: &mut EffectContext,
26 sa: &crate::spellability::SpellAbility,
27 target: crate::ids::PlayerId,
28 num: i32,
29) {
30 let mut actual_num = num;
31 if actual_num > 0 {
32 let mut event = ReplacementEvent::DrawCards {
33 player: target,
34 count: actual_num,
35 };
36 let result = apply_replacements_with_agents(ctx.game, ctx.agents, &mut event);
37 if result == ReplacementResult::Skipped || result == ReplacementResult::Replaced {
38 return;
39 }
40 if let ReplacementEvent::DrawCards { count, .. } = event {
41 actual_num = count;
42 }
43 }
44
45 let upto = sa.ir.upto;
51 let optional = sa.ir.optional_present || sa.ir.optional_decider.is_some() || upto;
52 if optional {
53 let source_name = sa.source.map(|cid| ctx.game.card(cid).card_name.as_str());
54 let accepted = ctx.agents[target.index()].confirm_action(
55 target,
56 None,
57 &format!("Do you want to draw {} card(s)?", actual_num),
58 &[],
59 sa.source,
60 Some(crate::ability::api_type::ApiType::Draw),
61 );
62 if !accepted {
63 return;
64 }
65 }
66 if upto {
67 let prompt = format!("Draw up to {actual_num} card(s).");
68 match ctx.agents[target.index()].choose_number(
69 target,
70 sa.source,
71 "Draw cards",
72 Some(&prompt),
73 0,
74 actual_num,
75 ) {
76 Some(picked) => {
77 actual_num = picked.clamp(0, actual_num);
78 if actual_num == 0 {
79 return;
80 }
81 }
82 None => return,
83 }
84 }
85 let remember_drawn = sa.ir.remember_drawn;
89 let mut drawn: Vec<crate::ids::CardId> = Vec::new();
90 for _ in 0..actual_num {
91 if let Some(card_id) = ctx.game.draw_card_with_agents(target, ctx.agents) {
92 drawn.push(card_id);
93 let drawn_snapshot = ctx.game.player(target).drawn_this_turn;
96 ctx.trigger_handler.run_trigger(
97 TriggerType::Drawn,
98 RunParams {
99 card: Some(card_id),
100 player: Some(target),
101 drawn_this_turn_snapshot: Some(drawn_snapshot),
102 ..Default::default()
103 },
104 false,
105 );
106 if ctx.trigger_handler.has_number_drawn_triggers(ctx.game) {
111 ctx.trigger_handler.flush_waiting_triggers(ctx.game);
112 }
113 }
114 }
115
116 if remember_drawn {
119 if let Some(source_id) = sa.source {
120 let card_mut = ctx.game.card_mut(source_id);
121 for cid in &drawn {
122 card_mut.add_remembered_card(*cid);
123 }
124 }
125 }
126}
127
128fn resolve_draw_amount(ctx: &EffectContext, sa: &SpellAbility) -> i32 {
129 if let Some(EffectIr::Draw(ir)) = &sa.ir.effect {
130 if let Some(amount) = &ir.amount {
131 let resolved = amount.resolve_for_spell_ability(ctx.game, sa, 1);
132 #[cfg(debug_assertions)]
133 debug_assert_eq!(
134 resolved,
135 resolve_numeric_svar(ctx.game, sa, keys::NUM_CARDS, 1),
136 "compiled Draw amount diverged from string params"
137 );
138 return resolved;
139 }
140 }
141
142 resolve_numeric_svar(ctx.game, sa, keys::NUM_CARDS, 1)
143}