Skip to main content

manabrew_engine/ability/effects/
connive_effect.rs

1use forge_foundation::ZoneType;
2
3use super::{emit_zone_trigger, resolve_numeric_svar, EffectContext};
4use crate::event::RunParams;
5use crate::parsing::keys;
6use crate::trigger::TriggerType;
7
8/// `DB$ Connive` — target creature connives N times.
9///
10/// Connive: draw N cards, then discard N cards. For each nonland card
11/// discarded this way, put a +1/+1 counter on the conniving creature.
12///
13/// Mirrors Java's `ConniveEffect.resolve()`.
14///
15/// # Params
16/// - `ConniveNum` — number of times to connive (default: 1)
17/// - Target or `Defined$` — the creature that connives
18/// Struct form of this effect so it can participate in the
19/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
20/// `ConniveEffect` class extending `SpellAbilityEffect`.
21#[manabrew_engine_macros::spell_effect(ConniveEffect)]
22fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
23    let num = resolve_numeric_svar(ctx.game, sa, keys::CONNIVE_NUM, 1).max(0) as usize;
24
25    // Resolve the conniving creature: source card by default.
26    let conniver_id = if let Some(target) = sa.target_chosen.target_card {
27        target
28    } else {
29        match sa.source {
30            Some(id) => id,
31            None => return,
32        }
33    };
34
35    // The conniver must be on the battlefield.
36    if ctx.game.card(conniver_id).zone != ZoneType::Battlefield {
37        return;
38    }
39
40    let controller = ctx.game.card(conniver_id).controller;
41
42    // Draw N cards.
43    for _ in 0..num {
44        ctx.game.draw_card(controller);
45    }
46
47    // Discard N cards from hand.
48    let hand: Vec<_> = ctx.game.cards_in_zone(ZoneType::Hand, controller).to_vec();
49
50    if hand.is_empty() {
51        return;
52    }
53
54    let amt = hand.len().min(num);
55    let to_discard = ctx.agents[controller.index()].choose_discard(controller, &hand, amt);
56
57    // Count nonland cards discarded (for +1/+1 counters).
58    let mut nonland_count = 0i32;
59    for card_id in &to_discard {
60        if ctx.game.card(*card_id).zone == ZoneType::Hand {
61            if !ctx.game.card(*card_id).is_land() {
62                nonland_count += 1;
63            }
64            ctx.game.player_record_discard(controller, 1);
65            ctx.game.card_mut(*card_id).set_discarded(true);
66            let owner = ctx.game.card(*card_id).owner;
67            ctx.move_card(*card_id, ZoneType::Graveyard, owner);
68            emit_zone_trigger(
69                ctx.trigger_handler,
70                *card_id,
71                ZoneType::Hand,
72                ZoneType::Graveyard,
73            );
74            ctx.trigger_handler.run_trigger(
75                TriggerType::Discarded,
76                RunParams {
77                    card: Some(*card_id),
78                    player: Some(controller),
79                    ..Default::default()
80                },
81                false,
82            );
83        }
84    }
85
86    // Put +1/+1 counters on the conniver for each nonland card discarded,
87    // but only if it's still on the battlefield.
88    if nonland_count > 0 && ctx.game.card(conniver_id).zone == ZoneType::Battlefield {
89        ctx.add_counter(
90            conniver_id,
91            &crate::card::CounterType::P1P1,
92            nonland_count,
93            sa,
94            RunParams {
95                player: Some(controller),
96                ..Default::default()
97            },
98        );
99    }
100}