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::agent::GameEntity;
5use crate::card::CounterType;
6use crate::game_entity_counter_table::GameEntityCounterTable;
7use crate::ids::CardId;
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    let placer = sa.activating_player;
50    let mut table = GameEntityCounterTable::default();
51    for card_id in targets {
52        if ctx.game.card(card_id).zone == zone {
53            if crate::staticability::static_ability_cant_put_counter::any_cant_put_counter_on_card(
54                &ctx.game.cards,
55                ctx.game.card(card_id),
56                &counter_type,
57            ) {
58                continue;
59            }
60            table.put(
61                Some(placer),
62                GameEntity::Card(card_id),
63                counter_type.clone(),
64                count,
65            );
66        }
67    }
68    table.replace_counter_effect(
69        ctx.game,
70        Some(ctx.trigger_handler),
71        Some(ctx.agents),
72        Some(sa),
73        true,
74        Default::default(),
75    );
76}