Skip to main content

manabrew_engine/player/
commander.rs

1use forge_foundation::ZoneType;
2
3use crate::game::GameState;
4use crate::ids::{CardId, PlayerId};
5use crate::player::player_factory_util::{
6    add_replacement_effect, add_static_ability, new_player_effect_card,
7};
8use crate::trigger::handler::TriggerHandler;
9
10impl GameState {
11    pub fn player_register_commander(&mut self, player: PlayerId, commander: CardId) {
12        if !self.player(player).commanders.contains(&commander) {
13            self.player_mut(player).commanders.push(commander);
14        }
15        self.card_mut(commander).is_commander = true;
16        self.card_mut(commander).move_to_command_zone = false;
17        let count = self
18            .player(player)
19            .commander_casts
20            .get(&commander.0)
21            .copied()
22            .unwrap_or(0);
23        self.card_mut(commander).commander_cast_count = count;
24    }
25
26    pub fn player_remove_commander(&mut self, player: PlayerId, commander: CardId) {
27        self.player_mut(player)
28            .commanders
29            .retain(|&id| id != commander);
30        self.player_mut(player).commander_casts.remove(&commander.0);
31        self.player_mut(player)
32            .commander_damage_received
33            .remove(&commander.0);
34        self.card_mut(commander).is_commander = false;
35        self.card_mut(commander).move_to_command_zone = false;
36        self.card_mut(commander).commander_cast_count = 0;
37    }
38
39    pub fn player_reset_commander_state(&mut self, player: PlayerId) {
40        let commanders = self.player(player).commanders.clone();
41        for commander in commanders {
42            self.card_mut(commander).is_commander = false;
43            self.card_mut(commander).move_to_command_zone = false;
44            self.card_mut(commander).commander_cast_count = 0;
45        }
46        self.player_mut(player).commanders.clear();
47        self.player_mut(player).commander_casts.clear();
48        self.player_mut(player).commander_damage_received.clear();
49        self.player_remove_commander_effect(player);
50    }
51
52    pub fn player_registered_commanders(&self, player: PlayerId) -> &[CardId] {
53        &self.player(player).commanders
54    }
55
56    pub fn player_is_commander(&self, player: PlayerId, card: CardId) -> bool {
57        self.player(player).commanders.contains(&card) || self.card(card).is_commander
58    }
59
60    pub fn player_commander_cast_count(&self, player: PlayerId, commander: CardId) -> u32 {
61        if let Some(v) = self.player(player).commander_casts.get(&commander.0) {
62            *v
63        } else {
64            self.card(commander).commander_cast_count
65        }
66    }
67
68    pub fn player_commander_tax(&self, player: PlayerId, commander: CardId) -> i32 {
69        (self.player_commander_cast_count(player, commander) as i32) * 2
70    }
71
72    pub fn player_total_commander_casts(&self, player: PlayerId) -> u32 {
73        self.player(player).commander_casts.values().copied().sum()
74    }
75
76    pub fn player_increment_commander_cast(&mut self, player: PlayerId, commander: CardId) {
77        let next = self.player_commander_cast_count(player, commander) + 1;
78        self.player_mut(player)
79            .commander_casts
80            .insert(commander.0, next);
81        self.card_mut(commander).commander_cast_count = next;
82    }
83
84    pub fn player_commander_color_identity(&self, player: PlayerId) -> Vec<String> {
85        let mut colors = Vec::new();
86        for &commander in self.player_registered_commanders(player) {
87            for color in self.card(commander).color_identity.iter() {
88                let name = color.long_name();
89                let mut chars = name.chars();
90                let Some(first) = chars.next() else {
91                    continue;
92                };
93                let formatted = first.to_uppercase().collect::<String>() + chars.as_str();
94                if !colors.contains(&formatted) {
95                    colors.push(formatted);
96                }
97            }
98        }
99        colors
100    }
101
102    pub fn player_create_commander_effect(
103        &mut self,
104        player: PlayerId,
105        _trigger_handler: Option<&mut TriggerHandler>,
106    ) -> Option<CardId> {
107        if self.player(player).commanders.is_empty() {
108            self.player_remove_commander_effect(player);
109            return None;
110        }
111        if let Some(effect_id) = self.player(player).commander_effect_card {
112            return Some(effect_id);
113        }
114
115        let mut effect = new_player_effect_card(player, "Commander Effect", None);
116        add_replacement_effect(
117            &mut effect,
118            "R$ Event$ Moved | ActiveZones$ Command | ValidCard$ Card.IsCommander+YouOwn | Secondary$ True | Optional$ True | OptionalDecider$ You | CommanderMoveReplacement$ True | Destination$ Hand,Library | NewDestination$ Command | Description$ If a commander would be put into its owner's hand or library from anywhere, its owner may put it into the command zone instead.",
119        );
120        add_static_ability(
121            &mut effect,
122            "S$ Mode$ Continuous | EffectZone$ Command | MayPlay$ True | Affected$ Card.IsCommander+YouOwn | AffectedZone$ Command",
123        );
124
125        let effect_id = self.create_card(effect);
126        self.move_card(effect_id, ZoneType::Command, player);
127        self.player_mut(player).commander_effect_card = Some(effect_id);
128        Some(effect_id)
129    }
130
131    pub fn player_set_commander_replacement_suppressed(
132        &mut self,
133        player: PlayerId,
134        suppressed: bool,
135    ) {
136        let Some(effect_id) = self.player(player).commander_effect_card else {
137            return;
138        };
139        for replacement in &mut self.card_mut(effect_id).replacement_effects {
140            replacement.suppressed = suppressed;
141        }
142    }
143
144    pub fn player_remove_commander_effect(&mut self, player: PlayerId) {
145        let Some(effect_id) = self.player(player).commander_effect_card else {
146            return;
147        };
148        if self.card(effect_id).zone == ZoneType::Command {
149            self.remove_card_from_zone(ZoneType::Command, player, effect_id);
150        }
151        self.card_mut(effect_id).zone = ZoneType::None;
152        self.player_mut(player).commander_effect_card = None;
153    }
154
155    pub fn initialize_player_commanders_from_registered(
156        &mut self,
157        player: PlayerId,
158        registered: &crate::player::RegisteredPlayer,
159        trigger_handler: Option<&mut TriggerHandler>,
160    ) {
161        self.player_reset_commander_state(player);
162        if registered.commanders.is_empty() {
163            return;
164        }
165        let command_cards: Vec<CardId> = self.cards_in_zone(ZoneType::Command, player).to_vec();
166        for commander_name in &registered.commanders {
167            if let Some(card_id) = command_cards
168                .iter()
169                .copied()
170                .find(|&cid| self.card(cid).card_name == *commander_name)
171            {
172                self.player_register_commander(player, card_id);
173            }
174        }
175        self.player_create_commander_effect(player, trigger_handler);
176    }
177}