Skip to main content

manabrew_engine/trigger/
trigger_token_created_once.rs

1use serde::{Deserialize, Serialize};
2
3use crate::ability::AbilityKey;
4use crate::event::{AbilityValue, RunParams};
5use crate::game::GameState;
6use crate::parsing::{keys, Params};
7use crate::spellability::SpellAbility;
8use crate::trigger::TriggerType;
9
10use super::trigger::{Trigger, TriggerBehavior};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct TriggerTokenCreatedOnce {
14    pub valid_card: Option<crate::parsing::CompiledSelector>,
15    pub only_first: Option<crate::parsing::CompiledSelector>,
16}
17
18impl TriggerTokenCreatedOnce {
19    pub fn parse(params: &Params) -> Box<dyn TriggerBehavior> {
20        let valid_card = params.selector_cloned_any(&["ValidToken", keys::VALID_CARD]);
21        let only_first = params.selector_cloned("OnlyFirst");
22        Box::new(Self {
23            valid_card,
24            only_first,
25        })
26    }
27}
28
29#[typetag::serde]
30impl TriggerBehavior for TriggerTokenCreatedOnce {
31    fn trigger_type(&self) -> TriggerType {
32        TriggerType::TokenCreatedOnce
33    }
34
35    fn perform_test(&self, trigger: &Trigger, params: &RunParams, game: &GameState) -> bool {
36        let Some(AbilityValue::Cards(cards)) = params.get_value(AbilityKey::Cards) else {
37            return false;
38        };
39        let any_match = cards.iter().any(|&card_id| {
40            trigger.matches_optional_valid_card_filter(&self.valid_card, Some(card_id), game)
41        });
42        if !any_match {
43            return false;
44        }
45        if let Some(filter) = self.only_first.as_ref() {
46            let Some(AbilityValue::Players(players)) = params.get_value(AbilityKey::FirstTime)
47            else {
48                return false;
49            };
50            if !players
51                .iter()
52                .copied()
53                .any(|pid| trigger.matches_valid_player_filter(filter, pid, game))
54            {
55                return false;
56            }
57        }
58        true
59    }
60
61    fn set_triggering_objects(
62        &self,
63        _trigger: &Trigger,
64        sa: &mut SpellAbility,
65        params: &RunParams,
66        _game: &GameState,
67    ) {
68        // TODO: port ValidToken filtering from Java (IterableUtil.filter with CardPredicates.restriction)
69        if let Some(cards) = params.cards.as_ref() {
70            let csv = cards
71                .iter()
72                .map(|c| c.0.to_string())
73                .collect::<Vec<_>>()
74                .join(",");
75            sa.set_triggering_object(crate::ability::AbilityKey::Cards, &csv);
76        }
77    }
78
79    fn get_important_stack_objects(&self, _trigger: &Trigger, _sa: &SpellAbility) -> String {
80        String::new()
81    }
82}