weiss_core/
effects.rs

1use serde::{Deserialize, Serialize};
2
3use crate::db::{CardId, TriggerIcon};
4use crate::events::RevealAudience;
5use crate::state::{
6    DamageType, ModifierDuration, ModifierKind, TargetSide, TargetSpec, TargetZone,
7};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum EffectSourceKind {
11    Trigger,
12    Auto,
13    Activated,
14    Continuous,
15    EventPlay,
16    Counter,
17    Replacement,
18    System,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
22pub struct EffectId {
23    pub source_kind: EffectSourceKind,
24    pub source_card: CardId,
25    pub ability_index: u8,
26    pub effect_index: u8,
27}
28
29impl EffectId {
30    pub fn new(
31        source_kind: EffectSourceKind,
32        source_card: CardId,
33        ability_index: u8,
34        effect_index: u8,
35    ) -> Self {
36        Self {
37            source_kind,
38            source_card,
39            ability_index,
40            effect_index,
41        }
42    }
43}
44
45#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
46pub struct EffectSpec {
47    pub id: EffectId,
48    pub kind: EffectKind,
49    pub target: Option<TargetSpec>,
50    pub optional: bool,
51}
52
53#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
54pub enum EffectKind {
55    Draw {
56        count: u8,
57    },
58    Damage {
59        amount: i32,
60        cancelable: bool,
61        damage_type: DamageType,
62    },
63    AddModifier {
64        kind: ModifierKind,
65        magnitude: i32,
66        duration: ModifierDuration,
67    },
68    MoveToHand,
69    MoveToWaitingRoom,
70    MoveToStock,
71    MoveToClock,
72    Heal,
73    RestTarget,
74    StandTarget,
75    StockCharge {
76        count: u8,
77    },
78    MillTop {
79        target: TargetSide,
80        count: u8,
81    },
82    MoveStageSlot {
83        slot: u8,
84    },
85    SwapStageSlots,
86    RandomDiscardFromHand {
87        target: TargetSide,
88        count: u8,
89    },
90    RandomMill {
91        target: TargetSide,
92        count: u8,
93    },
94    RevealZoneTop {
95        target: TargetSide,
96        zone: TargetZone,
97        count: u8,
98        audience: RevealAudience,
99    },
100    MoveTriggerCardToHand,
101    ChangeController {
102        new_controller: TargetSide,
103    },
104    Standby {
105        target_slot: u8,
106    },
107    TreasureStock {
108        take_stock: bool,
109    },
110    ModifyPendingAttackDamage {
111        delta: i32,
112    },
113    TriggerIcon {
114        icon: TriggerIcon,
115    },
116    RevealDeckTop {
117        count: u8,
118        audience: RevealAudience,
119    },
120    CounterBackup {
121        power: i32,
122    },
123    CounterDamageReduce {
124        amount: u8,
125    },
126    CounterDamageCancel,
127}
128
129impl EffectKind {
130    pub fn expects_target(&self) -> bool {
131        matches!(
132            self,
133            EffectKind::AddModifier { .. }
134                | EffectKind::MoveToHand
135                | EffectKind::MoveToWaitingRoom
136                | EffectKind::MoveToStock
137                | EffectKind::MoveToClock
138                | EffectKind::Heal
139                | EffectKind::RestTarget
140                | EffectKind::StandTarget
141                | EffectKind::MoveStageSlot { .. }
142                | EffectKind::SwapStageSlots
143                | EffectKind::ChangeController { .. }
144                | EffectKind::Standby { .. }
145        )
146    }
147
148    pub fn requires_target_zone(&self, zone: TargetZone) -> bool {
149        match self {
150            EffectKind::MoveToHand => {
151                matches!(
152                    zone,
153                    TargetZone::Stage | TargetZone::WaitingRoom | TargetZone::DeckTop
154                )
155            }
156            EffectKind::MoveToWaitingRoom => matches!(
157                zone,
158                TargetZone::Stage
159                    | TargetZone::Hand
160                    | TargetZone::DeckTop
161                    | TargetZone::Clock
162                    | TargetZone::Level
163                    | TargetZone::Stock
164                    | TargetZone::Memory
165                    | TargetZone::Climax
166                    | TargetZone::Resolution
167                    | TargetZone::WaitingRoom
168            ),
169            EffectKind::MoveToStock => matches!(
170                zone,
171                TargetZone::Stage
172                    | TargetZone::Hand
173                    | TargetZone::DeckTop
174                    | TargetZone::Clock
175                    | TargetZone::Level
176                    | TargetZone::WaitingRoom
177                    | TargetZone::Memory
178                    | TargetZone::Climax
179                    | TargetZone::Resolution
180                    | TargetZone::Stock
181            ),
182            EffectKind::MoveToClock => matches!(
183                zone,
184                TargetZone::Stage
185                    | TargetZone::Hand
186                    | TargetZone::DeckTop
187                    | TargetZone::WaitingRoom
188                    | TargetZone::Resolution
189                    | TargetZone::Clock
190            ),
191            EffectKind::Heal => matches!(zone, TargetZone::Clock),
192            EffectKind::ChangeController { .. } => matches!(zone, TargetZone::Stage),
193            EffectKind::AddModifier { .. } => matches!(zone, TargetZone::Stage),
194            EffectKind::RestTarget
195            | EffectKind::StandTarget
196            | EffectKind::MoveStageSlot { .. }
197            | EffectKind::SwapStageSlots => matches!(zone, TargetZone::Stage),
198            EffectKind::Standby { .. } => matches!(zone, TargetZone::WaitingRoom),
199            EffectKind::RandomDiscardFromHand { .. } => matches!(zone, TargetZone::Hand),
200            EffectKind::RandomMill { .. } => matches!(zone, TargetZone::DeckTop),
201            EffectKind::RevealZoneTop {
202                zone: reveal_zone, ..
203            } => zone == *reveal_zone,
204            _ => true,
205        }
206    }
207}
208
209#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
210pub struct EffectPayload {
211    pub spec: EffectSpec,
212    pub targets: Vec<crate::state::TargetRef>,
213}
214
215#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
216pub enum ReplacementHook {
217    Damage,
218}
219
220#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
221pub enum ReplacementKind {
222    CancelDamage,
223    RedirectDamage { new_target: TargetSide },
224}
225
226#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
227pub struct ReplacementSpec {
228    pub id: EffectId,
229    pub source: CardId,
230    pub hook: ReplacementHook,
231    pub kind: ReplacementKind,
232    pub priority: i16,
233    pub insertion: u32,
234}