Skip to main content

manabrew_engine/ability/effects/
life_gain_effect.rs

1use super::{resolve_defined_player_with_sa, resolve_numeric_svar, EffectContext};
2use crate::ability::ability_ir::EffectIr;
3use crate::event::RunParams;
4use crate::replacement::replacement_handler::{apply_replacements, ReplacementEvent};
5use crate::replacement::ReplacementResult;
6use crate::spellability::SpellAbility;
7use crate::trigger::TriggerType;
8
9/// Struct form of this effect so it can participate in the
10/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
11/// `LifeGainEffect` class extending `SpellAbilityEffect`.
12#[manabrew_engine_macros::spell_effect(LifeGainEffect)]
13fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
14    let amount = resolve_life_amount(ctx, sa);
15    let target = sa
16        .defined()
17        .and_then(|defined| {
18            resolve_defined_player_with_sa(defined, sa, sa.activating_player, ctx.game)
19        })
20        .unwrap_or(sa.activating_player);
21    if crate::staticability::static_ability_cant_gain_lose_pay_life::cant_gain_life(
22        ctx.game, target,
23    ) {
24        return;
25    }
26    // Run GainLife replacement effects (e.g. double life gain).
27    let mut event = ReplacementEvent::GainLife {
28        player: target,
29        amount,
30    };
31    let result = apply_replacements(ctx.game, &mut event);
32    let amount = if let ReplacementEvent::GainLife {
33        amount: final_amount,
34        ..
35    } = event
36    {
37        final_amount
38    } else {
39        amount
40    };
41    if result == ReplacementResult::Skipped || amount <= 0 {
42        return;
43    }
44    ctx.game.player_gain_life(target, amount);
45
46    // Fire LifeGained trigger
47    ctx.trigger_handler.run_trigger(
48        TriggerType::LifeGained,
49        RunParams {
50            player: Some(target),
51            life_amount: Some(amount),
52            first_time: Some(ctx.game.player(target).life_gained_this_turn == amount),
53            source_card: sa.source,
54            source_sa: Some(sa.clone()),
55            ..Default::default()
56        },
57        false,
58    );
59}
60
61fn resolve_life_amount(ctx: &EffectContext, sa: &SpellAbility) -> i32 {
62    if let Some(EffectIr::GainLife(ir)) = &sa.ir.effect {
63        if let Some(amount) = &ir.amount {
64            let resolved = amount.resolve_for_spell_ability(ctx.game, sa, 1);
65            #[cfg(debug_assertions)]
66            debug_assert_eq!(
67                resolved,
68                resolve_numeric_svar(ctx.game, sa, crate::parsing::keys::LIFE_AMOUNT, 1),
69                "compiled GainLife amount diverged from string params"
70            );
71            return resolved;
72        }
73    }
74
75    resolve_numeric_svar(ctx.game, sa, crate::parsing::keys::LIFE_AMOUNT, 1)
76}