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 #[serde(default, skip_serializing_if = "Option::is_none")]
159 #[ts(optional)]
160 pub decision_history: Option<DecisionHistoryDto>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, TS)]
164#[serde(rename_all = "camelCase")]
165#[ts(export, export_to = "game/index.ts")]
166pub struct DecisionHistoryDto {
167 pub rewinding: bool,
168 pub unavailable_reason: Option<String>,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, TS)]
172#[serde(rename_all = "camelCase")]
173#[ts(export, export_to = "game/index.ts")]
174pub struct RestoreVoteDto {
175 pub vote_id: u32,
176 pub checkpoint: CheckpointDto,
177 pub requested_by_player_id: String,
178 pub awaiting_player_ids: Vec<String>,
179 pub status: RestoreVoteStatus,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, TS)]
183#[serde(
184 tag = "type",
185 rename_all = "camelCase",
186 rename_all_fields = "camelCase"
187)]
188#[ts(export, export_to = "game/index.ts")]
189pub enum RestoreVoteStatus {
190 Pending,
191 Approved,
192 Declined { player_id: String },
193 Unavailable,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize, TS)]
197#[serde(rename_all = "camelCase")]
198#[ts(export, export_to = "game/index.ts")]
199pub struct CheckpointDto {
200 pub checkpoint_id: u32,
201 pub turn: u32,
202 pub step: StepKind,
203 pub active_player_id: String,
204 #[serde(default, skip_serializing_if = "Option::is_none")]
205 #[ts(optional)]
206 pub decision: Option<String>,
207 #[serde(default, skip_serializing_if = "Option::is_none")]
208 #[ts(optional)]
209 pub deciding_player_id: Option<String>,
210 #[serde(default, skip_serializing_if = "Vec::is_empty")]
211 pub log: Vec<String>,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize, TS)]
215#[serde(rename_all = "camelCase")]
216#[ts(export, export_to = "game/index.ts")]
217pub struct CombatAssignmentDto {
218 pub blocker_id: String,
219 pub attacker_id: String,
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, TS)]
223#[serde(rename_all = "camelCase")]
224#[ts(export, export_to = "game/index.ts")]
225pub enum PlayerStatus {
226 #[default]
227 Playing,
228 Lost,
229 Conceded,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize, TS)]
233#[serde(rename_all = "camelCase")]
234#[ts(export, export_to = "game/index.ts")]
235pub struct DungeonStateDto {
236 pub name: String,
237 pub room: String,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize, TS)]
241#[serde(rename_all = "camelCase")]
242#[ts(export, export_to = "game/index.ts")]
243pub struct PlayerDto {
244 pub id: String,
245 pub name: String,
246 pub status: PlayerStatus,
247 pub is_human: bool,
248 pub life: i32,
249 #[serde(default)]
250 pub max_hand_size: i32,
251 #[serde(default)]
252 pub unlimited_hand_size: bool,
253 #[serde(default)]
254 pub lands_played_this_turn: i32,
255 #[serde(default)]
256 pub max_land_plays_per_turn: i32,
257 #[serde(default)]
258 pub unlimited_land_plays: bool,
259 #[serde(default)]
260 pub cards_drawn_this_turn: i32,
261 #[serde(default)]
262 pub damage_prevention: i32,
263 #[serde(default)]
264 pub is_extra_turn: bool,
265 #[serde(default)]
266 pub extra_turn_count: u32,
267 #[ts(optional)]
268 pub controlled_by: Option<String>,
269 #[serde(default)]
270 pub player_keywords: Vec<String>,
271 #[serde(default)]
272 #[ts(type = "Record<string, number>")]
273 pub commander_casts: HashMap<String, u32>,
274 #[ts(optional)]
275 pub dungeon_state: Option<DungeonStateDto>,
276 #[ts(optional)]
277 pub active_scheme_names: Option<Vec<String>>,
278 #[ts(optional)]
279 pub team_number: Option<i32>,
280 pub counters: BTreeMap<PlayerCounterKind, u32>,
281 pub mana_pool: BTreeMap<ManaColor, u32>,
282 #[ts(type = "Record<string, number>")]
283 pub commander_damage: HashMap<String, i32>,
284 pub has_city_blessing: bool,
285 pub has_enduring_story: bool,
286 pub ring_level: i32,
287 pub speed: i32,
288}
289
290#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
291#[serde(rename_all = "camelCase", default)]
292#[ts(export, export_to = "game/index.ts")]
293pub struct CardIdentity {
294 pub name: String,
295 pub set_code: String,
296 pub card_number: String,
297 pub is_token: bool,
298 #[serde(default, skip_serializing_if = "Option::is_none")]
299 #[ts(optional)]
300 pub token_script: Option<TokenScript>,
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
304#[serde(rename_all = "camelCase")]
305#[ts(export, export_to = "game/index.ts")]
306pub enum RoomDoor {
307 Left,
308 Right,
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 ClassLevelDto {
315 pub level: i32,
316 pub oracle: String,
317 #[serde(default, skip_serializing_if = "Option::is_none")]
318 #[ts(optional)]
319 pub cost: Option<String>,
320}
321
322#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
323#[serde(rename_all = "camelCase", default)]
324#[ts(export, export_to = "game/index.ts")]
325pub struct SagaChapterDto {
326 pub chapters: Vec<i32>,
327 pub oracle: String,
328}
329
330#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
331#[serde(rename_all = "camelCase", default)]
332#[ts(export, export_to = "game/index.ts")]
333pub struct CardDto {
334 pub id: String,
335 pub identity: CardIdentity,
336 #[serde(
337 default,
338 deserialize_with = "crate::deck_dto::CardCosmetics::deserialize_optional",
339 skip_serializing_if = "Option::is_none"
340 )]
341 #[ts(optional)]
342 pub cosmetics: Option<crate::deck_dto::CardCosmetics>,
343 pub color: String,
344 pub mana_cost: String,
345 pub cmc: i32,
346 pub types: Vec<String>,
347 pub subtypes: Vec<String>,
348 pub supertypes: Vec<String>,
349 pub power: Option<String>,
350 pub toughness: Option<String>,
351 #[serde(skip_serializing_if = "Option::is_none")]
352 #[ts(optional)]
353 pub base_power: Option<i32>,
354 #[serde(skip_serializing_if = "Option::is_none")]
355 #[ts(optional)]
356 pub base_toughness: Option<i32>,
357 #[serde(default, skip_serializing_if = "Option::is_none")]
358 #[ts(optional)]
359 pub final_chapter: Option<i32>,
360 #[serde(default, skip_serializing_if = "Option::is_none")]
361 #[ts(optional)]
362 pub class_level: Option<i32>,
363 pub class_levels: Vec<ClassLevelDto>,
364 pub saga_chapters: Vec<SagaChapterDto>,
365 #[serde(default, skip_serializing_if = "Option::is_none")]
366 #[ts(optional)]
367 pub unlocked_doors: Option<Vec<RoomDoor>>,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
369 #[ts(optional)]
370 pub is_solved: Option<bool>,
371 #[serde(default, skip_serializing_if = "Option::is_none")]
372 #[ts(optional)]
373 pub is_suspended: Option<bool>,
374 #[serde(default, skip_serializing_if = "Option::is_none")]
375 #[ts(optional)]
376 pub is_on_adventure: Option<bool>,
377 #[serde(default, skip_serializing_if = "Option::is_none")]
378 #[ts(optional)]
379 pub is_prototyped: Option<bool>,
380 pub text: String,
381 #[serde(default)]
382 pub choices: Vec<CardChoiceDto>,
383 pub controller_id: String,
384 pub owner_id: String,
385 pub tapped: bool,
386 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
387 pub is_crewed: bool,
388 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
389 pub is_attacking: bool,
390 #[serde(default, skip_serializing_if = "Option::is_none")]
391 #[ts(optional)]
392 pub attacking_player_id: Option<String>,
393 #[serde(default, skip_serializing_if = "Option::is_none")]
394 #[ts(optional)]
395 pub attack_target_id: Option<String>,
396 pub keywords: Vec<String>,
397 #[serde(default, skip_serializing_if = "Option::is_none")]
398 #[ts(optional)]
399 pub keyword_labels: Option<BTreeMap<String, String>>,
400 #[serde(default, skip_serializing_if = "Vec::is_empty")]
401 pub granted_keywords: Vec<String>,
402 #[ts(type = "Record<string, number>")]
405 pub counters: BTreeMap<String, u32>,
406 pub damage: i32,
407 pub summoning_sick: bool,
408 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
409 pub is_copy: bool,
410 pub is_double_faced: bool,
411 #[serde(default, skip_serializing_if = "Option::is_none")]
412 #[ts(optional)]
413 pub face_index: Option<u8>,
414 pub is_transformed: bool,
415 pub is_face_down: bool,
416 pub is_bestowed: bool,
417 pub phased_out: bool,
418 pub exerted: bool,
419 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
420 pub is_ring_bearer: bool,
421 #[serde(skip_serializing_if = "Option::is_none")]
422 #[ts(optional)]
423 pub attached_to: Option<String>,
424 #[serde(default, skip_serializing_if = "Vec::is_empty")]
425 pub attachment_ids: Vec<String>,
426 #[serde(default, skip_serializing_if = "Vec::is_empty")]
428 pub merged_card_ids: Vec<String>,
429 #[serde(skip_serializing_if = "Option::is_none")]
430 #[ts(optional)]
431 pub flashback_cost: Option<String>,
432 #[serde(skip_serializing_if = "Option::is_none")]
433 #[ts(optional)]
434 pub kicker_cost: Option<String>,
435 #[serde(skip_serializing_if = "Option::is_none")]
436 #[ts(optional)]
437 pub effective_mana_cost: Option<String>,
438 #[serde(default, skip_serializing_if = "Option::is_none")]
439 #[ts(optional)]
440 pub commander_tax: Option<i32>,
441 #[serde(skip_serializing_if = "Option::is_none")]
442 #[ts(optional)]
443 pub madness_cost: Option<String>,
444 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
445 pub is_madness_exiled: bool,
446 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
447 pub is_plotted: bool,
448 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
449 pub is_warp_exiled: bool,
450 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
451 pub foil: bool,
452 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
453 pub would_die_in_combat: bool,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
455 #[ts(optional)]
456 pub zone: Option<ZoneKind>,
457}
458
459#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
460#[serde(rename_all = "camelCase", default)]
461#[ts(export, export_to = "game/index.ts")]
462pub struct StackObjectDto {
463 pub id: String,
464 pub source_id: String,
465 pub controller_id: String,
466 pub owner_id: String,
467 #[serde(skip_serializing_if = "Option::is_none")]
468 #[ts(optional)]
469 pub identity: Option<CardIdentity>,
470 pub text: String,
471 #[serde(skip_serializing_if = "Option::is_none")]
472 #[ts(optional)]
473 pub source_ability_text: Option<String>,
474 pub is_permanent_spell: bool,
475 pub is_casting: bool,
476 #[serde(default, skip_serializing_if = "Option::is_none")]
477 #[ts(optional)]
478 pub source_card: Option<CardDto>,
479 pub targets: Vec<TargetRef>,
480}
481
482#[derive(
483 Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, TS, strum_macros::Display,
484)]
485#[serde(rename_all = "camelCase")]
486#[ts(export, export_to = "game/index.ts")]
487pub enum TargetingIntent {
488 #[default]
489 Damage,
490 Destroy,
491 Sacrifice,
492 Exile,
493 Bounce,
494 Mill,
495 Discard,
496 Counter,
497 Tap,
498 Untap,
499 Copy,
500 Buff,
501 Debuff,
502 Heal,
503 LoseLife,
504 Reveal,
505 Draw,
506 Fetch,
507 GainControl,
508 Fight,
509 Attach,
510 Attack,
511 Block,
512 Hostile,
513 Friendly,
514}
515
516#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
517#[serde(rename_all = "camelCase")]
518#[ts(export, export_to = "game/index.ts")]
519pub struct PlaymatSettings {
520 #[serde(default, skip_serializing_if = "Option::is_none")]
521 #[ts(optional)]
522 pub opacity: Option<f32>,
523 #[serde(default, skip_serializing_if = "Option::is_none")]
524 #[ts(optional)]
525 pub texture: Option<f32>,
526 #[serde(default, skip_serializing_if = "Option::is_none")]
527 #[ts(optional)]
528 pub border_width: Option<f32>,
529 #[serde(default, skip_serializing_if = "Option::is_none")]
530 #[ts(optional)]
531 pub border_color: Option<String>,
532 #[serde(default, skip_serializing_if = "Option::is_none")]
533 #[ts(optional)]
534 pub fit: Option<String>,
535 #[serde(default, skip_serializing_if = "Option::is_none")]
536 #[ts(optional)]
537 pub offset_x: Option<f32>,
538 #[serde(default, skip_serializing_if = "Option::is_none")]
539 #[ts(optional)]
540 pub offset_y: Option<f32>,
541 #[serde(default, skip_serializing_if = "Option::is_none")]
542 #[ts(optional)]
543 pub zoom: Option<f32>,
544 #[serde(default, skip_serializing_if = "Option::is_none")]
545 #[ts(optional)]
546 pub blur: Option<f32>,
547 #[serde(default, skip_serializing_if = "Option::is_none")]
548 #[ts(optional)]
549 pub brightness: Option<f32>,
550 #[serde(default, skip_serializing_if = "Option::is_none")]
551 #[ts(optional)]
552 pub color: Option<String>,
553}
554
555#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
556#[ts(export, export_to = "game/index.ts")]
557pub enum GameFormat {
558 Any,
559 Standard,
560 Pioneer,
561 Modern,
562 Legacy,
563 Vintage,
564 Pauper,
565 Premodern,
566 Commander,
567 PauperCommander,
568 Brawl,
569 Oathbreaker,
570 Draft,
571 Sealed,
572}
573
574#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default, TS)]
575#[ts(export, export_to = "game/index.ts")]
576pub enum EngineKind {
577 #[default]
578 Manabrew,
579 Forge,
580 Ironsmith,
581}