Skip to main content

manabrew_engine/trigger/
trigger_counter_added_all.rs

1use serde::{Deserialize, Serialize};
2
3use crate::event::RunParams;
4use crate::game::GameState;
5use crate::spellability::SpellAbility;
6use crate::trigger::TriggerType;
7
8use super::trigger::TriggerBehavior;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct TriggerCounterAddedAll {
12    pub counter_type: Option<String>,
13    pub valid: Option<crate::parsing::CompiledSelector>,
14    pub valid_source: Option<crate::parsing::CompiledSelector>,
15}
16
17impl TriggerCounterAddedAll {
18    pub fn parse(
19        counter_type: Option<String>,
20        valid: Option<crate::parsing::CompiledSelector>,
21        valid_source: Option<crate::parsing::CompiledSelector>,
22    ) -> Box<dyn TriggerBehavior> {
23        Box::new(Self {
24            counter_type,
25            valid,
26            valid_source,
27        })
28    }
29}
30
31#[typetag::serde]
32impl TriggerBehavior for TriggerCounterAddedAll {
33    fn trigger_type(&self) -> TriggerType {
34        TriggerType::CounterAddedAll
35    }
36
37    fn perform_test(
38        &self,
39        trigger: &super::trigger::Trigger,
40        params: &RunParams,
41        game: &GameState,
42    ) -> bool {
43        matching_entries(self, trigger, params, game)
44            .next()
45            .is_some()
46    }
47
48    fn set_triggering_objects(
49        &self,
50        _trigger: &super::trigger::Trigger,
51        sa: &mut SpellAbility,
52        params: &RunParams,
53        game: &GameState,
54    ) {
55        let entries: Vec<_> = matching_entries(self, _trigger, params, game).collect();
56        let mut cards = Vec::new();
57        let mut players = Vec::new();
58        let mut amount = 0;
59        for entry in entries {
60            if let Some(card) = entry.object_card {
61                if !cards.contains(&card) {
62                    cards.push(card);
63                }
64            }
65            if let Some(player) = entry.object_player {
66                if !players.contains(&player) {
67                    players.push(player);
68                }
69            }
70            amount += entry
71                .counters
72                .iter()
73                .filter(|(counter_type, _)| {
74                    self.counter_type
75                        .as_ref()
76                        .is_none_or(|expected| expected.eq_ignore_ascii_case(counter_type))
77                })
78                .map(|(_, amount)| *amount)
79                .sum::<i32>();
80        }
81        let objects = cards
82            .into_iter()
83            .map(crate::agent::GameEntity::Card)
84            .chain(players.into_iter().map(crate::agent::GameEntity::Player))
85            .collect();
86        sa.set_triggering_value(
87            crate::ability::AbilityKey::Objects,
88            crate::event::AbilityValue::GameEntities(objects),
89        );
90        sa.set_triggering_object(crate::ability::AbilityKey::Amount, amount.to_string());
91    }
92
93    fn get_important_stack_objects(
94        &self,
95        _trigger: &super::trigger::Trigger,
96        sa: &SpellAbility,
97    ) -> String {
98        format!(
99            "Amount: {}",
100            sa.trigger_objects
101                .get(&crate::ability::AbilityKey::Amount)
102                .cloned()
103                .unwrap_or_default()
104        )
105    }
106}
107
108fn matching_entries<'a>(
109    behavior: &'a TriggerCounterAddedAll,
110    trigger: &'a super::trigger::Trigger,
111    params: &'a RunParams,
112    game: &'a GameState,
113) -> impl Iterator<Item = &'a crate::event::CounterTableEntry> {
114    params
115        .counter_table
116        .as_deref()
117        .unwrap_or_default()
118        .iter()
119        .filter(move |entry| {
120            if let Some(filter) = &behavior.valid_source {
121                let Some(source) = entry.source else {
122                    return false;
123                };
124                if !trigger.matches_valid_player_filter(filter, source, game) {
125                    return false;
126                }
127            }
128            if let Some(filter) = &behavior.valid {
129                let matches = entry
130                    .object_card
131                    .is_some_and(|card| trigger.matches_valid_card_filter(filter, card, game))
132                    || entry.object_player.is_some_and(|player| {
133                        trigger.matches_valid_player_filter(filter, player, game)
134                    });
135                if !matches {
136                    return false;
137                }
138            }
139            entry.counters.iter().any(|(counter_type, amount)| {
140                *amount > 0
141                    && behavior
142                        .counter_type
143                        .as_ref()
144                        .is_none_or(|expected| expected.eq_ignore_ascii_case(counter_type))
145            })
146        })
147}