Skip to main content

manabrew_engine/ability/effects/
control_gain_variant_effect.rs

1use forge_foundation::ZoneType;
2
3use super::{matches_valid_cards_for_sa, EffectContext};
4
5/// `SP$ ControlGainVariant` — complex control redistribution.
6///
7/// Mirrors Java's `ControlGainVariantEffect.java`.
8///
9/// # Params
10/// - `ChangeController` — mode: "CardOwner", "Random", etc.
11/// - `AllValid` — filter for which permanents are affected
12/// Struct form of this effect so it can participate in the
13/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
14/// `ControlGainVariantEffect` class extending `SpellAbilityEffect`.
15#[manabrew_engine_macros::spell_effect(ControlGainVariantEffect)]
16fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
17    let mode = sa
18        .ir
19        .change_type
20        .clone()
21        .unwrap_or_else(|| "CardOwner".to_string());
22
23    let filter = sa
24        .ir
25        .all_valid_text
26        .clone()
27        .unwrap_or_else(|| "Permanent".to_string());
28    let filter_selector = sa.ir.all_valid_selector.clone();
29
30    // Collect all matching permanents on the battlefield
31    let matching: Vec<crate::ids::CardId> = {
32        let mut result = Vec::new();
33        for &pid in &ctx.game.player_order.clone() {
34            let zone_cards = ctx.game.cards_in_zone(ZoneType::Battlefield, pid).to_vec();
35            for cid in zone_cards {
36                if matches_valid_cards_for_sa(
37                    ctx.game,
38                    sa,
39                    ctx.game.card(cid),
40                    filter_selector.as_ref(),
41                    &filter,
42                ) {
43                    result.push(cid);
44                }
45            }
46        }
47        result
48    };
49
50    match mode.as_str() {
51        "CardOwner" => {
52            // Homeward Path: return each permanent to its owner's control
53            for cid in matching {
54                let owner = ctx.game.card(cid).owner;
55                if ctx.game.card(cid).can_be_controlled_by(owner) {
56                    ctx.game.change_controller(cid, owner);
57                }
58            }
59        }
60        "Random" => {
61            // Scrambleverse: assign each permanent to a random player
62            let alive = ctx.game.alive_players();
63            if alive.is_empty() {
64                return;
65            }
66            for cid in matching {
67                let random_idx = ctx.rng.next_int(alive.len() as i32) as usize;
68                let new_controller = alive[random_idx];
69                if ctx.game.card(cid).can_be_controlled_by(new_controller) {
70                    ctx.game.change_controller(cid, new_controller);
71                }
72            }
73        }
74        _ => {
75            // Other modes (multiplayer-specific) are logged and skipped
76            eprintln!("[ControlGainVariant] Unimplemented mode: {}", mode);
77        }
78    }
79}