Skip to main content

manabrew_engine/ability/effects/
poison_effect.rs

1use super::{resolve_defined_player, resolve_numeric_svar, EffectContext};
2use crate::ability::ability_ir::EffectIr;
3use crate::spellability::SpellAbility;
4
5/// Resolve `DB$ Poison` / `SP$ Poison` — add poison counters to players.
6///
7/// Mirrors Java `PoisonEffect.java` (~45 lines).
8///
9/// Real card patterns:
10/// - `DB$ Poison | Defined$ Player | Num$ 1`    (Ichor Rats — all players)
11/// - `DB$ Poison | Defined$ Opponent | Num$ 1`  (Prologue to Phyresis)
12/// - `DB$ Poison | Defined$ You | Num$ 1`       (Phyrexian Vatmother)
13/// - `DB$ Poison | ValidTgts$ Player | Num$ 1`  (Hand of the Praetors — targeted)
14/// - `DB$ Poison | Defined$ TriggeredTarget | Num$ 1` (trigger context)
15/// Struct form of this effect so it can participate in the
16/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
17/// `PoisonEffect` class extending `SpellAbilityEffect`.
18#[manabrew_engine_macros::spell_effect(PoisonEffect)]
19fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
20    let amount = resolve_poison_amount(ctx, sa);
21    if amount == 0 {
22        return;
23    }
24
25    let controller = sa.activating_player;
26
27    // If targeting was used (ValidTgts$ Player), use the chosen target.
28    if let Some(target_player) = sa.target_chosen.target_player {
29        if ctx.game.player(target_player).is_alive()
30            && !crate::staticability::static_ability_cant_put_counter::any_cant_put_counter_on_player(
31                &ctx.game.cards,
32                target_player,
33                &crate::card::CounterType::Poison,
34            ) {
35                if amount > 0 {
36                    ctx.game.player_add_poison(target_player, amount);
37                } else {
38                    ctx.game.player_remove_poison(target_player, -amount);
39                }
40            }
41        return;
42    }
43
44    // Resolve Defined$ parameter.
45    let defined = sa.defined().unwrap_or("Opponent");
46
47    // Special case: "Player" means ALL alive players (distinct from
48    // resolve_defined_player which returns a single player).
49    if defined == "Player" {
50        let alive: Vec<_> = ctx.game.alive_players().into_iter().collect();
51        for pid in alive {
52            if !crate::staticability::static_ability_cant_put_counter::any_cant_put_counter_on_player(
53                &ctx.game.cards,
54                pid,
55                &crate::card::CounterType::Poison,
56            ) {
57                if amount > 0 {
58                    ctx.game.player_add_poison(pid, amount);
59                } else {
60                    ctx.game.player_remove_poison(pid, -amount);
61                }
62            }
63        }
64        return;
65    }
66
67    // Single player: You, Opponent, TriggeredTarget, etc.
68    if let Some(pid) = resolve_defined_player(defined, controller, ctx.game) {
69        if ctx.game.player(pid).is_alive()
70            && !crate::staticability::static_ability_cant_put_counter::any_cant_put_counter_on_player(
71                &ctx.game.cards,
72                pid,
73                &crate::card::CounterType::Poison,
74            ) {
75                if amount > 0 {
76                    ctx.game.player_add_poison(pid, amount);
77                } else {
78                    ctx.game.player_remove_poison(pid, -amount);
79                }
80            }
81    }
82}
83
84fn resolve_poison_amount(ctx: &EffectContext, sa: &SpellAbility) -> i32 {
85    if let Some(EffectIr::Poison(ir)) = &sa.ir.effect {
86        if let Some(amount) = &ir.amount {
87            let resolved = amount.resolve_for_spell_ability(ctx.game, sa, 1);
88            #[cfg(debug_assertions)]
89            debug_assert_eq!(
90                resolved,
91                resolve_numeric_svar(ctx.game, sa, "Num", 1),
92                "compiled Poison amount diverged from string params"
93            );
94            return resolved;
95        }
96    }
97
98    resolve_numeric_svar(ctx.game, sa, "Num", 1)
99}