manabrew_engine/ability/effects/
balance_effect.rs1use forge_foundation::ZoneType;
2
3use super::{emit_zone_trigger, matches_valid_cards_for_sa, EffectContext};
4use crate::event::RunParams;
5use crate::trigger::TriggerType;
6
7#[manabrew_engine_macros::spell_effect(BalanceEffect)]
20fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
21 let filter = sa
22 .ir
23 .valid_filter_text
24 .clone()
25 .unwrap_or_else(|| "Card".to_string());
26 let filter_selector = sa.ir.valid_filter_selector.clone();
27
28 let zone = sa.ir.zone.unwrap_or(ZoneType::Battlefield);
29
30 let player_order = ctx.game.player_order.clone();
31
32 let counts: Vec<(crate::ids::PlayerId, usize)> = player_order
34 .iter()
35 .map(|&pid| {
36 let count = ctx
37 .game
38 .cards_in_zone(zone, pid)
39 .to_vec()
40 .iter()
41 .filter(|&&cid| {
42 matches_valid_cards_for_sa(
43 ctx.game,
44 sa,
45 ctx.game.card(cid),
46 filter_selector.as_ref(),
47 &filter,
48 )
49 })
50 .count();
51 (pid, count)
52 })
53 .collect();
54
55 let min_count = counts.iter().map(|(_, c)| *c).min().unwrap_or(0);
56
57 for &(pid, count) in &counts {
59 if count <= min_count {
60 continue;
61 }
62 let excess = count - min_count;
63
64 match zone {
65 ZoneType::Battlefield => {
66 for _ in 0..excess {
68 let valid: Vec<_> = ctx
69 .game
70 .cards_in_zone(ZoneType::Battlefield, pid)
71 .to_vec()
72 .into_iter()
73 .filter(|&cid| {
74 matches_valid_cards_for_sa(
75 ctx.game,
76 sa,
77 ctx.game.card(cid),
78 filter_selector.as_ref(),
79 &filter,
80 )
81 })
82 .collect();
83
84 if valid.is_empty() {
85 break;
86 }
87
88 ctx.agents[pid.index()].snapshot_state(ctx.game, ctx.mana_pools);
89 if let Some(card_id) =
90 ctx.agents[pid.index()].choose_sacrifice(pid, &valid, sa.source)
91 {
92 if ctx.game.card(card_id).zone == ZoneType::Battlefield {
93 let owner = ctx.game.card(card_id).owner;
94 ctx.trigger_handler.run_trigger(
95 TriggerType::Sacrificed,
96 RunParams {
97 card: Some(card_id),
98 player: Some(pid),
99 ..Default::default()
100 },
101 false,
102 );
103 ctx.move_card(card_id, ZoneType::Graveyard, owner);
104 emit_zone_trigger(
105 ctx.trigger_handler,
106 card_id,
107 ZoneType::Battlefield,
108 ZoneType::Graveyard,
109 );
110 }
111 }
112 }
113 }
114 ZoneType::Hand => {
115 let hand: Vec<_> = ctx.game.cards_in_zone(ZoneType::Hand, pid).to_vec();
117 ctx.agents[pid.index()].snapshot_state(ctx.game, ctx.mana_pools);
118 let to_discard = ctx.agents[pid.index()].choose_discard(pid, &hand, excess);
119
120 for &card_id in to_discard.iter().take(excess) {
121 if ctx.game.card(card_id).zone == ZoneType::Hand {
122 ctx.game.player_record_discard(pid, 1);
123 ctx.game.card_mut(card_id).set_discarded(true);
124 let owner = ctx.game.card(card_id).owner;
125 ctx.move_card(card_id, ZoneType::Graveyard, owner);
126 emit_zone_trigger(
127 ctx.trigger_handler,
128 card_id,
129 ZoneType::Hand,
130 ZoneType::Graveyard,
131 );
132 ctx.trigger_handler.run_trigger(
133 TriggerType::Discarded,
134 RunParams {
135 card: Some(card_id),
136 player: Some(pid),
137 ..Default::default()
138 },
139 false,
140 );
141 }
142 }
143 }
144 _ => {
145 }
147 }
148 }
149}