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            ability.set_host_card_id(self.card_trait_base.host_card_id());
83            if let Some(keyword) = self.card_trait_base.get_keyword().cloned() {
84                ability.set_keyword(keyword);
85            }
86            ability.set_intrinsic(self.card_trait_base.is_intrinsic());
87        }
88    }
89
90    /// Port of Java `ensureAbility()`.
91    ///
92    /// The Java base type is abstract and concrete trigger/replacement classes
93    /// may lazily populate the overriding ability. In the current Rust port,
94    /// the base owns the resolved ability directly, so `ensure_ability`
95    /// returns the stored ability when present.
96    pub fn ensure_ability(&mut self) -> Option<&mut SpellAbility> {
97        self.overriding_ability.as_mut()
98    }
99
100    pub fn change_text(&mut self) {
101        if !self.card_trait_base.is_intrinsic() {
102            return;
103        }
104        self.card_trait_base.change_text();
105
106        let changed_text_pairs = self.card_trait_base.changed_text_pairs();
107        if let Some(sa) = self.ensure_ability() {
108            sa.apply_text_changes(&changed_text_pairs);
109        }
110    }
111
112    pub fn change_text_intrinsic(
113        &mut self,
114        color_map: HashMap<String, String>,
115        type_map: HashMap<String, String>,
116    ) {
117        self.card_trait_base
118            .change_text_intrinsic(color_map.clone(), type_map.clone());
119
120        if let Some(sa) = self.ensure_ability() {
121            sa.apply_text_changes_intrinsic(&color_map, &type_map);
122        }
123    }
124}