Skip to main content

manabrew_engine/game_loop/
trigger_replacement_base.rs

1use std::collections::HashMap;
2
3use forge_foundation::ZoneType;
4
5use crate::card::card_state::CardState;
6use crate::card_trait_base::CardTraitBase;
7use crate::core::Identifiable;
8use crate::game::GameState;
9use crate::ids::CardId;
10use crate::keyword::keyword_interface::KeywordInterface;
11use crate::spellability::SpellAbility;
12use serde::{Deserialize, Serialize};
13
14/// Port of Java `TriggerReplacementBase`.
15///
16/// Java:
17/// `public abstract class TriggerReplacementBase extends CardTraitBase implements IIdentifiable, Cloneable`
18#[derive(Debug, Clone, Default, Serialize, Deserialize)]
19pub struct TriggerReplacementBase {
20    pub card_trait_base: CardTraitBase,
21    pub valid_host_zones: Option<Vec<ZoneType>>,
22    pub overriding_ability: Option<SpellAbility>,
23}
24
25impl Identifiable for TriggerReplacementBase {
26    fn id(&self) -> i32 {
27        self.card_trait_base.id()
28    }
29}
30
31impl TriggerReplacementBase {
32    pub fn set_host_card_id(&mut self, id: CardId) {
33        self.card_trait_base.set_host_card_id(id);
34        if let Some(overriding_ability) = self.overriding_ability.as_mut() {
35            overriding_ability.set_host_card_id(id);
36        }
37    }
38
39    pub fn set_keyword(&mut self, kw: KeywordInterface) {
40        self.card_trait_base.set_keyword(kw.clone());
41        if let Some(overriding_ability) = self.overriding_ability.as_mut() {
42            overriding_ability.set_keyword(kw);
43        }
44    }
45
46    pub fn set_card_state(&mut self, state: CardState) {
47        self.card_trait_base.set_card_state(&state);
48        if let Some(overriding_ability) = self.overriding_ability.as_mut() {
49            overriding_ability.set_card_state(&state);
50        }
51    }
52
53    pub fn get_active_zone(&self) -> Option<&[ZoneType]> {
54        self.valid_host_zones.as_deref()
55    }
56
57    pub fn set_active_zone(&mut self, zones: Vec<ZoneType>) {
58        self.valid_host_zones = Some(zones);
59    }
60
61    pub fn zones_check(&self, game: &GameState, host_card_zone: Option<ZoneType>) -> bool {
62        !self.card_trait_base.host_card(game).phased_out
63            && (self.valid_host_zones.is_none()
64                || self
65                    .valid_host_zones
66                    .as_ref()
67                    .is_some_and(|zones| zones.is_empty())
68                || (host_card_zone.is_some()
69                    && self
70                        .valid_host_zones
71                        .as_ref()
72                        .is_some_and(|zones| zones.contains(&host_card_zone.unwrap()))))
73    }
74
75    pub fn get_overriding_ability(&self) -> Option<&SpellAbility> {
76        self.overriding_ability.as_ref()
77    }
78
79    pub fn set_overriding_ability(&mut self, overriding_ability: SpellAbility) {
80        self.overriding_ability = Some(overriding_ability);
81        if let Some(ability) = self.overriding_ability.as_mut() {
82            if let Some(host_card_id) = self.card_trait_base.get_host_card_id() {
83                ability.set_host_card_id(host_card_id);
84            }
85            if let Some(keyword) = self.card_trait_base.get_keyword().cloned() {
86                ability.set_keyword(keyword);
87            }
88            ability.set_intrinsic(self.card_trait_base.is_intrinsic());
89        }
90    }
91
92    /// Port of Java `ensureAbility()`.
93    ///
94    /// The Java base type is abstract and concrete trigger/replacement classes
95    /// may lazily populate the overriding ability. In the current Rust port,
96    /// the base owns the resolved ability directly, so `ensure_ability`
97    /// returns the stored ability when present.
98    pub fn ensure_ability(&mut self) -> Option<&mut SpellAbility> {
99        self.overriding_ability.as_mut()
100    }
101
102    pub fn change_text(&mut self) {
103        if !self.card_trait_base.is_intrinsic() {
104            return;
105        }
106        self.card_trait_base.change_text();
107
108        let changed_text_pairs = self.card_trait_base.changed_text_pairs();
109        if let Some(sa) = self.ensure_ability() {
110            sa.apply_text_changes(&changed_text_pairs);
111        }
112    }
113
114    pub fn change_text_intrinsic(
115        &mut self,
116        color_map: HashMap<String, String>,
117        type_map: HashMap<String, String>,
118    ) {
119        self.card_trait_base
120            .change_text_intrinsic(color_map.clone(), type_map.clone());
121
122        if let Some(sa) = self.ensure_ability() {
123            sa.apply_text_changes_intrinsic(&color_map, &type_map);
124        }
125    }
126}