1use std::collections::{BTreeMap, HashMap};
2
3use serde::{Deserialize, Serialize};
4use ts_rs::TS;
5
6use crate::prompts::common::TargetRef;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, TS)]
9#[ts(export, export_to = "game/index.ts")]
10pub enum ManaColor {
11 #[serde(rename = "W")]
12 White,
13 #[serde(rename = "U")]
14 Blue,
15 #[serde(rename = "B")]
16 Black,
17 #[serde(rename = "R")]
18 Red,
19 #[serde(rename = "G")]
20 Green,
21 #[serde(rename = "C")]
22 Colorless,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
26#[serde(rename_all = "camelCase")]
27#[ts(export, export_to = "game/index.ts")]
28pub struct Mana {
29 pub color: ManaColor,
30 pub amount: i32,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, TS)]
34#[serde(rename_all = "camelCase")]
35#[ts(export, export_to = "game/index.ts")]
36pub enum PlayerCounterKind {
37 Poison,
38 Energy,
39 Experience,
40 Radiation,
41 Ticket,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
45#[serde(rename_all = "camelCase")]
46#[ts(export, export_to = "game/index.ts")]
47pub enum ZoneKind {
48 Battlefield,
49 Hand,
50 Library,
51 Graveyard,
52 Exile,
53 Command,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, TS)]
57#[serde(rename_all = "camelCase")]
58#[ts(export, export_to = "game/index.ts")]
59pub enum StepKind {
60 #[default]
61 Untap,
62 Upkeep,
63 Draw,
64 Main1,
65 CombatBegin,
66 CombatDeclareAttackers,
67 CombatDeclareBlockers,
68 CombatFirstStrikeDamage,
69 CombatDamage,
70 CombatEnd,
71 Main2,
72 EndOfTurn,
73 Cleanup,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, TS)]
77#[serde(rename_all = "camelCase")]
78#[ts(export, export_to = "game/index.ts")]
79pub enum DayTime {
80 #[default]
81 Neither,
82 Day,
83 Night,
84}
85
86#[allow(clippy::large_enum_variant)]
87#[derive(Debug, Clone, Serialize, Deserialize, TS)]
88#[serde(
89 tag = "visibility",
90 rename_all = "camelCase",
91 rename_all_fields = "camelCase"
92)]
93#[ts(export, export_to = "game/index.ts")]
94pub enum CardView {
95 Visible(CardDto),
96 Hidden { id: String },
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, TS)]
101#[serde(rename_all = "camelCase")]
102#[ts(export, export_to = "game/index.ts")]
103pub struct ZoneDto {
104 pub zone: ZoneKind,
105 pub owner_id: String,
106 pub cards: Vec<CardView>,
109 pub count: usize,
110}
111
112#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
113#[serde(rename_all = "camelCase")]
114#[ts(export, export_to = "game/index.ts")]
115pub struct GameViewDto {
116 pub game_id: String,
117 pub turn: u32,
118 pub step: StepKind,
119 pub combat_assignments: Vec<CombatAssignmentDto>,
120 pub active_player_id: String,
121 pub priority_player_id: String,
122 pub players: Vec<PlayerDto>,
123 pub zones: Vec<ZoneDto>,
124 pub stack: Vec<StackObjectDto>,
125 pub game_over: bool,
126 pub winner_id: Option<String>,
127 pub monarch_id: Option<String>,
128 pub initiative_holder_id: Option<String>,
129 pub day_time: DayTime,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize, TS)]
133#[serde(rename_all = "camelCase")]
134#[ts(export, export_to = "game/index.ts")]
135pub struct CombatAssignmentDto {
136 pub blocker_id: String,
137 pub attacker_id: String,
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, TS)]
141#[serde(rename_all = "camelCase")]
142#[ts(export, export_to = "game/index.ts")]
143pub enum PlayerStatus {
144 #[default]
145 Playing,
146 Lost,
147 Conceded,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, TS)]
151#[serde(rename_all = "camelCase")]
152#[ts(export, export_to = "game/index.ts")]
153pub struct PlayerDto {
154 pub id: String,
155 pub name: String,
156 pub status: PlayerStatus,
157 pub is_human: bool,
158 pub life: i32,
159 pub counters: BTreeMap<PlayerCounterKind, u32>,
160 pub mana_pool: BTreeMap<ManaColor, u32>,
161 #[ts(type = "Record<string, number>")]
162 pub commander_damage: HashMap<String, i32>,
163 pub has_city_blessing: bool,
164 pub ring_level: i32,
165 pub speed: i32,
166}
167
168#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
169#[serde(rename_all = "camelCase", default)]
170#[ts(export, export_to = "game/index.ts")]
171pub struct CardIdentity {
172 pub name: String,
173 pub set_code: String,
174 pub card_number: String,
175 pub is_token: bool,
176}
177
178#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
179#[serde(rename_all = "camelCase", default)]
180#[ts(export, export_to = "game/index.ts")]
181pub struct ClassLevelDto {
182 pub level: i32,
183 pub oracle: String,
184 #[serde(default, skip_serializing_if = "Option::is_none")]
185 #[ts(optional)]
186 pub cost: Option<String>,
187}
188
189#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
190#[serde(rename_all = "camelCase", default)]
191#[ts(export, export_to = "game/index.ts")]
192pub struct SagaChapterDto {
193 pub chapters: Vec<i32>,
194 pub oracle: String,
195}
196
197#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
198#[serde(rename_all = "camelCase", default)]
199#[ts(export, export_to = "game/index.ts")]
200pub struct CardDto {
201 pub id: String,
202 pub identity: CardIdentity,
203 pub color: String,
204 pub mana_cost: String,
205 pub cmc: i32,
206 pub types: Vec<String>,
207 pub subtypes: Vec<String>,
208 pub supertypes: Vec<String>,
209 pub power: Option<String>,
210 pub toughness: Option<String>,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 #[ts(optional)]
213 pub base_power: Option<i32>,
214 #[serde(skip_serializing_if = "Option::is_none")]
215 #[ts(optional)]
216 pub base_toughness: Option<i32>,
217 #[serde(default, skip_serializing_if = "Option::is_none")]
218 #[ts(optional)]
219 pub final_chapter: Option<i32>,
220 #[serde(default, skip_serializing_if = "Option::is_none")]
221 #[ts(optional)]
222 pub class_level: Option<i32>,
223 pub class_levels: Vec<ClassLevelDto>,
224 pub saga_chapters: Vec<SagaChapterDto>,
225 pub text: String,
226 pub controller_id: String,
227 pub owner_id: String,
228 pub tapped: bool,
229 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
230 pub is_crewed: bool,
231 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
232 pub is_attacking: bool,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
234 #[ts(optional)]
235 pub attacking_player_id: Option<String>,
236 #[serde(default, skip_serializing_if = "Option::is_none")]
237 #[ts(optional)]
238 pub attack_target_id: Option<String>,
239 pub keywords: Vec<String>,
240 #[ts(type = "Record<string, number>")]
243 pub counters: BTreeMap<String, u32>,
244 pub damage: i32,
245 pub summoning_sick: bool,
246 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
247 pub is_copy: bool,
248 pub is_double_faced: bool,
249 pub is_transformed: bool,
250 pub is_face_down: bool,
251 pub is_bestowed: bool,
252 pub phased_out: bool,
253 pub exerted: bool,
254 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
255 pub is_ring_bearer: bool,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 #[ts(optional)]
258 pub attached_to: Option<String>,
259 #[serde(default, skip_serializing_if = "Vec::is_empty")]
260 pub attachment_ids: Vec<String>,
261 #[serde(default, skip_serializing_if = "Vec::is_empty")]
263 pub merged_card_ids: Vec<String>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 #[ts(optional)]
266 pub flashback_cost: Option<String>,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 #[ts(optional)]
269 pub kicker_cost: Option<String>,
270 #[serde(skip_serializing_if = "Option::is_none")]
271 #[ts(optional)]
272 pub effective_mana_cost: Option<String>,
273 #[serde(skip_serializing_if = "Option::is_none")]
274 #[ts(optional)]
275 pub madness_cost: Option<String>,
276 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
277 pub is_madness_exiled: bool,
278 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
279 pub is_plotted: bool,
280 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
281 pub is_warp_exiled: bool,
282 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
283 pub foil: bool,
284 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
285 pub would_die_in_combat: bool,
286}
287
288#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
289#[serde(rename_all = "camelCase", default)]
290#[ts(export, export_to = "game/index.ts")]
291pub struct StackObjectDto {
292 pub id: String,
293 pub source_id: String,
294 pub controller_id: String,
295 pub identity: CardIdentity,
296 pub text: String,
297 pub is_permanent_spell: bool,
298 pub is_casting: bool,
299 pub targets: Vec<TargetRef>,
300}
301
302#[derive(
303 Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, TS, strum_macros::Display,
304)]
305#[serde(rename_all = "camelCase")]
306#[ts(export, export_to = "game/index.ts")]
307pub enum TargetingIntent {
308 #[default]
309 Damage,
310 Destroy,
311 Sacrifice,
312 Exile,
313 Bounce,
314 Mill,
315 Discard,
316 Counter,
317 Tap,
318 Untap,
319 Copy,
320 Buff,
321 Debuff,
322 Heal,
323 LoseLife,
324 Reveal,
325 Draw,
326 Fetch,
327 GainControl,
328 Fight,
329 Attach,
330 Attack,
331 Block,
332 Hostile,
333 Friendly,
334}
335
336#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
337#[serde(rename_all = "camelCase")]
338#[ts(export, export_to = "game/index.ts")]
339pub struct PlaymatSettings {
340 #[serde(default, skip_serializing_if = "Option::is_none")]
341 #[ts(optional)]
342 pub opacity: Option<f32>,
343 #[serde(default, skip_serializing_if = "Option::is_none")]
344 #[ts(optional)]
345 pub texture: Option<f32>,
346 #[serde(default, skip_serializing_if = "Option::is_none")]
347 #[ts(optional)]
348 pub border_width: Option<f32>,
349 #[serde(default, skip_serializing_if = "Option::is_none")]
350 #[ts(optional)]
351 pub border_color: Option<String>,
352 #[serde(default, skip_serializing_if = "Option::is_none")]
353 #[ts(optional)]
354 pub fit: Option<String>,
355 #[serde(default, skip_serializing_if = "Option::is_none")]
356 #[ts(optional)]
357 pub offset_x: Option<f32>,
358 #[serde(default, skip_serializing_if = "Option::is_none")]
359 #[ts(optional)]
360 pub offset_y: Option<f32>,
361 #[serde(default, skip_serializing_if = "Option::is_none")]
362 #[ts(optional)]
363 pub zoom: Option<f32>,
364 #[serde(default, skip_serializing_if = "Option::is_none")]
365 #[ts(optional)]
366 pub blur: Option<f32>,
367 #[serde(default, skip_serializing_if = "Option::is_none")]
368 #[ts(optional)]
369 pub brightness: Option<f32>,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
371 #[ts(optional)]
372 pub color: Option<String>,
373}