Skip to main content

manabrew_engine/ability/effects/
debuff_effect.rs

1//! Debuff — reduce stats permanently (digital-only).
2
3use super::EffectContext;
4use crate::ability::ability_ir::DebuffAllSuffixKeywords;
5use crate::ability::spell_ability_effect::get_target_cards;
6use crate::keyword::keyword_instance::Keyword;
7use crate::parsing::keys;
8use crate::spellability::AbilityDuration;
9
10/// End-of-turn revert for debuff. Mirrors the `GameCommand.run()` in Java
11/// `DebuffEffect` that restores the original P/T when the effect expires.
12///
13/// Reverses the debuff by adding back the debuff amount to power/toughness modifiers.
14pub fn run(game: &mut crate::game::GameState, card_id: crate::ids::CardId, amount: i32) {
15    if game.card(card_id).zone == forge_foundation::ZoneType::Battlefield {
16        game.card_mut(card_id).power_modifier += amount;
17        game.card_mut(card_id).toughness_modifier += amount;
18    }
19}
20
21/// Struct form of this effect so it can participate in the
22/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
23/// `DebuffEffect` class extending `SpellAbilityEffect`.
24#[manabrew_engine_macros::spell_effect(DebuffEffect)]
25fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
26    let amount = if sa.ir.debuff.num_present {
27        super::resolve_numeric_svar(ctx.game, sa, keys::NUM, 0).max(0)
28    } else {
29        0
30    };
31    let permanent = matches!(sa.ir.duration, Some(AbilityDuration::Permanent));
32    let targets = debuff_targets(ctx, sa);
33    for target in targets {
34        if amount > 0 {
35            ctx.game.card_mut(target).power_modifier -= amount;
36            ctx.game.card_mut(target).toughness_modifier -= amount;
37        }
38
39        let mut removed_keywords = sa.ir.debuff.keywords.clone();
40        if matches!(
41            sa.ir.debuff.all_suffix_keywords,
42            Some(DebuffAllSuffixKeywords::Walk)
43        ) {
44            removed_keywords.extend(landwalk_keywords(ctx, target));
45        }
46
47        for kw in removed_keywords {
48            if permanent {
49                ctx.game.card_mut(target).remove_intrinsic_keyword(&kw);
50            } else {
51                ctx.game.card_mut(target).add_cant_have_keyword(&kw);
52            }
53        }
54    }
55}
56
57fn debuff_targets(
58    ctx: &EffectContext,
59    sa: &crate::spellability::SpellAbility,
60) -> Vec<crate::ids::CardId> {
61    get_target_cards(ctx.game, sa)
62}
63
64fn landwalk_keywords(ctx: &EffectContext, target: crate::ids::CardId) -> Vec<String> {
65    let card = ctx.game.card(target);
66    card.keywords
67        .get_values_for(Keyword::Landwalk)
68        .into_iter()
69        .chain(card.granted_keywords.get_values_for(Keyword::Landwalk))
70        .chain(card.pump_keywords.get_values_for(Keyword::Landwalk))
71        .map(|kw| kw.original.clone())
72        .collect()
73}