Skip to main content

manabrew_engine/replacement/
replace_gain_life.rs

1//! Replacement logic for `Event$ GainLife`.
2//!
3//! Mirrors Java `ReplaceGainLife.java` in `forge/game/replacement/`.
4
5use crate::card::Card;
6use crate::game::GameState;
7use crate::ids::CardId;
8use crate::parsing::compare::compare_expr;
9
10use super::replacement_effect::ReplacementEffect;
11use super::replacement_handler::{execute_replace_with_numeric_update, ReplacementEvent};
12use super::replacement_result::ReplacementResult;
13use super::replacement_type::ReplacementType;
14use crate::card_trait_base::CardTrait;
15
16/// Mirrors Java `ReplaceGainLife.canReplace()`.
17pub fn can_replace(
18    effect: &ReplacementEffect,
19    event: &ReplacementEvent,
20    _game: &GameState,
21    source_card: &Card,
22) -> bool {
23    if effect.event != ReplacementType::GainLife {
24        return false;
25    }
26    let (player, amount) = match event {
27        ReplacementEvent::GainLife { player, amount } => (*player, *amount),
28        _ => return false,
29    };
30    if amount <= 0 {
31        return false;
32    }
33    if let Some(valid) = effect.ir.valid_player_selector.as_ref() {
34        if !effect.matches_compiled_valid_player(valid, player, source_card) {
35            return false;
36        }
37    }
38    if let Some(amount_cmp) = effect.ir.amount_text.as_deref() {
39        let rhs = amount_cmp
40            .get(2..)
41            .and_then(|n| n.parse::<i32>().ok())
42            .unwrap_or(0);
43        let cmp = format!("{}{}", amount_cmp.get(..2).unwrap_or("GE"), rhs);
44        if !compare_expr(amount, &cmp) {
45            return false;
46        }
47    }
48    true
49}
50
51/// Mirrors Java `ReplacementHandler.executeReplacement()` for GainLife.
52pub fn execute(
53    effect: &ReplacementEffect,
54    event: &mut ReplacementEvent,
55    _game: &GameState,
56    _source_card_id: CardId,
57) -> ReplacementResult {
58    match event {
59        ReplacementEvent::GainLife { .. } => {}
60        _ => return ReplacementResult::NotReplaced,
61    }
62    if effect.prevents() {
63        if let ReplacementEvent::GainLife { amount, .. } = event {
64            *amount = 0;
65        }
66        return ReplacementResult::Skipped;
67    }
68    // Handle built-in replacement modes before SVar chain.
69    if let Some(replace) = effect.replace_with() {
70        match replace {
71            "GainDouble" => {
72                if let ReplacementEvent::GainLife { amount, .. } = event {
73                    *amount *= 2;
74                    return ReplacementResult::Updated;
75                }
76            }
77            "NoLife" => {
78                if let ReplacementEvent::GainLife { amount, .. } = event {
79                    *amount = 0;
80                    return ReplacementResult::Skipped;
81                }
82            }
83            _ => {}
84        }
85    }
86    if let Some(result) =
87        execute_replace_with_numeric_update(effect, event, _game, _source_card_id, "LifeGained")
88    {
89        return result;
90    }
91    ReplacementResult::Replaced
92}