manabrew_engine/trigger/
trigger_counter_added_all.rs1use 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}