Skip to main content

manabrew_engine/ability/effects/
regenerate_effect.rs

1use forge_foundation::ZoneType;
2
3use super::EffectContext;
4
5/// Resolve `SP$ Regenerate` — add a regeneration shield to a creature.
6///
7/// Mirrors Java `RegenerateEffect.java`.
8/// Increments `card.regeneration_shields` on the target creature.
9/// When a creature with shields would be destroyed, a shield is consumed
10/// instead: the creature is tapped, removed from combat, and damage is removed.
11/// Shields reset at end of turn.
12///
13/// # Card script examples
14/// ```text
15/// A:SP$ Regenerate | Defined$ Self
16/// A:SP$ Regenerate | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control
17/// ```
18/// Struct form of this effect so it can participate in the
19/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
20/// `RegenerateEffect` class extending `SpellAbilityEffect`.
21#[manabrew_engine_macros::spell_effect(RegenerateEffect)]
22fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
23    // Targeted: use the chosen target card.
24    if let Some(target_card) = sa.target_chosen.target_card {
25        if ctx.game.card(target_card).zone == ZoneType::Battlefield
26            && ctx.game.card(target_card).is_creature()
27            && !crate::staticability::static_ability_cant_regenerate::cant_regenerate(
28                &ctx.game.cards,
29                ctx.game.card(target_card),
30            )
31        {
32            ctx.game.card_mut(target_card).regeneration_shields += 1;
33        }
34        return;
35    }
36
37    // Defined$ Self — regenerate the source card.
38    if let Some(source) = sa.source {
39        if ctx.game.card(source).zone == ZoneType::Battlefield
40            && ctx.game.card(source).is_creature()
41            && !crate::staticability::static_ability_cant_regenerate::cant_regenerate(
42                &ctx.game.cards,
43                ctx.game.card(source),
44            )
45        {
46            ctx.game.card_mut(source).regeneration_shields += 1;
47        }
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use crate::ability::spell_ability_effect::SpellAbilityEffect;
54    use forge_foundation::{CardTypeLine, ColorSet, ManaCost, ZoneType};
55    use std::collections::HashMap;
56
57    use crate::ability::effects::EffectContext;
58    use crate::agent::PassAgent;
59    use crate::card::Card;
60    use crate::game::GameState;
61    use crate::ids::{CardId, PlayerId};
62    use crate::mana::ManaPool;
63    use crate::spellability::SpellAbility;
64    use crate::trigger::handler::TriggerHandler;
65
66    fn make_creature(game: &mut GameState, owner: PlayerId) -> CardId {
67        let c = Card::new(
68            CardId(0),
69            "Bear".into(),
70            owner,
71            CardTypeLine::parse("Creature - Bear"),
72            ManaCost::parse("1 G"),
73            ColorSet::GREEN,
74            Some(2),
75            Some(2),
76            vec![],
77            vec![],
78        );
79        game.create_card(c)
80    }
81
82    #[test]
83    fn regenerate_adds_shield() {
84        let mut game = GameState::new(&["Alice", "Bob"], 20);
85        let p0 = PlayerId(0);
86        let c1 = make_creature(&mut game, p0);
87        game.move_card(c1, ZoneType::Battlefield, p0);
88        assert_eq!(game.card(c1).regeneration_shields, 0);
89
90        let sa = SpellAbility::new_simple(Some(c1), p0, "SP$ Regenerate | Defined$ Self");
91
92        let mut th = TriggerHandler::new();
93        let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
94            vec![Box::new(PassAgent), Box::new(PassAgent)];
95        let mut mp = vec![ManaPool::default(), ManaPool::default()];
96        let templates = HashMap::new();
97        let templates_variants = HashMap::new();
98        let token_fallback = HashMap::new();
99        let edition_dates: HashMap<String, String> = HashMap::new();
100        let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
101        let mut ctx = EffectContext {
102            game: &mut game,
103            combat: None,
104            agents: &mut agents,
105            trigger_handler: &mut th,
106            token_templates: &templates,
107            token_art_variants: &templates_variants,
108            token_fallback: &token_fallback,
109            edition_dates: &edition_dates,
110            mana_pools: &mut mp,
111            parent_target_card: None,
112            rng: &mut rng_adapter,
113        };
114        super::RegenerateEffect::resolve(&mut ctx, &sa);
115
116        assert_eq!(ctx.game.card(c1).regeneration_shields, 1);
117    }
118
119    #[test]
120    fn regenerate_stacks_shields() {
121        let mut game = GameState::new(&["Alice", "Bob"], 20);
122        let p0 = PlayerId(0);
123        let c1 = make_creature(&mut game, p0);
124        game.move_card(c1, ZoneType::Battlefield, p0);
125
126        let sa = SpellAbility::new_simple(Some(c1), p0, "SP$ Regenerate | Defined$ Self");
127
128        let mut th = TriggerHandler::new();
129        let mut agents: Vec<Box<dyn crate::agent::PlayerAgent>> =
130            vec![Box::new(PassAgent), Box::new(PassAgent)];
131        let mut mp = vec![ManaPool::default(), ManaPool::default()];
132        let templates = HashMap::new();
133        let templates_variants = HashMap::new();
134        let token_fallback = HashMap::new();
135        let edition_dates: HashMap<String, String> = HashMap::new();
136        let mut rng_adapter = crate::game_rng::ThreadRngAdapter;
137        let mut ctx = EffectContext {
138            game: &mut game,
139            combat: None,
140            agents: &mut agents,
141            trigger_handler: &mut th,
142            token_templates: &templates,
143            token_art_variants: &templates_variants,
144            token_fallback: &token_fallback,
145            edition_dates: &edition_dates,
146            mana_pools: &mut mp,
147            parent_target_card: None,
148            rng: &mut rng_adapter,
149        };
150        super::RegenerateEffect::resolve(&mut ctx, &sa);
151        super::RegenerateEffect::resolve(&mut ctx, &sa);
152
153        assert_eq!(ctx.game.card(c1).regeneration_shields, 2);
154    }
155}