weiss_core/
events.rs

1use crate::db::CardId;
2use crate::state::{
3    AttackType, ChoiceOptionRef, ChoiceReason, DamageModifierKind, DamageType, ModifierDuration,
4    ModifierKind, StackItem, TimingWindow, TriggerEffect,
5};
6use serde::{Deserialize, Serialize};
7
8/// Reason for revealing a card.
9#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
10pub enum RevealReason {
11    /// Reveal was due to a trigger check.
12    TriggerCheck,
13    /// Reveal was due to a damage check.
14    DamageCheck,
15    /// Reveal was due to a refresh penalty.
16    RefreshPenalty,
17    /// Reveal was due to a card being played.
18    Play,
19    /// Reveal was due to an ability or effect.
20    AbilityEffect,
21}
22
23/// Audience allowed to see a revealed card.
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub enum RevealAudience {
26    /// Reveal is visible to all observers.
27    Public,
28    /// Reveal is visible only to the card owner.
29    OwnerOnly,
30    /// Reveal is visible only to the controller of the card/effect.
31    ControllerOnly,
32    /// Reveal is visible to both players.
33    BothPlayers,
34    /// Reveal is visible only in full replay output.
35    ReplayOnly,
36}
37
38/// Reason a trigger was canceled.
39#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
40pub enum TriggerCancelReason {
41    /// Trigger source was invalid or left play before resolution.
42    InvalidSource,
43    /// Trigger was suppressed by a rule/effect.
44    Suppressed,
45}
46
47/// Reason a choice was skipped.
48#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
49pub enum ChoiceSkipReason {
50    /// No valid candidates were available.
51    NoCandidates,
52}
53
54/// Logical zone in the game state.
55#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
56pub enum Zone {
57    /// Deck zone.
58    Deck,
59    /// Hand zone.
60    Hand,
61    /// Waiting room zone.
62    WaitingRoom,
63    /// Clock zone.
64    Clock,
65    /// Level zone.
66    Level,
67    /// Stock zone.
68    Stock,
69    /// Memory zone.
70    Memory,
71    /// Climax zone.
72    Climax,
73    /// Resolution zone.
74    Resolution,
75    /// Stage zone.
76    Stage,
77}
78
79/// Snapshot of a choice option for replay/debugging.
80#[derive(Clone, Debug, Serialize, Deserialize)]
81pub struct ChoiceOptionSnapshot {
82    /// Stable id for the option.
83    pub option_id: u64,
84    /// Reference to the underlying option.
85    pub reference: ChoiceOptionRef,
86}
87
88/// Canonical event stream emitted by the engine.
89#[derive(Clone, Debug, Serialize, Deserialize)]
90pub enum Event {
91    /// Player drew a card.
92    Draw {
93        /// Player index.
94        player: u8,
95        /// Card id drawn.
96        card: CardId,
97    },
98    /// Card was revealed during a damage check (damage resolution).
99    Damage {
100        /// Player receiving damage.
101        player: u8,
102        /// Card id revealed.
103        card: CardId,
104    },
105    /// Damage was canceled.
106    DamageCancel {
107        /// Player receiving damage.
108        player: u8,
109    },
110    /// Damage intent before modifiers/cancel resolution.
111    DamageIntent {
112        /// Stable event id for correlating downstream damage events.
113        event_id: u32,
114        /// Source player (if known).
115        source_player: u8,
116        /// Source stage slot (if known).
117        source_slot: Option<u8>,
118        /// Target player.
119        target: u8,
120        /// Intended damage amount.
121        amount: i32,
122        /// Damage classification (battle vs effect damage).
123        damage_type: DamageType,
124        /// Whether damage can be canceled by revealing a climax.
125        cancelable: bool,
126    },
127    /// A damage modifier was applied.
128    DamageModifierApplied {
129        /// Correlated damage event id.
130        event_id: u32,
131        /// Modifier kind applied.
132        modifier: DamageModifierKind,
133        /// Amount before modifier.
134        before_amount: i32,
135        /// Amount after modifier.
136        after_amount: i32,
137        /// Cancelable flag before modifier.
138        before_cancelable: bool,
139        /// Cancelable flag after modifier.
140        after_cancelable: bool,
141        /// Canceled flag before modifier.
142        before_canceled: bool,
143        /// Canceled flag after modifier.
144        after_canceled: bool,
145    },
146    /// Damage amount/cancel state was modified and finalized.
147    DamageModified {
148        /// Correlated damage event id.
149        event_id: u32,
150        /// Target player.
151        target: u8,
152        /// Original damage amount.
153        original: i32,
154        /// Modified damage amount.
155        modified: i32,
156        /// Whether damage was canceled.
157        canceled: bool,
158        /// Damage classification (battle vs effect damage).
159        damage_type: DamageType,
160    },
161    /// Damage was committed (a damage card moved to clock).
162    DamageCommitted {
163        /// Correlated damage event id.
164        event_id: u32,
165        /// Target player.
166        target: u8,
167        /// Damage card id committed.
168        card: CardId,
169        /// Damage classification (battle vs effect damage).
170        damage_type: DamageType,
171    },
172    /// A battle reversal was committed for a stage slot.
173    ReversalCommitted {
174        /// Player index.
175        player: u8,
176        /// Stage slot index.
177        slot: u8,
178        /// Damage event id which caused the reversal (if applicable).
179        cause_damage_event: Option<u32>,
180    },
181    /// A card was revealed (with reason/audience metadata).
182    Reveal {
183        /// Player index.
184        player: u8,
185        /// Card id revealed.
186        card: CardId,
187        /// Reason for the reveal.
188        reason: RevealReason,
189        /// Audience allowed to see the revealed card.
190        audience: RevealAudience,
191    },
192    /// A trigger was queued for resolution.
193    TriggerQueued {
194        /// Stable trigger id.
195        trigger_id: u32,
196        /// Trigger group id for simultaneous triggers.
197        group_id: u32,
198        /// Player index.
199        player: u8,
200        /// Source card id.
201        source: CardId,
202        /// Trigger effect kind.
203        effect: TriggerEffect,
204    },
205    /// Multiple triggers were grouped for resolution ordering.
206    TriggerGrouped {
207        /// Trigger group id.
208        group_id: u32,
209        /// Trigger ids in the group.
210        trigger_ids: Vec<u32>,
211    },
212    /// Trigger was resolved.
213    TriggerResolved {
214        /// Stable trigger id.
215        trigger_id: u32,
216        /// Player index.
217        player: u8,
218        /// Trigger effect kind.
219        effect: TriggerEffect,
220    },
221    /// Trigger was canceled.
222    TriggerCanceled {
223        /// Stable trigger id.
224        trigger_id: u32,
225        /// Player index.
226        player: u8,
227        /// Cancellation reason.
228        reason: TriggerCancelReason,
229    },
230    /// A timing window was entered.
231    TimingWindowEntered {
232        /// Timing window.
233        window: TimingWindow,
234        /// Active player index.
235        player: u8,
236    },
237    /// Priority was granted to a player within a timing window.
238    PriorityGranted {
239        /// Timing window.
240        window: TimingWindow,
241        /// Player index.
242        player: u8,
243    },
244    /// Priority was passed by a player.
245    PriorityPassed {
246        /// Player index.
247        player: u8,
248        /// Timing window.
249        window: TimingWindow,
250        /// Pass count within the window.
251        pass_count: u8,
252    },
253    /// Stack group was presented for ordering/selection.
254    StackGroupPresented {
255        /// Stable group id.
256        group_id: u32,
257        /// Player index controlling the stack group.
258        controller: u8,
259        /// Items in the stack group.
260        items: Vec<StackItem>,
261    },
262    /// Stack order was chosen for a presented stack group.
263    StackOrderChosen {
264        /// Stable group id.
265        group_id: u32,
266        /// Player index controlling the stack group.
267        controller: u8,
268        /// Stack id chosen.
269        stack_id: u32,
270    },
271    /// A stack item was pushed.
272    StackPushed {
273        /// Stack item pushed.
274        item: StackItem,
275    },
276    /// A stack item was resolved.
277    StackResolved {
278        /// Stack item resolved.
279        item: StackItem,
280    },
281    /// Automatic resolution cap was exceeded.
282    AutoResolveCapExceeded {
283        /// Cap value.
284        cap: u32,
285        /// Stack length at the time of cap exceed.
286        stack_len: u32,
287        /// Timing window (if known).
288        window: Option<TimingWindow>,
289    },
290    /// Timing window advanced.
291    WindowAdvanced {
292        /// Previous timing window.
293        from: TimingWindow,
294        /// Next timing window (or None if leaving timing windows).
295        to: Option<TimingWindow>,
296    },
297    /// A choice was presented to a player.
298    ChoicePresented {
299        /// Stable choice id.
300        choice_id: u32,
301        /// Player index.
302        player: u8,
303        /// Reason for the choice.
304        reason: ChoiceReason,
305        /// Current page options.
306        options: Vec<ChoiceOptionSnapshot>,
307        /// Total candidate count (before paging).
308        total_candidates: u16,
309        /// Page start index.
310        page_start: u16,
311    },
312    /// A choice page was changed.
313    ChoicePageChanged {
314        /// Stable choice id.
315        choice_id: u32,
316        /// Player index.
317        player: u8,
318        /// Previous page start index.
319        from_start: u16,
320        /// New page start index.
321        to_start: u16,
322    },
323    /// A player made a choice selection.
324    ChoiceMade {
325        /// Stable choice id.
326        choice_id: u32,
327        /// Player index.
328        player: u8,
329        /// Reason for the choice.
330        reason: ChoiceReason,
331        /// Selected option reference.
332        option: ChoiceOptionRef,
333    },
334    /// A choice was autopicked by the engine.
335    ChoiceAutopicked {
336        /// Stable choice id.
337        choice_id: u32,
338        /// Player index.
339        player: u8,
340        /// Reason for the choice.
341        reason: ChoiceReason,
342        /// Selected option reference.
343        option: ChoiceOptionRef,
344    },
345    /// A choice was skipped.
346    ChoiceSkipped {
347        /// Stable choice id.
348        choice_id: u32,
349        /// Player index.
350        player: u8,
351        /// Reason for the choice.
352        reason: ChoiceReason,
353        /// Skip reason.
354        skip_reason: ChoiceSkipReason,
355    },
356    /// A card moved between zones.
357    ZoneMove {
358        /// Player index.
359        player: u8,
360        /// Card id moved.
361        card: CardId,
362        /// Source zone.
363        from: Zone,
364        /// Destination zone.
365        to: Zone,
366        /// Source slot (if applicable).
367        from_slot: Option<u8>,
368        /// Destination slot (if applicable).
369        to_slot: Option<u8>,
370    },
371    /// Control of a card changed.
372    ControlChanged {
373        /// Card id whose controller changed.
374        card: CardId,
375        /// Owner player index.
376        owner: u8,
377        /// Previous controller index.
378        from_controller: u8,
379        /// New controller index.
380        to_controller: u8,
381        /// Previous stage slot index.
382        from_slot: u8,
383        /// New stage slot index.
384        to_slot: u8,
385    },
386    /// A modifier was added.
387    ModifierAdded {
388        /// Modifier id.
389        id: u32,
390        /// Source card id.
391        source: CardId,
392        /// Target player index.
393        target_player: u8,
394        /// Target stage slot index.
395        target_slot: u8,
396        /// Target card id.
397        target_card: CardId,
398        /// Modifier kind.
399        kind: ModifierKind,
400        /// Signed modifier magnitude.
401        magnitude: i32,
402        /// Modifier duration.
403        duration: ModifierDuration,
404    },
405    /// A modifier was removed.
406    ModifierRemoved {
407        /// Modifier id.
408        id: u32,
409        /// Removal reason.
410        reason: ModifierRemoveReason,
411    },
412    /// Player conceded the game.
413    Concede {
414        /// Player index.
415        player: u8,
416    },
417    /// Player played a character to a stage slot.
418    Play {
419        /// Player index.
420        player: u8,
421        /// Card id played.
422        card: CardId,
423        /// Stage slot index.
424        slot: u8,
425    },
426    /// Player played an event card.
427    PlayEvent {
428        /// Player index.
429        player: u8,
430        /// Event card id played.
431        card: CardId,
432    },
433    /// Player played a climax card.
434    PlayClimax {
435        /// Player index.
436        player: u8,
437        /// Climax card id played.
438        card: CardId,
439    },
440    /// Trigger step processed a trigger icon.
441    Trigger {
442        /// Player index.
443        player: u8,
444        /// Trigger icon.
445        icon: crate::db::TriggerIcon,
446        /// Triggered card id (if present).
447        card: Option<CardId>,
448    },
449    /// Player declared an attack with a slot.
450    Attack {
451        /// Player index.
452        player: u8,
453        /// Attacker stage slot index.
454        slot: u8,
455    },
456    /// Attack type was chosen (frontal/side/direct).
457    AttackType {
458        /// Player index.
459        player: u8,
460        /// Attacker stage slot index.
461        attacker_slot: u8,
462        /// Declared attack type.
463        attack_type: AttackType,
464    },
465    /// Player played a counter (backup) during counter step.
466    Counter {
467        /// Player index.
468        player: u8,
469        /// Counter card id.
470        card: CardId,
471        /// Power granted by the counter.
472        power: i32,
473    },
474    /// A card was placed into clock (e.g., from hand or damage).
475    Clock {
476        /// Player index.
477        player: u8,
478        /// Card id placed into clock (if known).
479        card: Option<CardId>,
480    },
481    /// A zone was shuffled.
482    Shuffle {
483        /// Player index.
484        player: u8,
485        /// Zone shuffled.
486        zone: Zone,
487    },
488    /// Refresh occurred (deck refilled from waiting room).
489    Refresh {
490        /// Player index.
491        player: u8,
492    },
493    /// Refresh penalty card was applied to clock.
494    RefreshPenalty {
495        /// Player index.
496        player: u8,
497        /// Card id moved to clock as penalty.
498        card: CardId,
499    },
500    /// Level-up choice was made.
501    LevelUpChoice {
502        /// Player index.
503        player: u8,
504        /// Card id selected for level.
505        card: CardId,
506    },
507    /// Encore resolution occurred for a stage slot.
508    Encore {
509        /// Player index.
510        player: u8,
511        /// Stage slot index.
512        slot: u8,
513        /// Whether the character was kept (encored) on stage.
514        kept: bool,
515    },
516    /// Stand phase occurred for a player.
517    Stand {
518        /// Player index.
519        player: u8,
520    },
521    /// Turn ended for a player.
522    EndTurn {
523        /// Player index.
524        player: u8,
525    },
526    /// Terminal game event.
527    Terminal {
528        /// Winner player index, or None for draw/timeout.
529        winner: Option<u8>,
530    },
531}
532
533/// Reason a modifier was removed.
534#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
535pub enum ModifierRemoveReason {
536    /// Removed during end-phase cleanup.
537    EndOfTurn,
538    /// Removed because the target left stage.
539    TargetLeftStage,
540    /// Removed due to continuous refresh/recompute of continuous effects.
541    ContinuousRefresh,
542}