Skip to main content

manabrew_engine/ability/effects/
counters_put_all_effect.rs

1use forge_foundation::ZoneType;
2
3use super::{matches_valid_cards_for_sa, resolve_numeric_svar, EffectContext};
4use crate::card::CounterType;
5use crate::event::RunParams;
6use crate::ids::CardId;
7use crate::trigger::TriggerType;
8
9/// `SP$ PutCounterAll` — put counters on all matching permanents.
10///
11/// Mirrors Java's `CountersPutAllEffect.java`.
12/// - `CounterType$` — type of counter (default P1P1).
13/// - `CounterNum$` — number of counters to add (default 1).
14/// - `ValidCards$` — filter for which cards receive counters.
15/// - `ValidZone$` — zone to search (default Battlefield).
16///
17/// # Card script examples
18/// ```text
19/// A:SP$ PutCounterAll | CounterType$ P1P1 | CounterNum$ 1 | ValidCards$ Creature.YouCtrl
20/// A:SP$ PutCounterAll | CounterType$ CHARGE | CounterNum$ 2 | ValidCards$ Artifact
21/// ```
22/// Struct form of this effect so it can participate in the
23/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
24/// `CountersPutAllEffect` class extending `SpellAbilityEffect`.
25#[manabrew_engine_macros::spell_effect(CountersPutAllEffect)]
26fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
27    let counter_type = sa.ir.counter_type.clone().unwrap_or(CounterType::P1P1);
28    let count = resolve_numeric_svar(ctx.game, sa, "CounterNum", 1);
29    if count == 0 {
30        return;
31    }
32
33    let valid_cards = sa.ir.valid_cards_selector.as_ref();
34    let zone = sa.ir.valid_zone.unwrap_or(ZoneType::Battlefield);
35
36    let player_ids = ctx.game.player_order.clone();
37    let mut targets: Vec<CardId> = Vec::new();
38
39    for &pid in &player_ids {
40        let zone_cards = ctx.game.cards_in_zone(zone, pid).to_vec();
41        for cid in zone_cards {
42            if matches_valid_cards_for_sa(ctx.game, sa, ctx.game.card(cid), valid_cards, "Creature")
43            {
44                targets.push(cid);
45            }
46        }
47    }
48
49    for card_id in targets {
50        if ctx.game.card(card_id).zone == zone {
51            if crate::staticability::static_ability_cant_put_counter::any_cant_put_counter_on_card(
52                &ctx.game.cards,
53                ctx.game.card(card_id),
54                &counter_type,
55            ) {
56                continue;
57            }
58            let add_count = if let Some(max) =
59                crate::staticability::static_ability_max_counter::max_counter(
60                    &ctx.game.cards,
61                    ctx.game.card(card_id),
62                    &counter_type,
63                ) {
64                (max - ctx.game.card(card_id).counter_count(&counter_type)).clamp(0, count)
65            } else {
66                count
67            };
68            if add_count <= 0 {
69                continue;
70            }
71            ctx.game
72                .card_mut(card_id)
73                .add_counter(&counter_type, add_count);
74            ctx.trigger_handler.run_trigger(
75                TriggerType::CounterAdded,
76                RunParams {
77                    card: Some(card_id),
78                    counter_type: Some(format!("{:?}", counter_type)),
79                    counter_amount: Some(add_count),
80                    ..Default::default()
81                },
82                false,
83            );
84        }
85    }
86}