Skip to main content

manabrew_engine/keyword/
keyword_interface.rs

1//! Keyword interface data model.
2//!
3//! Ported as a concrete struct because current Rust callers store
4//! `KeywordInterface` by value, unlike Java's interface-based hierarchy.
5
6use std::collections::HashMap;
7
8use crate::card::trait_card_trait_changes::CardTraitChanges;
9use crate::core::HasSVars;
10use crate::ids::CardId;
11use crate::replacement::ReplacementEffect;
12use crate::spellability::SpellAbility;
13use crate::staticability::StaticAbility;
14use crate::trigger::Trigger;
15
16use super::keyword_instance::Keyword;
17
18#[derive(Debug, Clone)]
19pub struct KeywordInterface {
20    original: String,
21    keyword: Keyword,
22    title: String,
23    reminder_text: String,
24    intrinsic: bool,
25    idx: i64,
26    amount: i32,
27    svars: HashMap<String, String>,
28    triggers: Vec<Trigger>,
29    replacements: Vec<ReplacementEffect>,
30    spell_abilities: Vec<SpellAbility>,
31    static_abilities: Vec<StaticAbility>,
32    static_ability: Option<StaticAbility>,
33}
34
35impl KeywordInterface {
36    pub fn new(keyword: Keyword, original: impl Into<String>) -> Self {
37        let original = original.into();
38        Self {
39            title: original.clone(),
40            reminder_text: String::new(),
41            original,
42            keyword,
43            intrinsic: false,
44            idx: -1,
45            amount: 1,
46            svars: HashMap::new(),
47            triggers: Vec::new(),
48            replacements: Vec::new(),
49            spell_abilities: Vec::new(),
50            static_abilities: Vec::new(),
51            static_ability: None,
52        }
53    }
54
55    /// Get the original keyword string (e.g. "Flying", "Kicker:1 R").
56    pub fn get_original(&self) -> &str {
57        &self.original
58    }
59
60    /// Get the keyword enum variant.
61    pub fn get_keyword(&self) -> Keyword {
62        self.keyword
63    }
64
65    /// Get the display title for this keyword.
66    pub fn get_title(&self) -> String {
67        self.title.clone()
68    }
69
70    /// Get the reminder text for this keyword.
71    pub fn get_reminder_text(&self) -> String {
72        self.reminder_text.clone()
73    }
74
75    /// Get the numeric amount (default 1 for most keywords).
76    pub fn get_amount(&self) -> i32 {
77        self.amount
78    }
79
80    /// Get the amount as a string.
81    pub fn get_amount_string(&self) -> String {
82        self.amount.to_string()
83    }
84
85    /// Whether this keyword is intrinsic to the card.
86    pub fn is_intrinsic(&self) -> bool {
87        self.intrinsic
88    }
89
90    /// Set whether this keyword is intrinsic.
91    pub fn set_intrinsic(&mut self, value: bool) {
92        self.intrinsic = value;
93    }
94
95    /// Get the unique index for this keyword instance.
96    pub fn get_idx(&self) -> i64 {
97        self.idx
98    }
99
100    /// Set the unique index.
101    pub fn set_idx(&mut self, i: i64) {
102        self.idx = i;
103    }
104
105    pub fn set_title(&mut self, title: impl Into<String>) {
106        self.title = title.into();
107    }
108
109    pub fn set_reminder_text(&mut self, reminder_text: impl Into<String>) {
110        self.reminder_text = reminder_text.into();
111    }
112
113    pub fn set_amount(&mut self, amount: i32) {
114        self.amount = amount;
115    }
116
117    pub fn get_static(&self) -> Option<&StaticAbility> {
118        self.static_ability.as_ref()
119    }
120
121    pub fn set_static(&mut self, st: StaticAbility) {
122        self.static_ability = Some(st);
123    }
124
125    pub fn has_traits(&self) -> bool {
126        !self.triggers.is_empty()
127            || !self.replacements.is_empty()
128            || !self.spell_abilities.is_empty()
129            || !self.static_abilities.is_empty()
130            || self.static_ability.is_some()
131    }
132
133    pub fn add_trigger(&mut self, trg: Trigger) {
134        self.triggers.push(trg);
135    }
136
137    pub fn add_replacement(&mut self, repl: ReplacementEffect) {
138        self.replacements.push(repl);
139    }
140
141    pub fn add_spell_ability(&mut self, sa: SpellAbility) {
142        self.spell_abilities.push(sa);
143    }
144
145    pub fn add_static_ability(&mut self, st: StaticAbility) {
146        self.static_abilities.push(st);
147    }
148
149    pub fn get_triggers(&self) -> &[Trigger] {
150        &self.triggers
151    }
152
153    pub fn get_replacements(&self) -> &[ReplacementEffect] {
154        &self.replacements
155    }
156
157    pub fn get_abilities(&self) -> &[SpellAbility] {
158        &self.spell_abilities
159    }
160
161    pub fn get_static_abilities(&self) -> &[StaticAbility] {
162        &self.static_abilities
163    }
164
165    /// Whether this keyword instance is redundant given existing keywords.
166    pub fn redundant(&self, _existing_keywords: &[KeywordInterface]) -> bool {
167        false
168    }
169}
170
171impl HasSVars for KeywordInterface {
172    fn get_svar(&self, name: &str) -> Option<&str> {
173        self.svars.get(name).map(String::as_str)
174    }
175
176    fn set_svar(&mut self, name: String, value: String) {
177        self.svars.insert(name, value);
178    }
179
180    fn set_svars(&mut self, new_svars: HashMap<String, String>) {
181        self.svars = new_svars;
182    }
183
184    fn get_svars(&self) -> &HashMap<String, String> {
185        &self.svars
186    }
187
188    fn remove_svar(&mut self, var: &str) {
189        self.svars.remove(var);
190    }
191}
192
193impl CardTraitChanges for KeywordInterface {
194    fn apply_spell_ability(&self, mut list: Vec<SpellAbility>) -> Vec<SpellAbility> {
195        list.extend(self.spell_abilities.iter().cloned());
196        list
197    }
198
199    fn apply_trigger(&self, mut list: Vec<Trigger>) -> Vec<Trigger> {
200        list.extend(self.triggers.iter().cloned());
201        list
202    }
203
204    fn apply_replacement_effect(&self, mut list: Vec<ReplacementEffect>) -> Vec<ReplacementEffect> {
205        list.extend(self.replacements.iter().cloned());
206        list
207    }
208
209    fn apply_static_ability(&self, mut list: Vec<StaticAbility>) -> Vec<StaticAbility> {
210        if let Some(st) = &self.static_ability {
211            list.push(st.clone());
212        }
213        list.extend(self.static_abilities.iter().cloned());
214        list
215    }
216
217    fn change_text(&mut self) {
218        self.title = self.title.trim().to_string();
219        self.reminder_text = self.reminder_text.trim().to_string();
220    }
221
222    fn copy(&self, _host: CardId, _lki: bool) -> Box<dyn CardTraitChanges> {
223        Box::new(self.clone())
224    }
225}