Skip to main content

manabrew_engine/card/
card_trait_changes.rs

1//! Rust parity model for Java `CardTraitChanges`.
2
3use crate::ids::CardId;
4use crate::replacement::ReplacementEffect;
5use crate::spellability::SpellAbility;
6use crate::staticability::{StaticAbility, StaticMode};
7use crate::trigger::Trigger;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11pub struct CardTraitChanges {
12    pub abilities: Vec<SpellAbility>,
13    pub removed_abilities: Vec<SpellAbility>,
14    pub triggers: Vec<Trigger>,
15    pub replacements: Vec<ReplacementEffect>,
16    pub static_abilities: Vec<StaticAbility>,
17    /// Java parity subset for predicate removal: true means remove all existing
18    /// card traits before applying additions (used by addChangedCardTraitsByText).
19    pub remove_all: bool,
20}
21
22impl CardTraitChanges {
23    pub fn remove_all_layer(
24        abilities: Vec<SpellAbility>,
25        triggers: Vec<Trigger>,
26        replacements: Vec<ReplacementEffect>,
27        static_abilities: Vec<StaticAbility>,
28    ) -> Self {
29        Self {
30            abilities,
31            removed_abilities: Vec::new(),
32            triggers,
33            replacements,
34            static_abilities,
35            remove_all: true,
36        }
37    }
38
39    /// Return true when any injected static ability changes casting cost.
40    pub fn contains_cost_change(&self) -> bool {
41        self.static_abilities.iter().any(|st_ab| {
42            st_ab.check_mode(&StaticMode::ReduceCost) || st_ab.check_mode(&StaticMode::RaiseCost)
43        })
44    }
45
46    /// Java-parity copy hook.
47    /// Host/LKI-specific rewrites are not yet modeled in Rust, so this clones.
48    pub fn copy(&self, _host: CardId, _lki: bool) -> Self {
49        self.clone()
50    }
51
52    /// Java-parity text-rewrite hook.
53    /// Normalizes mutable text payloads carried by trait changes.
54    pub fn change_text(&mut self) {
55        for sa in &mut self.abilities {
56            sa.ability_text = sa.ability_text.trim().to_string();
57        }
58        for sa in &mut self.removed_abilities {
59            sa.ability_text = sa.ability_text.trim().to_string();
60        }
61    }
62
63    pub fn apply_spell_ability(&self, mut list: Vec<SpellAbility>) -> Vec<SpellAbility> {
64        if self.remove_all {
65            list.clear();
66        }
67        for removed in &self.removed_abilities {
68            list.retain(|sa| {
69                !(sa.source == removed.source
70                    && sa.api == removed.api
71                    && sa.ability_text == removed.ability_text)
72            });
73        }
74        list.extend(self.abilities.iter().cloned());
75        list
76    }
77
78    pub fn apply_trigger(&self, mut list: Vec<Trigger>) -> Vec<Trigger> {
79        if self.remove_all {
80            list.clear();
81        }
82        list.extend(self.triggers.iter().cloned());
83        list
84    }
85
86    pub fn apply_replacement_effect(
87        &self,
88        mut list: Vec<ReplacementEffect>,
89    ) -> Vec<ReplacementEffect> {
90        if self.remove_all {
91            list.clear();
92        }
93        list.extend(self.replacements.iter().cloned());
94        list
95    }
96
97    pub fn apply_static_ability(&self, mut list: Vec<StaticAbility>) -> Vec<StaticAbility> {
98        if self.remove_all {
99            list.clear();
100        }
101        list.extend(self.static_abilities.iter().cloned());
102        list
103    }
104}
105
106impl crate::card::trait_card_trait_changes::CardTraitChanges for CardTraitChanges {
107    fn apply_spell_ability(&self, list: Vec<SpellAbility>) -> Vec<SpellAbility> {
108        CardTraitChanges::apply_spell_ability(self, list)
109    }
110
111    fn apply_trigger(&self, list: Vec<Trigger>) -> Vec<Trigger> {
112        CardTraitChanges::apply_trigger(self, list)
113    }
114
115    fn apply_replacement_effect(&self, list: Vec<ReplacementEffect>) -> Vec<ReplacementEffect> {
116        CardTraitChanges::apply_replacement_effect(self, list)
117    }
118
119    fn apply_static_ability(&self, list: Vec<StaticAbility>) -> Vec<StaticAbility> {
120        CardTraitChanges::apply_static_ability(self, list)
121    }
122
123    fn change_text(&mut self) {
124        CardTraitChanges::change_text(self);
125    }
126
127    fn copy(
128        &self,
129        host: CardId,
130        lki: bool,
131    ) -> Box<dyn crate::card::trait_card_trait_changes::CardTraitChanges> {
132        Box::new(CardTraitChanges::copy(self, host, lki))
133    }
134}