1use crate::db::CardId;
2use crate::effects::{EffectId, EffectPayload, ReplacementSpec};
3use crate::util::Rng64;
4use serde::{Deserialize, Serialize};
5use std::collections::VecDeque;
6
7pub type CardInstanceId = u32;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct CardInstance {
11 pub id: CardId,
12 pub instance_id: CardInstanceId,
13 pub owner: u8,
14 pub controller: u8,
15}
16
17pub const REVEAL_HISTORY_LEN: usize = 8;
18
19#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
20pub struct RevealHistory {
21 entries: [CardId; REVEAL_HISTORY_LEN],
22 len: u8,
23 head: u8,
24}
25
26impl RevealHistory {
27 pub fn new() -> Self {
28 Self {
29 entries: [0; REVEAL_HISTORY_LEN],
30 len: 0,
31 head: 0,
32 }
33 }
34
35 pub fn push(&mut self, card: CardId) {
36 if REVEAL_HISTORY_LEN == 0 {
37 return;
38 }
39 let head = self.head as usize;
40 self.entries[head] = card;
41 if (self.len as usize) < REVEAL_HISTORY_LEN {
42 self.len = self.len.saturating_add(1);
43 }
44 self.head = ((head + 1) % REVEAL_HISTORY_LEN) as u8;
45 }
46
47 pub fn write_chronological(&self, out: &mut [i32]) {
48 out.fill(0);
49 let len = self.len as usize;
50 if len == 0 || REVEAL_HISTORY_LEN == 0 {
51 return;
52 }
53 let start = if len < REVEAL_HISTORY_LEN {
54 0
55 } else {
56 self.head as usize
57 };
58 for idx in 0..len.min(out.len()) {
59 let entry_idx = if len < REVEAL_HISTORY_LEN {
60 idx
61 } else {
62 (start + idx) % REVEAL_HISTORY_LEN
63 };
64 out[idx] = self.entries[entry_idx] as i32;
65 }
66 }
67}
68
69impl Default for RevealHistory {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl CardInstance {
76 pub fn new(id: CardId, owner: u8, instance_id: CardInstanceId) -> Self {
77 Self {
78 id,
79 instance_id,
80 owner,
81 controller: owner,
82 }
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
87pub enum Phase {
88 Mulligan,
89 Stand,
90 Draw,
91 Clock,
92 Main,
93 Climax,
94 Attack,
95 End,
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
99pub enum TimingWindow {
100 MainWindow,
101 ClimaxWindow,
102 AttackDeclarationWindow,
103 TriggerResolutionWindow,
104 CounterWindow,
105 DamageResolutionWindow,
106 EncoreWindow,
107 EndPhaseWindow,
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
111pub enum StageStatus {
112 Stand,
113 Rest,
114 Reverse,
115}
116
117#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
118pub struct StageSlot {
119 pub card: Option<CardInstance>,
120 pub status: StageStatus,
121 pub power_mod_battle: i32,
122 pub power_mod_turn: i32,
123 pub has_attacked: bool,
124 pub cannot_attack: bool,
125 pub attack_cost: u8,
126}
127
128impl StageSlot {
129 pub fn empty() -> Self {
130 Self {
131 card: None,
132 status: StageStatus::Stand,
133 power_mod_battle: 0,
134 power_mod_turn: 0,
135 has_attacked: false,
136 cannot_attack: false,
137 attack_cost: 0,
138 }
139 }
140
141 pub fn is_empty(&self) -> bool {
142 self.card.is_none()
143 }
144}
145
146#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
147pub enum AttackType {
148 Frontal,
149 Side,
150 Direct,
151}
152
153#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
154pub enum AttackStep {
155 Trigger,
156 Counter,
157 Damage,
158 Battle,
159 Encore,
160}
161
162#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
163pub enum DamageType {
164 Battle,
165 Effect,
166}
167
168#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
169pub enum DamageModifierKind {
170 AddAmount { delta: i32 },
171 SetCancelable { cancelable: bool },
172 CancelNext,
173 SetAmount { amount: i32 },
174}
175
176#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
177pub struct DamageModifier {
178 pub kind: DamageModifierKind,
179 pub priority: i16,
180 pub insertion: u32,
181 pub source_id: u32,
182 pub remaining: i32,
183 pub used: bool,
184}
185
186#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
187pub enum TriggerEffect {
188 Soul,
189 Draw,
190 Shot,
191 Bounce,
192 Treasure,
193 Gate,
194 Standby,
195 AutoAbility { ability_index: u8 },
196}
197
198#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
199pub enum TargetZone {
200 Stage,
201 Hand,
202 DeckTop,
203 Clock,
204 Level,
205 Stock,
206 Memory,
207 WaitingRoom,
208 Climax,
209 Resolution,
210}
211
212#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
213pub enum TargetSide {
214 SelfSide,
215 Opponent,
216}
217
218#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
219pub enum TargetSlotFilter {
220 Any,
221 FrontRow,
222 BackRow,
223 SpecificSlot(u8),
224}
225
226#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
227pub struct TargetSpec {
228 pub zone: TargetZone,
229 pub side: TargetSide,
230 pub slot_filter: TargetSlotFilter,
231 pub card_type: Option<crate::db::CardType>,
232 #[serde(default)]
233 pub card_trait: Option<u16>,
234 #[serde(default)]
235 pub level_max: Option<u8>,
236 #[serde(default)]
237 pub cost_max: Option<u8>,
238 pub count: u8,
239 #[serde(default)]
240 pub limit: Option<u8>,
241 #[serde(default)]
242 pub source_only: bool,
243 #[serde(default)]
244 pub reveal_to_controller: bool,
245}
246
247#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
248pub struct TargetRef {
249 pub player: u8,
250 pub zone: TargetZone,
251 pub index: u8,
252 pub card_id: CardId,
253 pub instance_id: CardInstanceId,
254}
255
256#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
257pub enum PendingTargetEffect {
258 EffectPending {
259 instance_id: u32,
260 payload: EffectPayload,
261 },
262}
263
264#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
265pub struct TargetSelectionState {
266 pub controller: u8,
267 pub source_id: CardId,
268 pub spec: TargetSpec,
269 pub remaining: u8,
270 pub selected: Vec<TargetRef>,
271 #[serde(default)]
272 pub candidates: Vec<TargetRef>,
273 pub effect: PendingTargetEffect,
274 pub allow_skip: bool,
275}
276
277#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
278pub struct StackItem {
279 pub id: u32,
280 pub controller: u8,
281 pub source_id: CardId,
282 pub effect_id: EffectId,
283 pub payload: EffectPayload,
284}
285
286#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
287pub struct PriorityState {
288 pub holder: u8,
289 pub passes: u8,
290 pub window: TimingWindow,
291 pub used_act_mask: u32,
292}
293
294#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
295pub struct StackOrderState {
296 pub group_id: u32,
297 pub controller: u8,
298 pub items: Vec<StackItem>,
299}
300
301#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
302pub enum ChoiceReason {
303 TriggerStandbySelect,
304 TriggerTreasureSelect,
305 StackOrderSelect,
306 PriorityActionSelect,
307 CostPayment,
308 TargetSelect,
309 EndPhaseDiscard,
310}
311
312#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
313pub enum CostStepKind {
314 RestOther,
315 DiscardFromHand,
316 ClockFromHand,
317 ClockFromDeckTop,
318 RevealFromHand,
319}
320
321#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
322pub struct CostPaymentState {
323 pub controller: u8,
324 pub source_id: CardId,
325 pub source_instance_id: CardInstanceId,
326 pub source_slot: Option<u8>,
327 pub ability_index: u8,
328 pub remaining: crate::db::AbilityCost,
329 pub current_step: Option<CostStepKind>,
330}
331
332#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
333pub enum ChoiceZone {
334 WaitingRoom,
335 Stage,
336 Hand,
337 DeckTop,
338 Clock,
339 Level,
340 Stock,
341 Memory,
342 Climax,
343 Resolution,
344 Stack,
345 PriorityCounter,
346 PriorityAct,
347 PriorityPass,
348 Skip,
349}
350
351#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
352pub struct ChoiceOptionRef {
353 pub card_id: CardId,
354 pub instance_id: CardInstanceId,
355 pub zone: ChoiceZone,
356 pub index: Option<u8>,
357 pub target_slot: Option<u8>,
358}
359
360#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
361pub struct ChoiceState {
362 pub id: u32,
363 pub reason: ChoiceReason,
364 pub player: u8,
365 pub options: Vec<ChoiceOptionRef>,
366 pub total_candidates: u16,
367 pub page_start: u16,
368 pub pending_trigger: Option<PendingTrigger>,
369}
370
371#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
372pub struct AttackContext {
373 pub attacker_slot: u8,
374 pub defender_slot: Option<u8>,
375 pub attack_type: AttackType,
376 pub trigger_card: Option<CardId>,
377 pub trigger_instance_id: Option<CardInstanceId>,
378 pub damage: i32,
379 pub counter_allowed: bool,
380 pub counter_played: bool,
381 pub counter_power: i32,
382 pub damage_modifiers: Vec<DamageModifier>,
383 pub next_modifier_id: u32,
384 pub last_damage_event_id: Option<u32>,
385 pub auto_damage_enqueued: bool,
386 pub battle_damage_applied: bool,
387 pub step: AttackStep,
388 pub decl_window_done: bool,
389 pub trigger_window_done: bool,
390 pub damage_window_done: bool,
391}
392
393#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
394pub struct PendingTrigger {
395 pub id: u32,
396 pub group_id: u32,
397 pub player: u8,
398 pub source_card: CardId,
399 pub effect: TriggerEffect,
400 pub effect_id: Option<EffectId>,
401}
402
403#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
404pub struct TriggerOrderState {
405 pub group_id: u32,
406 pub player: u8,
407 pub choices: Vec<u32>,
408}
409
410#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
411pub struct DerivedAttackSlot {
412 pub cannot_attack: bool,
413 pub attack_cost: u8,
414}
415
416impl DerivedAttackSlot {
417 pub fn empty() -> Self {
418 Self {
419 cannot_attack: false,
420 attack_cost: 0,
421 }
422 }
423}
424
425#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
426pub struct DerivedAttackState {
427 pub per_player: [[DerivedAttackSlot; 5]; 2],
428}
429
430impl DerivedAttackState {
431 pub fn new() -> Self {
432 Self {
433 per_player: [[DerivedAttackSlot::empty(); 5]; 2],
434 }
435 }
436}
437
438impl Default for DerivedAttackState {
439 fn default() -> Self {
440 Self::new()
441 }
442}
443
444#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
445pub struct EncoreRequest {
446 pub player: u8,
447 pub slot: u8,
448}
449
450#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
451pub enum TerminalResult {
452 Win { winner: u8 },
453 Draw,
454 Timeout,
455}
456
457#[derive(Clone, Debug, Hash)]
458pub struct PlayerState {
459 pub deck: Vec<CardInstance>,
460 pub hand: Vec<CardInstance>,
461 pub waiting_room: Vec<CardInstance>,
462 pub clock: Vec<CardInstance>,
463 pub level: Vec<CardInstance>,
464 pub stock: Vec<CardInstance>,
465 pub memory: Vec<CardInstance>,
466 pub climax: Vec<CardInstance>,
467 pub resolution: Vec<CardInstance>,
468 pub stage: [StageSlot; 5],
469}
470
471impl PlayerState {
472 pub fn new(deck: Vec<CardInstance>) -> Self {
473 Self {
474 deck,
475 hand: Vec::new(),
476 waiting_room: Vec::new(),
477 clock: Vec::new(),
478 level: Vec::new(),
479 stock: Vec::new(),
480 memory: Vec::new(),
481 climax: Vec::new(),
482 resolution: Vec::new(),
483 stage: [
484 StageSlot::empty(),
485 StageSlot::empty(),
486 StageSlot::empty(),
487 StageSlot::empty(),
488 StageSlot::empty(),
489 ],
490 }
491 }
492}
493
494#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
495pub enum ModifierKind {
496 Power,
497 AttackCost,
498 CannotAttack,
499}
500
501#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
502pub enum ModifierDuration {
503 UntilEndOfTurn,
504 WhileOnStage,
505}
506
507#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
508pub enum ModifierLayer {
509 Continuous,
510 #[default]
511 Effect,
512}
513
514#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
515pub struct ModifierInstance {
516 pub id: u32,
517 pub source: CardId,
518 #[serde(default)]
519 pub source_slot: Option<u8>,
520 pub target_player: u8,
521 pub target_slot: u8,
522 pub target_card: CardId,
523 pub kind: ModifierKind,
524 pub magnitude: i32,
525 pub duration: ModifierDuration,
526 #[serde(default)]
527 pub layer: ModifierLayer,
528 pub insertion: u32,
529}
530
531#[derive(Clone, Debug, Hash)]
532pub struct TurnState {
533 pub active_player: u8,
534 pub starting_player: u8,
535 pub turn_number: u32,
536 pub phase: Phase,
537 pub mulligan_done: [bool; 2],
538 pub mulligan_selected: [u64; 2],
539 pub main_passed: bool,
540 pub decision_count: u32,
541 pub tick_count: u32,
542 pub attack: Option<AttackContext>,
543 pub attack_subphase_count: u8,
544 pub pending_level_up: Option<u8>,
545 pub encore_queue: Vec<EncoreRequest>,
546 pub encore_step_player: Option<u8>,
547 pub pending_triggers: Vec<PendingTrigger>,
548 pub pending_triggers_sorted: bool,
549 pub active_window: Option<TimingWindow>,
550 pub end_phase_window_done: bool,
551 pub end_phase_discard_done: bool,
552 pub end_phase_climax_done: bool,
553 pub end_phase_cleanup_done: bool,
554 pub encore_window_done: bool,
555 pub pending_losses: [bool; 2],
556 pub damage_resolution_target: Option<u8>,
557 pub cost_payment_depth: u8,
558 pub pending_resolution_cleanup: Vec<(u8, CardInstanceId)>,
559 pub phase_step: u8,
560 pub attack_phase_begin_done: bool,
561 pub attack_decl_check_done: bool,
562 pub encore_begin_done: bool,
563 pub trigger_order: Option<TriggerOrderState>,
564 pub choice: Option<ChoiceState>,
565 pub target_selection: Option<TargetSelectionState>,
566 pub pending_cost: Option<CostPaymentState>,
567 pub priority: Option<PriorityState>,
568 pub stack: Vec<StackItem>,
569 pub pending_stack_groups: VecDeque<StackOrderState>,
570 pub stack_order: Option<StackOrderState>,
571 pub derived_attack: Option<DerivedAttackState>,
572 pub next_trigger_id: u32,
573 pub next_trigger_group_id: u32,
574 pub next_choice_id: u32,
575 pub next_stack_group_id: u32,
576 pub next_damage_event_id: u32,
577 pub next_effect_instance_id: u32,
578 pub end_phase_pending: bool,
579}
580
581#[derive(Clone, Debug, Hash)]
582pub struct GameState {
583 pub players: [PlayerState; 2],
584 pub reveal_history: [RevealHistory; 2],
585 pub turn: TurnState,
586 pub rng: Rng64,
587 pub modifiers: Vec<ModifierInstance>,
588 pub next_modifier_id: u32,
589 pub replacements: Vec<ReplacementSpec>,
590 pub next_replacement_insertion: u32,
591 pub terminal: Option<TerminalResult>,
592}
593
594impl GameState {
595 pub fn new(deck_a: Vec<CardId>, deck_b: Vec<CardId>, seed: u64, starting_player: u8) -> Self {
596 assert!(
597 deck_a.len() == crate::encode::MAX_DECK,
598 "Deck A must contain exactly {} cards",
599 crate::encode::MAX_DECK
600 );
601 assert!(
602 deck_b.len() == crate::encode::MAX_DECK,
603 "Deck B must contain exactly {} cards",
604 crate::encode::MAX_DECK
605 );
606 let rng = Rng64::new(seed);
607 let mut next_instance_id: CardInstanceId = 1;
608 let deck_a = Self::build_deck(deck_a, 0, &mut next_instance_id);
609 let deck_b = Self::build_deck(deck_b, 1, &mut next_instance_id);
610 Self {
611 players: [PlayerState::new(deck_a), PlayerState::new(deck_b)],
612 reveal_history: [RevealHistory::new(), RevealHistory::new()],
613 turn: TurnState {
614 active_player: starting_player,
615 starting_player,
616 turn_number: 0,
617 phase: Phase::Mulligan,
618 mulligan_done: [false; 2],
619 mulligan_selected: [0; 2],
620 main_passed: false,
621 decision_count: 0,
622 tick_count: 0,
623 attack: None,
624 attack_subphase_count: 0,
625 pending_level_up: None,
626 encore_queue: Vec::new(),
627 encore_step_player: None,
628 pending_triggers: Vec::new(),
629 pending_triggers_sorted: true,
630 trigger_order: None,
631 choice: None,
632 target_selection: None,
633 pending_cost: None,
634 priority: None,
635 stack: Vec::new(),
636 pending_stack_groups: VecDeque::new(),
637 stack_order: None,
638 derived_attack: None,
639 next_trigger_id: 1,
640 next_trigger_group_id: 1,
641 next_choice_id: 1,
642 next_stack_group_id: 1,
643 next_damage_event_id: 1,
644 next_effect_instance_id: 1,
645 active_window: None,
646 end_phase_window_done: false,
647 end_phase_discard_done: false,
648 end_phase_climax_done: false,
649 end_phase_cleanup_done: false,
650 encore_window_done: false,
651 pending_losses: [false; 2],
652 damage_resolution_target: None,
653 cost_payment_depth: 0,
654 pending_resolution_cleanup: Vec::new(),
655 phase_step: 0,
656 attack_phase_begin_done: false,
657 attack_decl_check_done: false,
658 encore_begin_done: false,
659 end_phase_pending: false,
660 },
661 rng,
662 modifiers: Vec::new(),
663 next_modifier_id: 1,
664 replacements: Vec::new(),
665 next_replacement_insertion: 1,
666 terminal: None,
667 }
668 }
669
670 fn build_deck(
671 deck: Vec<CardId>,
672 owner: u8,
673 next_instance_id: &mut CardInstanceId,
674 ) -> Vec<CardInstance> {
675 deck.into_iter()
676 .map(|id| {
677 let instance_id = *next_instance_id;
678 *next_instance_id = next_instance_id.wrapping_add(1);
679 CardInstance::new(id, owner, instance_id)
680 })
681 .collect()
682 }
683}