Skip to main content

manabrew_engine/ability/effects/
trait_animate_effect.rs

1//! AnimateEffectBase — abstract base for animate effects.
2//!
3//! Mirrors Java's `AnimateEffectBase.java`.
4//! Provides shared logic for `AnimateEffect` and `AnimateAllEffect`
5//! that handles setting power/toughness, types, colors, and keywords.
6
7use crate::spellability::SpellAbility;
8
9/// Parsed animate parameters from a spell ability.
10/// Captures the fields that animate effects need to apply.
11#[derive(Debug, Clone, Default)]
12pub struct AnimateParams {
13    pub power: Option<i32>,
14    pub toughness: Option<i32>,
15    pub add_types: Vec<String>,
16    pub add_keywords: Vec<String>,
17    pub colors: Option<Vec<String>>,
18    pub overwrite_types: bool,
19}
20
21/// Parse shared animate parameters from a spell ability.
22/// Used by both `animate_effect` and `animate_all_effect`.
23pub fn parse_animate_params(sa: &SpellAbility) -> AnimateParams {
24    AnimateParams {
25        power: sa.ir.animate_power,
26        toughness: sa.ir.animate_toughness,
27        add_types: sa
28            .ir
29            .animate_types_text
30            .as_deref()
31            .map(|types| types.split(',').map(|s| s.trim().to_string()).collect())
32            .unwrap_or_default(),
33        add_keywords: sa
34            .ir
35            .animate_keywords_text
36            .as_deref()
37            .map(|kws| kws.split(',').map(|s| s.trim().to_string()).collect())
38            .unwrap_or_default(),
39        colors: sa
40            .ir
41            .animate_colors_text
42            .as_deref()
43            .map(|colors| colors.split(',').map(|s| s.trim().to_string()).collect()),
44        overwrite_types: sa.ir.animate_overwrite_types,
45    }
46}
47
48/// Apply animate effects to a single card.
49/// Mirrors Java's `AnimateEffectBase.doAnimate(Card, SpellAbility, ...)`.
50///
51/// Sets the card's power/toughness, adds types, keywords, and colors
52/// as specified by the parsed AnimateParams.
53pub fn do_animate(
54    game: &mut crate::game::GameState,
55    card_id: crate::ids::CardId,
56    params: &AnimateParams,
57    _sa: &SpellAbility,
58) {
59    let card = game.card_mut(card_id);
60
61    // Set power/toughness if specified
62    if let Some(power) = params.power {
63        card.add_new_pt(power, params.toughness.unwrap_or(card.toughness()));
64    } else if let Some(toughness) = params.toughness {
65        card.add_new_pt(card.power(), toughness);
66    }
67
68    // Add types
69    if params.overwrite_types {
70        card.type_line.core_types.clear();
71    }
72    for type_name in &params.add_types {
73        if let Some(core_type) = forge_foundation::CoreType::from_name(type_name) {
74            card.type_line.core_types.insert(core_type);
75        } else {
76            // Treat as subtype
77            if !card
78                .type_line
79                .subtypes
80                .iter()
81                .any(|s| s.eq_ignore_ascii_case(type_name))
82            {
83                card.type_line.subtypes.push(type_name.clone());
84            }
85        }
86    }
87
88    // Add keywords
89    for kw in &params.add_keywords {
90        if !card.granted_keywords.contains_string_ignore_case(kw) {
91            card.granted_keywords.add(kw);
92        }
93    }
94
95    // Set colors if specified
96    if let Some(ref colors) = params.colors {
97        let mut color_set = forge_foundation::ColorSet::COLORLESS;
98        for color_name in colors {
99            color_set = color_set.union(forge_foundation::ColorSet::from_names(color_name));
100        }
101        card.color = color_set;
102    }
103}
104
105/// Run the animate effect (entry point that parses params then applies).
106/// Mirrors Java's `AnimateEffectBase.run()`.
107pub fn run(ctx: &mut super::EffectContext, sa: &SpellAbility, target_cards: &[crate::ids::CardId]) {
108    let params = parse_animate_params(sa);
109    for &card_id in target_cards {
110        do_animate(ctx.game, card_id, &params, sa);
111    }
112}
113
114/// Resolve the animate effect for targeted/defined cards.
115/// Mirrors Java's `AnimateEffectBase.resolve()`.
116pub fn resolve(ctx: &mut super::EffectContext, sa: &SpellAbility) {
117    let target_cards = crate::ability::spell_ability_effect::get_target_cards(ctx.game, sa);
118    run(ctx, sa, &target_cards);
119}