weiss_core/state/attack.rs
1use serde::{Deserialize, Serialize};
2
3use crate::db::CardId;
4use crate::effects::EffectId;
5
6use super::CardInstanceId;
7
8/// Attack types available during the attack step.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum AttackType {
11 /// Frontal attack against an opposing character.
12 Frontal,
13 /// Side attack against an opposing character.
14 Side,
15 /// Direct attack (no opposing character).
16 Direct,
17}
18
19/// Attack step sub-phase.
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
21pub enum AttackStep {
22 /// Trigger reveal/resolution step.
23 Trigger,
24 /// Counter timing step.
25 Counter,
26 /// Damage resolution step.
27 Damage,
28 /// Battle comparison step.
29 Battle,
30 /// Encore timing step.
31 Encore,
32}
33
34/// Damage type classification.
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
36pub enum DamageType {
37 /// Damage caused by an attack.
38 Battle,
39 /// Damage caused by an effect.
40 Effect,
41}
42
43/// Modifier categories for damage processing.
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
45pub enum DamageModifierKind {
46 /// Add `delta` to the damage amount.
47 AddAmount {
48 /// Signed delta to apply.
49 delta: i32,
50 },
51 /// Set whether the damage is cancelable.
52 SetCancelable {
53 /// New cancelable flag.
54 cancelable: bool,
55 },
56 /// Cancel the next damage instance.
57 CancelNext,
58 /// Set the damage amount to an absolute value.
59 SetAmount {
60 /// Absolute damage amount.
61 amount: i32,
62 },
63}
64
65/// Applied damage modifier instance.
66#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
67pub struct DamageModifier {
68 /// Modifier behavior.
69 pub kind: DamageModifierKind,
70 /// Ordering priority for application.
71 pub priority: i16,
72 /// Insertion order used as a tie-breaker.
73 pub insertion: u32,
74 /// Source identifier for debugging/auditing.
75 pub source_id: u32,
76 /// Remaining applications or magnitude budget (variant-dependent).
77 pub remaining: i32,
78 /// Whether this modifier has been applied at least once.
79 pub used: bool,
80}
81
82/// Trigger effects resolved from trigger icons.
83#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
84pub enum TriggerEffect {
85 /// Add soul for the current attack.
86 Soul,
87 /// Draw a card.
88 Draw,
89 /// Deal shot damage.
90 Shot,
91 /// Return a character to hand.
92 Bounce,
93 /// Perform a choice selection.
94 Choice,
95 /// Add the revealed card to stock ("Pool").
96 Pool,
97 /// Add the revealed card to hand ("Treasure").
98 Treasure,
99 /// Salvage from waiting room ("Gate").
100 Gate,
101 /// Place a character from deck ("Standby").
102 Standby,
103 /// Resolve an auto ability on the trigger source card.
104 AutoAbility {
105 /// Index into the card's ability list.
106 ability_index: u8,
107 },
108 /// Resolve an auto ability that was granted at runtime.
109 GrantedAutoAbility {
110 /// Stable grant identifier referencing a `GrantedAbilityInstance`.
111 grant_id: u64,
112 },
113}
114
115/// Context for an ongoing attack.
116#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
117pub struct AttackContext {
118 /// Attacker stage slot index.
119 pub attacker_slot: u8,
120 /// Optional defender stage slot index (None for direct attacks).
121 pub defender_slot: Option<u8>,
122 /// Declared attack type.
123 pub attack_type: AttackType,
124 /// Trigger-check revealed card id (if any).
125 pub trigger_card: Option<CardId>,
126 /// Trigger-check revealed card instance id (if any).
127 pub trigger_instance_id: Option<CardInstanceId>,
128 /// Total trigger checks required.
129 pub trigger_checks_total: u8,
130 /// Trigger checks already resolved.
131 pub trigger_checks_resolved: u8,
132 /// Current damage amount.
133 pub damage: i32,
134 /// Whether counter is allowed.
135 pub counter_allowed: bool,
136 /// Whether a counter was played.
137 pub counter_played: bool,
138 /// Power added by counters.
139 pub counter_power: i32,
140 /// Active damage modifiers.
141 pub damage_modifiers: Vec<DamageModifier>,
142 /// Pending shot damage remaining to apply.
143 pub pending_shot_damage: u8,
144 /// Next id for damage modifier instances within this attack.
145 pub next_modifier_id: u32,
146 /// Last damage event id emitted for this attack (if any).
147 pub last_damage_event_id: Option<u32>,
148 /// Whether auto triggers were enqueued for this attack.
149 pub auto_trigger_enqueued: bool,
150 /// Whether auto damage effects were enqueued for this attack.
151 pub auto_damage_enqueued: bool,
152 /// Whether battle damage has been applied.
153 pub battle_damage_applied: bool,
154 /// Current sub-step within the attack.
155 pub step: AttackStep,
156 /// Whether the declaration timing window is complete.
157 pub decl_window_done: bool,
158 /// Whether the trigger timing window is complete.
159 pub trigger_window_done: bool,
160 /// Whether the damage timing window is complete.
161 pub damage_window_done: bool,
162}
163
164/// Trigger pending resolution.
165#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
166pub struct PendingTrigger {
167 /// Unique trigger id.
168 pub id: u32,
169 /// Group id for simultaneous triggers.
170 pub group_id: u32,
171 /// Player seat that owns the trigger.
172 pub player: u8,
173 /// Source card id that produced the trigger.
174 pub source_card: CardId,
175 /// Trigger effect kind.
176 pub effect: TriggerEffect,
177 /// Optional effect id for auto/granted abilities.
178 pub effect_id: Option<EffectId>,
179}
180
181/// Ordering state for multiple triggers.
182#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
183pub struct TriggerOrderState {
184 /// Group id for the set of triggers being ordered.
185 pub group_id: u32,
186 /// Player seat choosing the order.
187 pub player: u8,
188 /// Remaining trigger ids to choose from.
189 pub choices: Vec<u32>,
190}
191
192/// Derived attack information for a single slot.
193#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
194pub struct DerivedAttackSlot {
195 /// Whether the slot is unable to declare any attack.
196 pub cannot_attack: bool,
197 /// Whether side attacks are disallowed.
198 #[serde(default)]
199 pub cannot_side_attack: bool,
200 /// Whether frontal attacks are disallowed.
201 #[serde(default)]
202 pub cannot_frontal_attack: bool,
203 /// Additional stock cost required to attack from this slot.
204 pub attack_cost: u8,
205}
206
207impl DerivedAttackSlot {
208 /// Create an empty derived attack slot.
209 pub fn empty() -> Self {
210 Self {
211 cannot_attack: false,
212 cannot_side_attack: false,
213 cannot_frontal_attack: false,
214 attack_cost: 0,
215 }
216 }
217}
218
219/// Derived attack state for a turn.
220#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
221pub struct DerivedAttackState {
222 /// Per-player derived slot info for each stage slot.
223 pub per_player: [[DerivedAttackSlot; 5]; 2],
224}
225
226impl DerivedAttackState {
227 /// Create a default derived attack state.
228 pub fn new() -> Self {
229 Self {
230 per_player: [[DerivedAttackSlot::empty(); 5]; 2],
231 }
232 }
233}
234
235impl Default for DerivedAttackState {
236 fn default() -> Self {
237 Self::new()
238 }
239}
240
241/// Encore request tracking for a character.
242#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
243pub struct EncoreRequest {
244 /// Player seat that owns the encore request.
245 pub player: u8,
246 /// Stage slot index of the character requesting encore.
247 pub slot: u8,
248}