Skip to main content

manabrew_engine/ability/effects/
counters_move_effect.rs

1//! CountersMove effect — move counters between permanents.
2//!
3//! Ported from Java's `CountersMoveEffect.java`.
4//! Move N counters of a type from one permanent to another.
5
6use super::{parse_counter_type, EffectContext};
7use forge_foundation::ZoneType;
8
9/// Struct form of this effect so it can participate in the
10/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
11/// `CountersMoveEffect` class extending `SpellAbilityEffect`.
12#[manabrew_engine_macros::spell_effect(CountersMoveEffect)]
13fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
14    let counter_type_str = sa
15        .ir
16        .counter_type
17        .as_ref()
18        .map(|s| s.to_string())
19        .unwrap_or_else(|| "P1P1".to_string());
20    let counter_type = parse_counter_type(&counter_type_str);
21    let amount = super::resolve_numeric_svar(ctx.game, sa, "CounterNum", 1).max(0);
22
23    // Source: card to remove counters from
24    let source_card = sa
25        .source
26        .filter(|&cid| ctx.game.card(cid).zone == ZoneType::Battlefield);
27    // Target: card to add counters to
28    let target_card = sa
29        .target_chosen
30        .target_card
31        .filter(|&cid| ctx.game.card(cid).zone == ZoneType::Battlefield);
32
33    let (Some(from), Some(to)) = (source_card, target_card) else {
34        return;
35    };
36    if from == to {
37        return;
38    }
39
40    // Remove counters from source
41    let current = *ctx
42        .game
43        .card(from)
44        .counters
45        .get(&counter_type)
46        .unwrap_or(&0);
47    let to_move = amount.min(current);
48    if to_move <= 0 {
49        return;
50    }
51    if ctx.game.card(to).phased_out
52        || crate::staticability::static_ability_cant_put_counter::any_cant_put_counter_on_card(
53            &ctx.game.cards,
54            ctx.game.card(to),
55            &counter_type,
56        )
57    {
58        return;
59    }
60
61    ctx.game
62        .card_mut(from)
63        .remove_counter(&counter_type, to_move);
64    ctx.add_counter(
65        to,
66        &counter_type,
67        to_move,
68        sa,
69        crate::event::RunParams::default(),
70    );
71}