manabrew_engine/trigger/
wrapped_ability.rs1use std::collections::HashMap;
2
3use crate::game::GameState;
4use crate::keyword::keyword_instance::Keyword;
5use crate::spellability::alternative_cost::AlternativeCost;
6use crate::spellability::SpellAbility;
7use crate::trigger::trigger::Trigger;
8
9#[derive(Debug, Clone)]
12pub struct WrappedAbility {
13 pub wrapped: SpellAbility,
14 pub trigger: Option<Trigger>,
17 additional_ability_lists: HashMap<String, Vec<String>>,
18}
19
20impl WrappedAbility {
21 pub fn new(wrapped: SpellAbility) -> Self {
22 Self {
23 wrapped,
24 trigger: None,
25 additional_ability_lists: HashMap::new(),
26 }
27 }
28
29 pub fn with_trigger(wrapped: SpellAbility, trigger: Trigger) -> Self {
30 Self {
31 wrapped,
32 trigger: Some(trigger),
33 additional_ability_lists: HashMap::new(),
34 }
35 }
36
37 pub fn has_param(&self, key: &str) -> bool {
38 self.get_param(key).is_some() || self.wrapped.has_additional_ability(key)
39 }
40
41 pub fn add_cost_to_hash_list(&mut self, cost_key: &str, value: String) {
42 self.wrapped
43 .paid_hash
44 .entry(cost_key.to_string())
45 .or_default()
46 .push(value);
47 }
48
49 pub fn reset_paid_hash(&mut self) {
50 self.wrapped.paid_hash.clear();
51 }
52
53 pub fn has_triggering_object(&self, key: &str) -> bool {
54 self.wrapped.has_triggering_object(key)
55 }
56
57 pub fn reset_triggering_objects(&mut self) {
58 self.wrapped.trigger_objects.clear();
59 }
60
61 pub fn can_play(&self) -> bool {
62 true
63 }
64
65 pub fn copy(&self) -> Self {
66 self.clone()
67 }
68
69 pub fn yield_key(&self) -> String {
70 if !self.wrapped.stack_description.is_empty() {
71 self.wrapped.stack_description.clone()
72 } else if !self.wrapped.description.is_empty() {
73 self.wrapped.description.clone()
74 } else {
75 self.wrapped.ability_text.clone()
76 }
77 }
78
79 pub fn to_unsuppressed_string(&self) -> String {
80 self.yield_key()
81 }
82
83 pub fn has_s_var(&self, game: &GameState, key: &str) -> bool {
84 self.wrapped
85 .source
86 .map(|cid| game.card(cid).svars.contains_key(key))
87 .unwrap_or(false)
88 }
89
90 pub fn reset_once_resolved(&mut self) {
91 }
93
94 pub fn uses_targeting(&self) -> bool {
95 self.wrapped.uses_targeting()
96 }
97
98 pub fn has_additional_ability(&self, key: &str) -> bool {
99 self.wrapped.has_additional_ability(key)
100 || self.additional_ability_lists.contains_key(key)
101 || self.get_param(key).is_some()
102 }
103
104 pub fn reset_targets(&mut self) {
105 self.wrapped.clear_targets();
106 }
107
108 pub fn resolve(&self) -> bool {
109 true
110 }
111
112 pub fn get_param(&self, key: &str) -> Option<&str> {
117 if self.wrapped.param_is_true(key) {
118 Some("True")
119 } else {
120 self.wrapped.param_value(key)
121 }
122 }
123
124 pub fn get_param_or_default<'a>(&'a self, key: &str, default: &'a str) -> &'a str {
127 self.get_param(key).unwrap_or(default)
128 }
129
130 pub fn set_paid_hash(&mut self, hash: HashMap<String, Vec<String>>) {
133 self.wrapped.paid_hash = hash;
134 }
135
136 pub fn get_paid_list(&self, key: &str, _intrinsic: bool) -> Vec<String> {
141 self.wrapped.paid_hash.get(key).cloned().unwrap_or_default()
142 }
143
144 pub fn set_triggering_objects(&mut self, objects: HashMap<String, String>) {
147 self.wrapped.trigger_objects.clear();
148 for (key, value) in objects {
149 self.wrapped.set_triggering_object(&key, value);
150 }
151 }
152
153 pub fn set_triggering_object(&mut self, key: &str, value: String) {
156 self.wrapped.set_triggering_object(key, value);
157 }
158
159 pub fn get_triggering_object(&self, key: &str) -> Option<&str> {
162 self.wrapped.get_triggering_object(key)
163 }
164
165 pub fn get_stack_description(&self, game: &GameState) -> String {
171 if let Some(ref trigger) = self.trigger {
172 let source = self.wrapped.source.unwrap_or(crate::ids::CardId(0));
173 let player = self.wrapped.activating_player;
174 let base = trigger.replace_ability_text(&trigger.description, game, source, player);
175 let important = trigger
176 .mode
177 .get_important_stack_objects(trigger, &self.wrapped);
178 let mut sb = base;
179 if !important.is_empty() {
180 sb.push_str(" [");
181 sb.push_str(&important);
182 sb.push(']');
183 }
184 sb
185 } else if !self.wrapped.stack_description.is_empty() {
186 self.wrapped.stack_description.clone()
187 } else {
188 self.wrapped.description.clone()
189 }
190 }
191
192 pub fn get_s_var(&self, game: &GameState, name: &str) -> Option<String> {
195 self.wrapped
196 .source
197 .and_then(|cid| game.card(cid).get_s_var(name).map(str::to_string))
198 }
199
200 pub fn get_s_var_int(&self, game: &GameState, name: &str) -> Option<i32> {
203 self.get_s_var(game, name)
204 .and_then(|v| v.parse::<i32>().ok())
205 }
206
207 pub fn set_s_var(&self, game: &mut GameState, name: &str, value: &str) {
210 if let Some(cid) = self.wrapped.source {
211 game.card_mut(cid).set_s_var(name, value);
212 }
213 }
214
215 pub fn get_additional_ability(&self, key: &str) -> Option<&str> {
219 self.get_param(key)
220 }
221
222 pub fn get_additional_ability_list(&self, name: &str) -> Vec<String> {
226 if let Some(list) = self.additional_ability_lists.get(name) {
227 return list.clone();
228 }
229 self.get_param(name)
230 .map(|v| v.split('&').map(|s| s.trim().to_string()).collect())
231 .unwrap_or_default()
232 }
233
234 pub fn set_additional_ability_list(&mut self, name: &str, list: Vec<String>) {
237 self.additional_ability_lists.insert(name.to_string(), list);
238 }
239
240 pub fn is_alternative_cost(&self, ac: AlternativeCost) -> bool {
243 self.wrapped.alt_cost == Some(ac)
244 }
245
246 pub fn is_keyword(&self, kw: Keyword) -> bool {
250 self.wrapped
251 .param_value("Keyword")
252 .map(|v| {
253 let kw_str = format!("{kw:?}");
254 v.eq_ignore_ascii_case(&kw_str)
255 })
256 .unwrap_or(false)
257 }
258}