1use std::collections::{BTreeMap, HashMap};
2
3use serde::{Deserialize, Serialize};
4use ts_rs::TS;
5
6use crate::{prompts::common::TargetRef, TokenScript};
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, PartialEq, Eq, Serialize, Deserialize, TS)]
26#[serde(
27 tag = "kind",
28 rename_all = "camelCase",
29 rename_all_fields = "camelCase"
30)]
31#[ts(export, export_to = "game/index.ts")]
32pub enum CardChoiceDto {
33 Color { colors: Vec<ManaColor> },
34 Type { values: Vec<String> },
35 NamedCard { names: Vec<String> },
36 ChosenCard { count: usize },
37 Number { value: i32 },
38 Mode { value: String },
39 Player { player_id: String, name: String },
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
43#[serde(rename_all = "camelCase")]
44#[ts(export, export_to = "game/index.ts")]
45pub struct Mana {
46 pub color: ManaColor,
47 pub amount: i32,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, TS)]
51#[serde(rename_all = "camelCase")]
52#[ts(export, export_to = "game/index.ts")]
53pub enum PlayerCounterKind {
54 Poison,
55 Energy,
56 Experience,
57 Radiation,
58 Ticket,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
62#[serde(rename_all = "camelCase")]
63#[ts(export, export_to = "game/index.ts")]
64pub enum ZoneKind {
65 Battlefield,
66 Hand,
67 Library,
68 Graveyard,
69 Exile,
70 Command,
71 Attractions,
72 Junkyard,
73 Sideboard,
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 StepKind {
80 #[default]
81 Untap,
82 Upkeep,
83 Draw,
84 Main1,
85 CombatBegin,
86 CombatDeclareAttackers,
87 CombatDeclareBlockers,
88 CombatFirstStrikeDamage,
89 CombatDamage,
90 CombatEnd,
91 Main2,
92 EndOfTurn,
93 Cleanup,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, TS)]
97#[serde(rename_all = "camelCase")]
98#[ts(export, export_to = "game/index.ts")]
99pub enum DayTime {
100 #[default]
101 Neither,
102 Day,
103 Night,
104}
105
106#[allow(clippy::large_enum_variant)]
107#[derive(Debug, Clone, Serialize, Deserialize, TS)]
108#[serde(
109 tag = "visibility",
110 rename_all = "camelCase",
111 rename_all_fields = "camelCase"
112)]
113#[ts(export, export_to = "game/index.ts")]
114pub enum CardView {
115 Visible(CardDto),
116 Hidden { id: String },
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, TS)]
121#[serde(rename_all = "camelCase")]
122#[ts(export, export_to = "game/index.ts")]
123pub struct ZoneDto {
124 pub zone: ZoneKind,
125 pub owner_id: String,
126 pub cards: Vec<CardView>,
129 pub count: usize,
130}
131
132#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
133#[serde(rename_all = "camelCase")]
134#[ts(export, export_to = "game/index.ts")]
135pub struct GameViewDto {
136 pub game_id: String,
137 pub turn: u32,
138 pub step: StepKind,
139 pub combat_assignments: Vec<CombatAssignmentDto>,
140 pub active_player_id: String,
141 pub priority_player_id: String,
142 pub players: Vec<PlayerDto>,
143 pub zones: Vec<ZoneDto>,
144 pub stack: Vec<StackObjectDto>,
145 pub game_over: bool,
146 pub winner_id: Option<String>,
147 pub monarch_id: Option<String>,
148 pub initiative_holder_id: Option<String>,
149 pub day_time: DayTime,
150 #[ts(optional)]
151 pub active_plane_names: Option<Vec<String>>,
152 #[serde(default)]
153 pub checkpoints: Vec<CheckpointDto>,
154 #[serde(default)]
155 pub restore_vote: Option<RestoreVoteDto>,
156 #[serde(default)]
157 pub snapshot_recording: bool,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, TS)]
161#[serde(rename_all = "camelCase")]
162#[ts(export, export_to = "game/index.ts")]
163pub struct RestoreVoteDto {
164 pub vote_id: u32,
165 pub checkpoint: CheckpointDto,
166 pub requested_by_player_id: String,
167 pub awaiting_player_ids: Vec<String>,
168 pub status: RestoreVoteStatus,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, TS)]
172#[serde(
173 tag = "type",
174 rename_all = "camelCase",
175 rename_all_fields = "camelCase"
176)]
177#[ts(export, export_to = "game/index.ts")]
178pub enum RestoreVoteStatus {
179 Pending,
180 Approved,
181 Declined { player_id: String },
182 Unavailable,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, TS)]
186#[serde(rename_all = "camelCase")]
187#[ts(export, export_to = "game/index.ts")]
188pub struct CheckpointDto {
189 pub checkpoint_id: u32,
190 pub turn: u32,
191 pub step: StepKind,
192 pub active_player_id: String,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, TS)]
196#[serde(rename_all = "camelCase")]
197#[ts(export, export_to = "game/index.ts")]
198pub struct CombatAssignmentDto {
199 pub blocker_id: String,
200 pub attacker_id: String,
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, TS)]
204#[serde(rename_all = "camelCase")]
205#[ts(export, export_to = "game/index.ts")]
206pub enum PlayerStatus {
207 #[default]
208 Playing,
209 Lost,
210 Conceded,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize, TS)]
214#[serde(rename_all = "camelCase")]
215#[ts(export, export_to = "game/index.ts")]
216pub struct DungeonStateDto {
217 pub name: String,
218 pub room: String,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize, TS)]
222#[serde(rename_all = "camelCase")]
223#[ts(export, export_to = "game/index.ts")]
224pub struct PlayerDto {
225 pub id: String,
226 pub name: String,
227 pub status: PlayerStatus,
228 pub is_human: bool,
229 pub life: i32,
230 #[serde(default)]
231 pub max_hand_size: i32,
232 #[serde(default)]
233 pub unlimited_hand_size: bool,
234 #[serde(default)]
235 pub lands_played_this_turn: i32,
236 #[serde(default)]
237 pub max_land_plays_per_turn: i32,
238 #[serde(default)]
239 pub unlimited_land_plays: bool,
240 #[serde(default)]
241 pub cards_drawn_this_turn: i32,
242 #[serde(default)]
243 pub damage_prevention: i32,
244 #[serde(default)]
245 pub is_extra_turn: bool,
246 #[serde(default)]
247 pub extra_turn_count: u32,
248 #[ts(optional)]
249 pub controlled_by: Option<String>,
250 #[serde(default)]
251 pub player_keywords: Vec<String>,
252 #[serde(default)]
253 #[ts(type = "Record<string, number>")]
254 pub commander_casts: HashMap<String, u32>,
255 #[ts(optional)]
256 pub dungeon_state: Option<DungeonStateDto>,
257 #[ts(optional)]
258 pub active_scheme_names: Option<Vec<String>>,
259 #[ts(optional)]
260 pub team_number: Option<i32>,
261 pub counters: BTreeMap<PlayerCounterKind, u32>,
262 pub mana_pool: BTreeMap<ManaColor, u32>,
263 #[ts(type = "Record<string, number>")]
264 pub commander_damage: HashMap<String, i32>,
265 pub has_city_blessing: bool,
266 pub has_enduring_story: bool,
267 pub ring_level: i32,
268 pub speed: i32,
269}
270
271#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
272#[serde(rename_all = "camelCase", default)]
273#[ts(export, export_to = "game/index.ts")]
274pub struct CardIdentity {
275 pub name: String,
276 pub set_code: String,
277 pub card_number: String,
278 pub is_token: bool,
279 #[serde(default, skip_serializing_if = "Option::is_none")]
280 #[ts(optional)]
281 pub token_script: Option<TokenScript>,
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
285#[serde(rename_all = "camelCase")]
286#[ts(export, export_to = "game/index.ts")]
287pub enum RoomDoor {
288 Left,
289 Right,
290}
291
292#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
293#[serde(rename_all = "camelCase", default)]
294#[ts(export, export_to = "game/index.ts")]
295pub struct ClassLevelDto {
296 pub level: i32,
297 pub oracle: String,
298 #[serde(default, skip_serializing_if = "Option::is_none")]
299 #[ts(optional)]
300 pub cost: Option<String>,
301}
302
303#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
304#[serde(rename_all = "camelCase", default)]
305#[ts(export, export_to = "game/index.ts")]
306pub struct SagaChapterDto {
307 pub chapters: Vec<i32>,
308 pub oracle: String,
309}
310
311#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
312#[serde(rename_all = "camelCase", default)]
313#[ts(export, export_to = "game/index.ts")]
314pub struct CardDto {
315 pub id: String,
316 pub identity: CardIdentity,
317 #[serde(
318 default,
319 deserialize_with = "crate::deck_dto::CardCosmetics::deserialize_optional",
320 skip_serializing_if = "Option::is_none"
321 )]
322 #[ts(optional)]
323 pub cosmetics: Option<crate::deck_dto::CardCosmetics>,
324 pub color: String,
325 pub mana_cost: String,
326 pub cmc: i32,
327 pub types: Vec<String>,
328 pub subtypes: Vec<String>,
329 pub supertypes: Vec<String>,
330 pub power: Option<String>,
331 pub toughness: Option<String>,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 #[ts(optional)]
334 pub base_power: Option<i32>,
335 #[serde(skip_serializing_if = "Option::is_none")]
336 #[ts(optional)]
337 pub base_toughness: Option<i32>,
338 #[serde(default, skip_serializing_if = "Option::is_none")]
339 #[ts(optional)]
340 pub final_chapter: Option<i32>,
341 #[serde(default, skip_serializing_if = "Option::is_none")]
342 #[ts(optional)]
343 pub class_level: Option<i32>,
344 pub class_levels: Vec<ClassLevelDto>,
345 pub saga_chapters: Vec<SagaChapterDto>,
346 #[serde(default, skip_serializing_if = "Option::is_none")]
347 #[ts(optional)]
348 pub unlocked_doors: Option<Vec<RoomDoor>>,
349 #[serde(default, skip_serializing_if = "Option::is_none")]
350 #[ts(optional)]
351 pub is_solved: Option<bool>,
352 #[serde(default, skip_serializing_if = "Option::is_none")]
353 #[ts(optional)]
354 pub is_suspended: Option<bool>,
355 #[serde(default, skip_serializing_if = "Option::is_none")]
356 #[ts(optional)]
357 pub is_on_adventure: Option<bool>,
358 #[serde(default, skip_serializing_if = "Option::is_none")]
359 #[ts(optional)]
360 pub is_prototyped: Option<bool>,
361 pub text: String,
362 #[serde(default)]
363 pub choices: Vec<CardChoiceDto>,
364 pub controller_id: String,
365 pub owner_id: String,
366 pub tapped: bool,
367 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
368 pub is_crewed: bool,
369 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
370 pub is_attacking: bool,
371 #[serde(default, skip_serializing_if = "Option::is_none")]
372 #[ts(optional)]
373 pub attacking_player_id: Option<String>,
374 #[serde(default, skip_serializing_if = "Option::is_none")]
375 #[ts(optional)]
376 pub attack_target_id: Option<String>,
377 pub keywords: Vec<String>,
378 #[serde(default, skip_serializing_if = "Option::is_none")]
379 #[ts(optional)]
380 pub keyword_labels: Option<BTreeMap<String, String>>,
381 #[serde(default, skip_serializing_if = "Vec::is_empty")]
382 pub granted_keywords: Vec<String>,
383 #[ts(type = "Record<string, number>")]
386 pub counters: BTreeMap<String, u32>,
387 pub damage: i32,
388 pub summoning_sick: bool,
389 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
390 pub is_copy: bool,
391 pub is_double_faced: bool,
392 #[serde(default, skip_serializing_if = "Option::is_none")]
393 #[ts(optional)]
394 pub face_index: Option<u8>,
395 pub is_transformed: bool,
396 pub is_face_down: bool,
397 pub is_bestowed: bool,
398 pub phased_out: bool,
399 pub exerted: bool,
400 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
401 pub is_ring_bearer: bool,
402 #[serde(skip_serializing_if = "Option::is_none")]
403 #[ts(optional)]
404 pub attached_to: Option<String>,
405 #[serde(default, skip_serializing_if = "Vec::is_empty")]
406 pub attachment_ids: Vec<String>,
407 #[serde(default, skip_serializing_if = "Vec::is_empty")]
409 pub merged_card_ids: Vec<String>,
410 #[serde(skip_serializing_if = "Option::is_none")]
411 #[ts(optional)]
412 pub flashback_cost: Option<String>,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 #[ts(optional)]
415 pub kicker_cost: Option<String>,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 #[ts(optional)]
418 pub effective_mana_cost: Option<String>,
419 #[serde(default, skip_serializing_if = "Option::is_none")]
420 #[ts(optional)]
421 pub commander_tax: Option<i32>,
422 #[serde(skip_serializing_if = "Option::is_none")]
423 #[ts(optional)]
424 pub madness_cost: Option<String>,
425 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
426 pub is_madness_exiled: bool,
427 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
428 pub is_plotted: bool,
429 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
430 pub is_warp_exiled: bool,
431 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
432 pub foil: bool,
433 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
434 pub would_die_in_combat: bool,
435 #[serde(default, skip_serializing_if = "Option::is_none")]
436 #[ts(optional)]
437 pub zone: Option<ZoneKind>,
438}
439
440#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
441#[serde(rename_all = "camelCase", default)]
442#[ts(export, export_to = "game/index.ts")]
443pub struct StackObjectDto {
444 pub id: String,
445 pub source_id: String,
446 pub controller_id: String,
447 pub owner_id: String,
448 #[serde(skip_serializing_if = "Option::is_none")]
449 #[ts(optional)]
450 pub identity: Option<CardIdentity>,
451 pub text: String,
452 #[serde(skip_serializing_if = "Option::is_none")]
453 #[ts(optional)]
454 pub source_ability_text: Option<String>,
455 pub is_permanent_spell: bool,
456 pub is_casting: bool,
457 #[serde(default, skip_serializing_if = "Option::is_none")]
458 #[ts(optional)]
459 pub source_card: Option<CardDto>,
460 pub targets: Vec<TargetRef>,
461}
462
463#[derive(
464 Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, TS, strum_macros::Display,
465)]
466#[serde(rename_all = "camelCase")]
467#[ts(export, export_to = "game/index.ts")]
468pub enum TargetingIntent {
469 #[default]
470 Damage,
471 Destroy,
472 Sacrifice,
473 Exile,
474 Bounce,
475 Mill,
476 Discard,
477 Counter,
478 Tap,
479 Untap,
480 Copy,
481 Buff,
482 Debuff,
483 Heal,
484 LoseLife,
485 Reveal,
486 Draw,
487 Fetch,
488 GainControl,
489 Fight,
490 Attach,
491 Attack,
492 Block,
493 Hostile,
494 Friendly,
495}
496
497#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
498#[serde(rename_all = "camelCase")]
499#[ts(export, export_to = "game/index.ts")]
500pub struct PlaymatSettings {
501 #[serde(default, skip_serializing_if = "Option::is_none")]
502 #[ts(optional)]
503 pub opacity: Option<f32>,
504 #[serde(default, skip_serializing_if = "Option::is_none")]
505 #[ts(optional)]
506 pub texture: Option<f32>,
507 #[serde(default, skip_serializing_if = "Option::is_none")]
508 #[ts(optional)]
509 pub border_width: Option<f32>,
510 #[serde(default, skip_serializing_if = "Option::is_none")]
511 #[ts(optional)]
512 pub border_color: Option<String>,
513 #[serde(default, skip_serializing_if = "Option::is_none")]
514 #[ts(optional)]
515 pub fit: Option<String>,
516 #[serde(default, skip_serializing_if = "Option::is_none")]
517 #[ts(optional)]
518 pub offset_x: Option<f32>,
519 #[serde(default, skip_serializing_if = "Option::is_none")]
520 #[ts(optional)]
521 pub offset_y: Option<f32>,
522 #[serde(default, skip_serializing_if = "Option::is_none")]
523 #[ts(optional)]
524 pub zoom: Option<f32>,
525 #[serde(default, skip_serializing_if = "Option::is_none")]
526 #[ts(optional)]
527 pub blur: Option<f32>,
528 #[serde(default, skip_serializing_if = "Option::is_none")]
529 #[ts(optional)]
530 pub brightness: Option<f32>,
531 #[serde(default, skip_serializing_if = "Option::is_none")]
532 #[ts(optional)]
533 pub color: Option<String>,
534}
535
536#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
537#[ts(export, export_to = "game/index.ts")]
538pub enum GameFormat {
539 Any,
540 Standard,
541 Pioneer,
542 Modern,
543 Legacy,
544 Vintage,
545 Pauper,
546 Premodern,
547 Commander,
548 PauperCommander,
549 Brawl,
550 Oathbreaker,
551 Draft,
552 Sealed,
553}
554
555#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default, TS)]
556#[ts(export, export_to = "game/index.ts")]
557pub enum EngineKind {
558 #[default]
559 Manabrew,
560 Forge,
561 Ironsmith,
562}