Skip to main content

ygopro_data/message/
game_message.rs

1//! ygocore game messages (`gm`).
2//!
3//! Messages emitted by the ygocore engine during a duel.
4
5#![allow(non_upper_case_globals)]
6#![allow(non_camel_case_types)]
7
8use binrw::BinRead;
9use binrw::BinWrite;
10use binrw::binrw;
11use binrw::helpers::until_eof;
12use modular_bitfield::bitfield;
13use modular_bitfield::specifiers::B3;
14use modular_bitfield::specifiers::B28;
15use num_enum::TryFromPrimitive;
16use ygopro_derive::GameMessage;
17use ygopro_derive::Message;
18
19use crate::constants::*;
20use crate::data::CardPosition;
21use crate::data::InfoLocation;
22use crate::data::UpdateCardInfo;
23
24include!(concat!(env!("OUT_DIR"), "/game_message.rs"));
25every_game_message_flat_message!(crate::generate_enum);
26
27macro_rules! impl_into_stoc_message {
28    ($($message_name:ident=$message_flag:literal),*) => {
29        $(
30            impl From<$message_name> for crate::message::server_to_client::Message {
31                fn from(value: $message_name) -> Self {
32                    crate::message::server_to_client::Message::GameMessage(
33                        crate::message::server_to_client::GameMessage { message: value.into() }
34                    )
35                }
36            }
37        )*
38    };
39}
40every_game_message_flat_message!(impl_into_stoc_message);
41
42impl From<crate::message::game_message::Message> for crate::message::server_to_client::Message {
43    fn from(value: crate::message::game_message::Message) -> Self {
44        crate::message::server_to_client::Message::GameMessage(
45            crate::message::server_to_client::GameMessage { message: value }
46        )
47    }
48}
49
50
51pub trait GameMessage {
52    fn mask(&mut self);
53    fn should_mask(&self, _player: CorePlayer) -> bool {
54        true
55    }
56    fn waiting_for(&self) -> Option<CorePlayer> {
57        None
58    }
59}
60
61pub trait MaskedClone: GameMessage + Clone {
62    fn clone_masked(&self) -> Self {
63        let mut mirror = self.clone();
64        mirror.mask();
65        mirror
66    }
67}
68
69impl<T: GameMessage> GameMessage for Vec<T> {
70    fn mask(&mut self) {
71        for item in self { item.mask(); }
72    }
73    fn should_mask(&self, player: CorePlayer) -> bool {
74        self.iter().any(|item| item.should_mask(player))
75    }
76}
77
78impl GameMessage for Vec<u32> {
79    fn mask(&mut self) {
80        self.fill(0);
81    }
82}
83
84impl<T> MaskedClone for T where T: GameMessage + Clone {}
85
86macro_rules! impl_mask_for_message {
87    ($($message_name:ident=$message_flag:literal),*) => {
88        impl GameMessage for Message {
89            fn mask(&mut self) {
90                match self {
91                    $(Message::$message_name(inner) => inner.mask()),*
92                }
93            }
94            fn should_mask(&self, player: CorePlayer) -> bool {
95                match self {
96                    $(Message::$message_name(inner) => inner.should_mask(player)),*
97                }
98            }
99            fn waiting_for(&self) -> Option<CorePlayer> {
100                match self {
101                    $(Message::$message_name(inner) => inner.waiting_for()),*
102                }
103            }
104        }
105    };
106}
107every_game_message_flat_message!(impl_mask_for_message);
108
109#[binrw]
110#[derive(Debug, Clone, Message, GameMessage)]
111#[message(gm, flag = 1)]
112#[repr(C)] 
113pub struct Retry;
114
115#[derive(Debug, Message, Clone, GameMessage)]
116#[message(gm, flag = 2)]
117#[binrw]
118#[repr(C)]
119pub struct Hint {
120    pub _type: crate::constants::Hint,
121    pub player: CorePlayer,
122    pub data: i32
123}
124
125#[binrw]
126#[derive(Debug, Clone, Message, GameMessage)]
127#[message(gm, flag = 3)]
128#[repr(C)]
129pub struct Waiting;
130
131#[binrw]
132#[derive(Debug, Clone, Message, GameMessage)]
133#[message(gm, flag = 4)]
134#[repr(C)]
135pub struct Start {
136    pub player_type: u8,
137    pub rule: MasterRule,
138    pub player1_lp: i32,
139    pub player2_lp: i32,
140    pub player1_deck_count: u16,
141    pub player1_extra_count: u16,
142    pub player2_deck_count: u16,
143    pub player2_extra_count: u16
144}
145
146#[binrw]
147#[derive(Debug, Clone, Message, GameMessage)]
148#[message(gm, flag = 5)]
149#[repr(C)]
150pub struct Win {
151    pub winner: CorePlayer,
152    pub reason: WinReason
153}
154
155#[binrw]
156#[derive(Debug, Clone, Message, GameMessage)]
157#[message(gm, flag = 6)]
158#[repr(C)]
159pub struct UpdateData {
160    pub player: CorePlayer,
161    pub location: Location,
162    #[br(parse_with=until_eof)]
163    #[mask]
164    #[mask_if(self.player != player)]
165    pub data: Vec<UpdateCardInfo>
166}
167
168#[binrw]
169#[derive(Debug, Clone, Message, GameMessage)]
170#[message(gm, flag = 7)]
171#[repr(C)]
172pub struct UpdateCard {
173    pub position: CardPosition<false, false, false>,
174    #[mask]
175    #[mask_if(self.position.controller != player && self.data.should_mask(player))]
176    pub data: UpdateCardInfo
177}
178
179#[binrw]
180#[derive(Debug, Clone, Message, GameMessage)]
181#[message(gm, flag = 8)]
182#[repr(C)]
183pub struct RequestDeck;
184
185#[binrw]
186#[derive(Debug, Clone, Message, GameMessage)]
187#[message(gm, flag = 10)]
188#[repr(C)]
189pub struct SelectBattleCommand {
190    #[wait_for]
191    pub selecting_player: CorePlayer,
192    #[bw(calc(activatable_cards.len() as u8))]
193    activatable_cards_size: u8,
194    #[br(count = activatable_cards_size)]
195    pub activatable_cards: Vec<CardPosition<true, false, true>>,
196    #[bw(calc(attackable_cards.len() as u8))]
197    attackable_cards_size: u8,
198    #[br(count = attackable_cards_size)]
199    pub attackable_cards: Vec<(CardPosition<true, false, false>, i8)>, // Diratt
200    #[br(map=|v:u8| v>0)]
201    #[bw(map=|v| if *v {1u8} else {0u8})]
202    pub can_enter_m2: bool, // u8
203    #[br(map=|v:u8| v>0)]
204    #[bw(map=|v| if *v {1u8} else {0u8})]
205    pub can_enter_ep: bool  // u8
206}
207
208#[binrw]
209#[derive(Debug, Clone, Message, GameMessage)]
210#[message(gm, flag = 11)]
211#[repr(C)]
212pub struct SelectIdleCommand {
213    #[wait_for]
214    pub selecting_player: CorePlayer,
215    #[bw(calc(summonable_cards.len() as u8))]
216    summonable_cards_size: u8,
217    #[br(count = summonable_cards_size)]
218    pub summonable_cards: Vec<CardPosition<true, false, false>>,
219    #[bw(calc(special_summonable_cards.len() as u8))]
220    special_summonable_cards_size: u8,
221    #[br(count = special_summonable_cards_size)]
222    pub special_summonable_cards: Vec<CardPosition<true, false, false>>,
223    #[bw(calc(repositionable_cards.len() as u8))]
224    repositionable_cards_size: u8,
225    #[br(count = repositionable_cards_size)]
226    pub repositionable_cards: Vec<CardPosition<true, false, false>>,
227    #[bw(calc(m_setable_cards.len() as u8))]
228    m_setable_cards_size: u8,
229    #[br(count = m_setable_cards_size)]
230    pub m_setable_cards: Vec<CardPosition<true, false, false>>,
231    #[bw(calc(s_setable_cards.len() as u8))]
232    s_setable_cards_size: u8,
233    #[br(count = s_setable_cards_size)]
234    pub s_setable_cards: Vec<CardPosition<true, false, false>>,
235    #[bw(calc(activatable_cards.len() as u8))]
236    activatable_cards_size: u8,
237    #[br(count = activatable_cards_size)]
238    pub activatable_cards: Vec<CardPosition<true, false, true>>,
239    #[br(map=|v:u8| v>0)]
240    #[bw(map=|v| if *v {1u8} else {0u8})]
241    pub can_enter_bp: bool, // u8
242    #[br(map=|v:u8| v>0)]
243    #[bw(map=|v| if *v {1u8} else {0u8})]
244    pub can_enter_ep: bool, // u8
245    #[br(map=|v:u8| v>0)]
246    #[bw(map=|v| if *v {1u8} else {0u8})]
247    pub can_shuffle_hand: bool // u8
248}
249
250#[binrw]
251#[derive(Debug, Clone, Message, GameMessage)]
252#[message(gm, flag = 12)]
253#[repr(C)]
254pub struct SelectEffectYesNo {
255    #[wait_for]
256    pub selecting_player: CorePlayer,
257    pub code: CardCode,
258    pub card_position: InfoLocation,
259    pub description: i32,
260}
261
262#[binrw]
263#[derive(Debug, Clone, Message, GameMessage)]
264#[message(gm, flag = 13)]
265#[repr(C)]
266pub struct SelectYesNo {
267    #[wait_for]
268    pub selecting_player: CorePlayer,
269    pub description: i32
270}
271
272#[binrw]
273#[derive(Debug, Clone, Message, GameMessage)]
274#[message(gm, flag = 14)]
275#[repr(C)]
276pub struct SelectOption {
277    #[wait_for]
278    pub selecting_player: CorePlayer,
279    #[bw(calc(options.len() as u8))]
280    options_size: u8,
281    #[br(count = options_size)]
282    pub options: Vec<i32>
283}
284
285#[binrw]
286#[derive(Debug, Clone, Message)]
287#[message(gm, flag = 15)]
288#[repr(C)]
289pub struct SelectCard {
290    pub selecting_player: CorePlayer,
291    #[br(map=|v:u8| v>0)]
292    #[bw(map=|v| if *v {1u8} else {0u8})]
293    pub select_cancelable: bool,
294    pub select_min: u8,
295    pub select_max: u8,
296    #[bw(calc(positions.len() as u8))]
297    positions_size: u8,
298    #[br(count = positions_size)]
299    pub positions: Vec<(CardCode, InfoLocation)>
300}
301
302impl GameMessage for SelectCard {
303    fn mask(&mut self) {
304        for (code, position) in &mut self.positions {
305            if position.controller != self.selecting_player {
306                code.mask();
307            }
308        }
309    }
310    fn should_mask(&self, _player: CorePlayer) -> bool {
311        true
312    }
313    fn waiting_for(&self) -> Option<CorePlayer> {
314        Some(self.selecting_player)
315    }
316}
317
318#[binrw]
319#[derive(Debug, Clone)]
320pub struct Chain {
321    pub flag: u8,
322    #[br(map = |v: u8| v != 0)]
323    #[bw(map = |v: &bool| *v as u8)]
324    pub forced: bool,
325    pub code: CardCode,
326    pub location: InfoLocation,
327    pub description: i32,
328}
329
330#[binrw]
331#[derive(Debug, Clone, Message, GameMessage)]
332#[message(gm, flag = 16)]
333#[repr(C)]
334pub struct SelectChain {
335    #[wait_for]
336    pub selecting_player: CorePlayer,
337    #[bw(calc(activatable_cards.len() as u8))]
338    pub activatable_cards_count: u8,
339    pub special_count: u8,
340    pub hint0: i32,
341    pub hint1: i32,
342    #[br(count = activatable_cards_count)]
343    pub activatable_cards: Vec<Chain>,
344}
345
346#[binrw]
347#[derive(Debug, Clone, Message, GameMessage)]
348#[message(gm, flag = 18)]
349#[repr(C)]
350pub struct SelectPlace {
351    #[wait_for]
352    pub selecting_player: CorePlayer,
353    pub count: u8,
354    pub unselectable_field: i32,
355}
356
357#[binrw]
358#[derive(Debug, Clone, Message, GameMessage)]
359#[message(gm, flag = 19)]
360#[repr(C)]
361pub struct SelectPosition {
362    #[wait_for]
363    pub selecting_player: CorePlayer,
364    pub code: u32,
365    pub positions: Position
366}
367
368#[binrw]
369#[derive(Debug, Clone, Message)]
370#[message(gm, flag = 20)]
371#[repr(C)]
372pub struct SelectTribute {
373    pub selecting_player: CorePlayer,
374    #[br(map=|v:u8| v>0)]
375    #[bw(map=|v| if *v {1u8} else {0u8})]
376    pub cancelable: bool,
377    pub select_min: u8,
378    pub select_max: u8,
379    #[bw(calc(tributes.len() as u8))]
380    tributes_size: u8,
381    #[br(count = tributes_size)]
382    pub tributes: Vec<(CardPosition<true, false, false>, i8)>
383}
384
385impl GameMessage for SelectTribute {
386    fn mask(&mut self) {
387        for (card_position, _) in &mut self.tributes {
388            if card_position.controller != self.selecting_player {
389                card_position.code.mask();
390            }
391        }
392    }
393    fn should_mask(&self, _player: CorePlayer) -> bool {
394        true
395    }
396    fn waiting_for(&self) -> Option<CorePlayer> {
397        Some(self.selecting_player)
398    }
399}
400
401#[binrw]
402#[derive(Debug, Clone, Message, GameMessage)]
403#[message(gm, flag = 21)]
404#[repr(C)]
405pub struct SortChain;
406
407#[binrw]
408#[derive(Debug, Clone, Message, GameMessage)]
409#[message(gm, flag = 22)]
410#[repr(C)]
411pub struct SelectCounter {
412    #[wait_for]
413    pub selecting_player: CorePlayer,
414    pub select_counter_type: u16,
415    pub select_counter_count: u16,
416    #[bw(calc(selectable_cards.len() as u8))]
417    selectable_cards_size: u8,
418    #[br(count = selectable_cards_size)]
419    pub selectable_cards: Vec<(CardPosition<true, false, false>, i16)>
420}
421
422#[binrw]
423#[derive(Debug, Clone, Message, GameMessage)]
424#[message(gm, flag = 23)]
425#[repr(C)]
426pub struct SelectSum {
427    pub select_mode: SelectSumMode,
428    #[wait_for]
429    pub selecting_player: CorePlayer,
430    pub select_sum_value: i32,
431    pub select_min: u8,
432    pub select_max: u8,
433    #[bw(calc(must_select_cards.len() as u8))]
434    must_select_cards_size: u8,
435    #[br(count = must_select_cards_size)]
436    pub must_select_cards: Vec<(CardPosition<true, false, false>, i32)>, // OpParam
437    #[bw(calc(select_cards.len() as u8))]
438    select_cards_size: u8,
439    #[br(count = select_cards_size)]
440    pub select_cards: Vec<(CardPosition<true, false, false>, i32)> // OpParam
441}
442
443#[binrw]
444#[derive(Debug, Clone, Message, GameMessage)]
445#[message(gm, flag = 24)]
446#[repr(C)]
447pub struct SelectDisableField {
448    #[wait_for]
449    pub selecting_player: CorePlayer,
450    pub count: u8,
451    pub unselectable_field: i32,
452}
453
454#[binrw]
455#[derive(Debug, Clone, Message, GameMessage)]
456#[message(gm, flag = 25)]
457#[repr(C)]
458pub struct SortCard {
459    #[wait_for]
460    pub player: CorePlayer,
461    #[bw(calc(cards.len() as u8))]
462    cards_size: u8,
463    #[br(count = cards_size)]
464    pub cards: Vec<CardPosition<true, false, false>>
465}
466
467#[binrw]
468#[derive(Debug, Clone, Message)]
469#[message(gm, flag = 26)]
470#[repr(C)]
471pub struct SelectUnselectCard {
472    pub selecting_player: CorePlayer,
473    #[br(map=|v:u8| v>0)]
474    #[bw(map=|v| if *v {1u8} else {0u8})]
475    pub able: bool,
476    #[br(map=|v:u8| v>0)]
477    #[bw(map=|v| if *v {1u8} else {0u8})]
478    pub cancelable: bool,
479    pub select_min: u8,
480    pub select_max: u8,
481    #[bw(calc(positions1.len() as u8))]
482    positions1_size: u8,
483    #[br(count = positions1_size)]
484    pub positions1: Vec<(CardCode, InfoLocation)>,
485    #[bw(calc(positions2.len() as u8))]
486    positions2_size: u8,
487    #[br(count = positions2_size)]
488    pub positions2: Vec<(CardCode, InfoLocation)>
489}
490
491impl GameMessage for SelectUnselectCard {
492    fn mask(&mut self) {
493        for (code, position) in &mut self.positions1 {
494            if position.controller != self.selecting_player {
495                code.mask();
496            }
497        }
498        for (code, position) in &mut self.positions2 {
499            if position.controller != self.selecting_player {
500                code.mask();
501            }
502        }
503    }
504    fn should_mask(&self, _player: CorePlayer) -> bool {
505        true
506    }
507    fn waiting_for(&self) -> Option<CorePlayer> {
508        Some(self.selecting_player)
509    }
510}
511
512#[binrw]
513#[derive(Clone, Debug, Message, GameMessage)]
514#[message(gm, flag = 30)]
515pub struct ConfirmDecktop {
516    pub controller: CorePlayer,
517    #[bw(calc(codes.len() as u8))]
518    codes_size: u8,
519    #[br(count = codes_size)]
520    pub codes: Vec<CardPosition<true, false, false>>
521}
522
523#[binrw]
524#[derive(Debug, Clone, Message, GameMessage)]
525#[message(gm, flag = 31)]
526#[repr(C)]
527pub struct ConfirmCards {
528    pub player: CorePlayer,
529    #[br(map=|v:u8| v>0)]
530    #[bw(map=|v| if *v {1u8} else {0u8})]
531    pub skip_panel: bool, //u8
532    #[bw(calc(cards.len() as u8))]
533    cards_size: u8,
534    #[br(count = cards_size)]
535    pub cards: Vec<CardPosition<true, false, false>>
536}
537
538#[binrw]
539#[derive(Debug, Clone, Message, GameMessage)]
540#[message(gm, flag = 32)]
541#[repr(C)]
542pub struct ShuffleDeck {
543    pub player: CorePlayer 
544}
545
546#[binrw]
547#[derive(Debug, Clone, Message, GameMessage)]
548#[message(gm, flag = 33)]
549#[repr(C)]
550pub struct ShuffleHand {
551    pub player: CorePlayer,
552    #[bw(calc(codes.len() as u8))]
553    pub count: u8,
554    #[br(count = count)]
555    #[mask]
556    #[mask_if(self.player != player)]
557    pub codes: Vec<u32>
558}
559
560#[binrw]
561#[derive(Debug, Clone, Message, GameMessage)]
562#[message(gm, flag = 34)]
563#[repr(C)]
564pub struct RefreshDeck {
565    pub player: CorePlayer
566}
567
568#[binrw]
569#[derive(Debug, Clone, Message, GameMessage)]
570#[message(gm, flag = 35)]
571#[repr(C)]
572pub struct SwapGraveDeck {
573    pub player: CorePlayer 
574}
575
576#[binrw]
577#[derive(Debug, Clone, Message, GameMessage)]
578#[message(gm, flag = 36)]
579pub struct ShuffleSetCard {
580    pub location: Location,
581    #[bw(calc(old_locations.len() as u8), assert(old_locations.len() == new_locations.len(), "ShuffleSetCard: old/new location count mismatch"))]
582    count: u8,
583    #[br(count = count)]
584    pub old_locations: Vec<InfoLocation>,
585    #[br(count = count)]
586    pub new_locations: Vec<InfoLocation>,
587}
588
589#[binrw]
590#[derive(Debug, Clone, Message, GameMessage)]
591#[message(gm, flag = 37)]
592#[repr(C)]
593pub struct ReverseDeck;
594
595#[binrw]
596#[derive(Debug, Clone, Message, GameMessage)]
597#[message(gm, flag = 38)]
598#[repr(C)]
599pub struct DeckTop {
600    pub player: CorePlayer,
601    pub sequence: u8,
602    pub code: CardCode
603}
604
605#[binrw]
606#[derive(Debug, Clone, Message, GameMessage)]
607#[message(gm, flag = 39)]
608#[repr(C)]
609pub struct ShuffleExtra {
610    pub player: CorePlayer,
611    #[bw(calc(codes.len() as u8))]
612    pub count: u8,
613    #[br(count = count)]
614    #[mask]
615    #[mask_if(self.player != player)]
616    pub codes: Vec<u32>
617}
618
619#[binrw]
620#[derive(Debug, Clone, Message, GameMessage)]
621#[message(gm, flag = 40)]
622#[repr(C)]
623pub struct NewTurn {
624    pub player: CorePlayer 
625}
626
627#[binrw]
628#[derive(Debug, Clone, Message, GameMessage)]
629#[message(gm, flag = 41)]
630#[repr(C)]
631pub struct NewPhase {
632    pub phase: crate::constants::Phase,
633}
634
635#[binrw]
636#[derive(Debug, Clone, Message, GameMessage)]
637#[message(gm, flag = 42)]
638#[repr(C)]
639pub struct ConfirmExtraTop {
640    pub player: CorePlayer,
641    #[bw(calc(selectable_cards.len() as u8))]
642    selectable_cards_size: u8,
643    #[br(count = selectable_cards_size)]
644    pub selectable_cards: Vec<CardPosition<true,false,false>>
645}
646
647#[binrw]
648#[derive(Debug, Clone, Message, GameMessage)]
649#[message(gm, flag = 50)]
650#[repr(C)]
651pub struct Move {
652    #[mask(if self.should_mask(CorePlayer::None) { 0 } else { self.code })]
653    #[mask_if(self.current.controller != player && !self.current.location.intersects(Location::Grave | Location::Overlay) && (self.current.location.intersects(Location::Deck | Location::Hand) || self.current.position.is_face_down()))]
654    pub code: i32,
655    pub previous: InfoLocation,
656    pub current: InfoLocation,
657    pub reason: crate::constants::Reason
658}
659
660#[binrw]
661#[derive(Debug, Clone, Message, GameMessage)]
662#[message(gm, flag = 53)]
663#[repr(C)]
664pub struct PositionChange {
665    pub card: u32,
666    pub controller: CorePlayer,
667    pub location: Location,
668    pub sequence: u8,
669    pub previous_position: Position,
670    pub current_position: Position
671}
672
673#[binrw]
674#[derive(Debug, Clone, Message, GameMessage)]
675#[message(gm, flag = 54)]
676#[repr(C)]
677pub struct Set {
678    #[mask]
679    pub code: i32,
680    pub position: InfoLocation
681}
682
683#[binrw]
684#[derive(Debug, Clone, Message, GameMessage)]
685#[message(gm, flag = 55)]
686#[repr(C)]
687pub struct Swap {
688    pub code1: CardCode,
689    pub position1: InfoLocation,
690    pub code2: CardCode,
691    pub position2: InfoLocation,
692}
693
694#[binrw]
695#[derive(Debug, Clone, Message, GameMessage)]
696#[message(gm, flag = 56)]
697#[repr(C)]
698pub struct FieldDisabled {
699    pub disabled: i32
700}
701
702#[binrw]
703#[derive(Debug, Clone, Message, GameMessage)]
704#[message(gm, flag = 60)]
705#[repr(C)]
706pub struct Summoning {
707    pub code: u32,
708    pub position: InfoLocation,
709}
710
711#[binrw]
712#[derive(Debug, Clone, Message, GameMessage)]
713#[message(gm, flag = 61)]
714#[repr(C)]
715pub struct Summoned;
716
717#[binrw]
718#[derive(Debug, Clone, Message, GameMessage)]
719#[message(gm, flag = 62)]
720#[repr(C)]
721pub struct SpecialSummoning {
722    #[mask]
723    #[mask_if(self.position.position.is_face_down() && self.position.controller != player)]
724    pub code: u32,
725    pub position: InfoLocation,
726}
727
728#[binrw]
729#[derive(Debug, Clone, Message, GameMessage)]
730#[message(gm, flag = 63)]
731#[repr(C)]
732pub struct SpecialSummoned;
733
734#[binrw]
735#[derive(Debug, Clone, Message, GameMessage)]
736#[message(gm, flag = 64)]
737#[repr(C)]
738pub struct FlipSummoning {
739    pub code: u32,
740    pub position: InfoLocation,
741}
742
743#[binrw]
744#[derive(Debug, Clone, Message, GameMessage)]
745#[message(gm, flag = 65)]
746#[repr(C)]
747pub struct FlipSummoned;
748
749#[binrw]
750#[derive(Debug, Clone, Message, GameMessage)]
751#[message(gm, flag = 70)]
752#[repr(C)]
753pub struct Chaining {
754    pub card: u32,
755    pub previous: CardPosition<false, true, false>,
756    pub current: CardPosition<false, false, true>,
757    pub chain_count: u8
758}
759
760#[binrw]
761#[derive(Debug, Clone, Message, GameMessage)]
762#[message(gm, flag = 71)]
763#[repr(C)]
764pub struct Chained {
765    pub chain_index: u8
766}
767
768#[binrw]
769#[derive(Debug, Clone, Message, GameMessage)]
770#[message(gm, flag = 72)]
771#[repr(C)]
772pub struct ChainSolving {
773    pub chain_index: u8
774}
775
776#[binrw]
777#[derive(Debug, Clone, Message, GameMessage)]
778#[message(gm, flag = 73)]
779#[repr(C)]
780pub struct ChainSolved {
781    pub chain_index: u8
782}
783
784#[binrw]
785#[derive(Debug, Clone, Message, GameMessage)]
786#[message(gm, flag = 74)]
787#[repr(C)]
788pub struct ChainEnd;
789
790#[binrw]
791#[derive(Debug, Clone, Message, GameMessage)]
792#[message(gm, flag = 75)]
793#[repr(C)]
794pub struct ChainNegated {
795    pub chain_index: u8
796}
797
798#[binrw]
799#[derive(Debug, Clone, Message, GameMessage)]
800#[message(gm, flag = 76)]
801#[repr(C)]
802pub struct ChainDisabled {
803    pub chain_index: u8
804}
805
806#[binrw]
807#[derive(Debug, Clone, Message, GameMessage)]
808#[message(gm, flag = 80)]
809#[repr(C)]
810pub struct CardSelected {
811    pub player: CorePlayer,
812    #[bw(calc(pcards.len() as u8))]
813    pcards_size: u8,
814    #[br(count = pcards_size)]
815    pub pcards: Vec<InfoLocation>
816}
817
818#[binrw]
819#[derive(Debug, Clone, Message, GameMessage)]
820#[message(gm, flag = 81)]
821#[repr(C)]
822pub struct RandomSelected {
823    pub player: CorePlayer,
824    #[bw(calc(pcards.len() as u8))]
825    pcards_size: u8,
826    #[br(count = pcards_size)]
827    pub pcards: Vec<InfoLocation>
828}
829
830#[binrw]
831#[derive(Debug, Clone, Message, GameMessage)]
832#[message(gm, flag = 83)]
833#[repr(C)]
834pub struct BecomeTarget {
835    #[bw(calc(pcards.len() as u8))]
836    pcards_size: u8,
837    #[br(count = pcards_size)]
838    pub pcards: Vec<InfoLocation>
839}
840
841#[binrw]
842#[derive(Debug, Clone, Message, GameMessage)]
843#[message(gm, flag = 90)]
844#[repr(C)]
845pub struct Draw {
846    pub player: CorePlayer,
847    #[bw(calc(codes.len() as u8))]
848    codes_size: u8,
849    #[br(count = codes_size)]
850    #[mask]
851    #[mask_if(self.player != player)]
852    pub codes: Vec<CardCode>,
853}
854
855#[bitfield]
856#[derive(BinRead, BinWrite, Debug, Copy, Clone, PartialEq, Eq, Default)]
857#[br(map = Self::from_bytes)]
858#[bw(map = |&x| Self::into_bytes(x))]
859#[repr(u32)]
860pub struct CardCode {
861    pub id: B28,
862    #[skip] __: B3,
863    pub is_public: bool,
864}
865
866impl GameMessage for CardCode {
867    fn mask(&mut self) {
868        if !self.is_public() {
869            self.set_id(0);
870        }
871    }
872    fn should_mask(&self, _player: CorePlayer) -> bool {
873        !self.is_public()
874    }
875}
876
877impl<T: GameMessage> GameMessage for (T,) {
878    fn mask(&mut self) {
879        self.0.mask();
880    }
881    fn should_mask(&self, player: CorePlayer) -> bool {
882        self.0.should_mask(player)
883    }
884}
885
886#[binrw]
887#[derive(Debug, Clone, Message, GameMessage)]
888#[message(gm, flag = 91)]
889#[repr(C)]
890pub struct Damage {
891    pub player: CorePlayer,
892    pub value: i32
893}
894
895#[binrw]
896#[derive(Debug, Clone, Message, GameMessage)]
897#[message(gm, flag = 92)]
898#[repr(C)]
899pub struct Recover {
900    pub player: CorePlayer,
901    pub value: i32
902}
903
904#[binrw]
905#[derive(Debug, Clone, Message, GameMessage)]
906#[message(gm, flag = 93)]
907#[repr(C)]
908pub struct Equip {
909    pub position1: InfoLocation,
910    pub position2: InfoLocation,
911}
912
913#[binrw]
914#[derive(Debug, Clone, Message, GameMessage)]
915#[message(gm, flag = 94)]
916#[repr(C)]
917pub struct LPUpdate {
918    pub player: CorePlayer,
919    pub lp: i32
920}
921
922#[binrw]
923#[derive(Debug, Clone, Message, GameMessage)]
924#[message(gm, flag = 95)]
925#[repr(C)]
926pub struct Unequip {
927    pub position: InfoLocation
928}
929
930#[binrw]
931#[derive(Debug, Clone, Message, GameMessage)]
932#[message(gm, flag = 96)]
933#[repr(C)]
934pub struct CardTarget {
935    pub position1: InfoLocation,
936    pub position2: InfoLocation,
937}
938
939#[binrw]
940#[derive(Debug, Clone, Message, GameMessage)]
941#[message(gm, flag = 97)]
942#[repr(C)]
943pub struct CancelTarget {
944    pub position1: InfoLocation,
945    pub position2: InfoLocation,
946}
947
948#[binrw]
949#[derive(Debug, Clone, Message, GameMessage)]
950#[message(gm, flag = 100)]
951#[repr(C)]
952pub struct PayLPCost {
953    pub player: CorePlayer,
954    pub cost: i32
955}
956
957#[binrw]
958#[derive(Debug, Clone, Message, GameMessage)]
959#[message(gm, flag = 101)]
960#[repr(C)]
961pub struct AddCounter {
962    pub counter_type: u16,
963    pub position: CardPosition<false, false, false>,
964    pub count: u16
965}
966
967#[binrw]
968#[derive(Debug, Clone, Message, GameMessage)]
969#[message(gm, flag = 102)]
970#[repr(C)]
971pub struct RemoveCounter {
972    pub counter_type: u16,
973    pub position: CardPosition<false, false, false>,
974    pub count: u16
975}
976
977#[binrw]
978#[derive(Debug, Clone, Message, GameMessage)]
979#[message(gm, flag = 110)]
980#[repr(C)]
981pub struct Attack {
982    pub attacker: InfoLocation,
983    pub defenser: InfoLocation,
984}
985
986#[binrw]
987#[derive(Debug, Clone, Message, GameMessage)]
988#[message(gm, flag = 111)]
989#[repr(C)]
990pub struct Battle {
991    pub attacker: InfoLocation,
992    pub attacker_attack: i32,
993    pub attacker_defense: i32,
994    #[br(map = |v: u8| v != 0)]
995    #[bw(map = |v: &bool| *v as u8)]
996    pub attacker_destroyed: bool,
997    pub defenser: InfoLocation,
998    pub defenser_attack: i32,
999    pub defenser_defense: i32,
1000    #[br(map = |v: u8| v != 0)]
1001    #[bw(map = |v: &bool| *v as u8)]
1002    pub defender_destroyed: bool,
1003}
1004
1005#[binrw]
1006#[derive(Debug, Clone, Message, GameMessage)]
1007#[message(gm, flag = 112)]
1008#[repr(C)]
1009pub struct AttackDisabled;
1010
1011#[binrw]
1012#[derive(Debug, Clone, Message, GameMessage)]
1013#[message(gm, flag = 113)]
1014#[repr(C)]
1015pub struct DamageStepStart;
1016
1017#[binrw]
1018#[derive(Debug, Clone, Message, GameMessage)]
1019#[message(gm, flag = 114)]
1020#[repr(C)]
1021pub struct DamageStepEnd;
1022
1023#[binrw]
1024#[derive(Debug, Clone, Message, GameMessage)]
1025#[message(gm, flag = 120)]
1026#[repr(C)]
1027pub struct MissedEffect {
1028    pub location: InfoLocation,
1029    pub code: i32
1030}
1031
1032#[binrw]
1033#[derive(Debug, Clone, Message, GameMessage)]
1034#[message(gm, flag = 121)]
1035#[repr(C)]
1036pub struct BeChainTarget;
1037
1038#[binrw]
1039#[derive(Debug, Clone, Message, GameMessage)]
1040#[message(gm, flag = 122)]
1041#[repr(C)]
1042pub struct CreateRelation;
1043
1044#[binrw]
1045#[derive(Debug, Clone, Message, GameMessage)]
1046#[message(gm, flag = 123)]
1047#[repr(C)]
1048pub struct ReleaseRelation;
1049
1050#[binrw]
1051#[derive(Debug, Clone, Message, GameMessage)]
1052#[message(gm, flag = 130)]
1053#[repr(C)]
1054pub struct TossCoin {
1055    pub player: CorePlayer,
1056    #[bw(calc(result.len() as u8))]
1057    result_size: u8,
1058    #[br(count = result_size)]
1059    pub result: Vec<i8>
1060}
1061
1062#[binrw]
1063#[derive(Debug, Clone, Message, GameMessage)]
1064#[message(gm, flag = 131)]
1065#[repr(C)]
1066pub struct TossDice {
1067    pub player: CorePlayer,
1068    #[bw(calc(result.len() as u8))]
1069    result_size: u8,
1070    #[br(count = result_size)]
1071    pub result: Vec<i8>
1072}
1073
1074#[binrw]
1075#[derive(Debug, Clone, Message, GameMessage)]
1076#[message(gm, flag = 132)]
1077#[repr(C)]
1078pub struct RockPaperScissors {
1079    #[wait_for]
1080    pub player: CorePlayer
1081}
1082
1083#[binrw]
1084#[derive(Debug, Clone, Message, GameMessage)]
1085#[message(gm, flag = 133)]
1086#[repr(C)]
1087pub struct HandResult {
1088    #[br(temp)]
1089    #[bw(calc = u8::from(*hand0) | (u8::from(*hand1) << 2))]
1090    _packed: u8,
1091    #[br(calc = Hand::try_from_primitive(_packed & 0x03).unwrap())]
1092    #[bw(ignore)]
1093    pub hand0: Hand,
1094    #[br(calc = Hand::try_from_primitive((_packed >> 2) & 0x03).unwrap())]
1095    #[bw(ignore)]
1096    pub hand1: Hand,
1097}
1098
1099#[binrw]
1100#[derive(Debug, Clone, Message, GameMessage)]
1101#[message(gm, flag = 140)]
1102#[repr(C)]
1103pub struct AnnounceRace {
1104    #[wait_for]
1105    pub player: CorePlayer,
1106    pub announce_count: u8,
1107    pub available: Race
1108}
1109
1110#[binrw]
1111#[derive(Debug, Clone, Message, GameMessage)]
1112#[message(gm, flag = 141)]
1113#[repr(C)]
1114pub struct AnnounceAttribute {
1115    #[wait_for]
1116    pub player: CorePlayer,
1117    pub announce_count: u8,
1118    pub available: Attribute
1119}
1120
1121#[binrw]
1122#[derive(Debug, Clone, Message, GameMessage)]
1123#[message(gm, flag = 142)]
1124#[repr(C)]
1125pub struct AnnounceCard {
1126    #[wait_for]
1127    pub player: CorePlayer,
1128    #[bw(calc(value.len() as u8))]
1129    value_size: u8,
1130    #[br(count = value_size)]
1131    pub value: Vec<Operation>
1132}
1133
1134#[binrw]
1135#[derive(Debug, Clone, Message, GameMessage)]
1136#[message(gm, flag = 143)]
1137#[repr(C)]
1138pub struct AnnounceNumber {
1139    #[wait_for]
1140    pub player: CorePlayer,
1141    #[bw(calc(value.len() as u8))]
1142    value_size: u8,
1143    #[br(count = value_size)]
1144    pub value: Vec<i32>
1145}
1146
1147#[binrw]
1148#[derive(Debug, Clone, Message, GameMessage)]
1149#[message(gm, flag = 160)]
1150#[repr(C)]
1151pub struct CardHint {
1152    pub position: InfoLocation,
1153    pub card_hint_type: crate::constants::CardHint,
1154    pub value: i32,
1155}
1156
1157#[binrw]
1158#[derive(Debug, Clone, Message, GameMessage)]
1159#[message(gm, flag = 161)]
1160#[repr(C)]
1161pub struct TagSwap {
1162    pub player: CorePlayer,
1163    pub main_count: u8,
1164    #[bw(calc(extra_codes.len() as u8))]
1165    pub extra_count: u8,
1166    pub extra_p_count: u8,
1167    #[bw(calc(hand_codes.len() as u8))]
1168    pub hand_count: u8,
1169    pub top_code: i32,
1170    #[br(count = hand_count)]
1171    #[mask]
1172    #[mask_if(self.player != player)]
1173    pub hand_codes: Vec<CardCode>,
1174    #[br(count = extra_count)]
1175    #[mask]
1176    #[mask_if(self.player != player)]
1177    pub extra_codes: Vec<CardCode>,
1178}
1179
1180#[binrw]
1181#[derive(Debug, Clone, Default)]
1182pub struct MzoneSlot {
1183    pub occupied: u8,
1184    #[br(if(occupied != 0))]
1185    #[bw(if(*occupied != 0))]
1186    pub position: u8,
1187    #[br(if(occupied != 0))]
1188    #[bw(if(*occupied != 0))]
1189    pub xyz_count: u8,
1190}
1191
1192#[binrw]
1193#[derive(Debug, Clone)]
1194pub struct SzonaSlot {
1195    pub occupied: u8,
1196    #[br(if(occupied != 0, Position::Any))]
1197    #[bw(if(*occupied != 0))]
1198    pub position: Position,
1199}
1200
1201#[binrw]
1202#[derive(Debug, Clone)]
1203pub struct PlayerField {
1204    pub lp: i32,
1205    pub mzone: [MzoneSlot; 7],
1206    pub szone: [SzonaSlot; 8],
1207    pub main_count: u8,
1208    pub hand_count: u8,
1209    pub grave_count: u8,
1210    pub remove_count: u8,
1211    pub extra_count: u8,
1212    pub extra_p_count: u8,
1213}
1214
1215#[binrw]
1216#[derive(Debug, Clone)]
1217pub struct ChainLink {
1218    pub code: u32,
1219    pub card_position: CardPosition<false, true, false>,
1220    pub controller: CorePlayer,
1221    pub location: Location,
1222    pub sequence: i8,
1223    pub description: u32,
1224}
1225
1226#[binrw]
1227#[derive(Debug, Clone, Message, GameMessage)]
1228#[message(gm, flag = 162)]
1229#[repr(C)]
1230pub struct ReloadField {
1231    pub duel_rule: MasterRule,
1232    pub player1: PlayerField,
1233    pub player2: PlayerField,
1234    #[bw(calc(chains.len() as u8))]
1235    pub chain_count: u8,
1236    #[br(count = chain_count)]
1237    pub chains: Vec<ChainLink>,
1238}
1239
1240#[binrw]
1241#[derive(Debug, Clone, Message, GameMessage)]
1242#[message(gm, flag = 163)]
1243pub struct AIName {
1244    #[bw(calc = name.as_bytes().len() as u16)]
1245    len: u16,
1246    #[br(count = len, map = |bytes: Vec<u8>| String::from_utf8(bytes).unwrap())]
1247    #[bw(map = |s: &String| s.as_bytes().to_vec())]
1248    #[brw(pad_after = 1)]
1249    pub name: String,
1250}
1251
1252#[binrw]
1253#[derive(Debug, Clone, Message, GameMessage)]
1254#[message(gm, flag = 164)]
1255pub struct ShowHint {
1256    #[bw(calc = name.as_bytes().len() as u16)]
1257    len: u16,
1258    #[br(count = len, map = |bytes: Vec<u8>| String::from_utf8(bytes).unwrap())]
1259    #[bw(map = |s: &String| s.as_bytes().to_vec())]
1260    #[brw(pad_after = 1)]
1261    pub name: String,
1262}
1263
1264#[binrw]
1265#[derive(Debug, Clone, Message, GameMessage)]
1266#[message(gm, flag = 165)]
1267#[repr(C)]
1268pub struct PlayerHint {
1269    pub player: CorePlayer,
1270    pub player_hint_type: crate::constants::PlayerHint,
1271    pub value: i32
1272}
1273
1274#[binrw]
1275#[derive(Debug, Clone, Message, GameMessage)]
1276#[message(gm, flag = 170)]
1277#[repr(C)]
1278pub struct MatchKill {
1279    pub card_code: u32
1280}
1281
1282#[binrw]
1283#[derive(Debug, Clone, Message, GameMessage)]
1284#[message(gm, flag = 180)]
1285#[repr(C)]
1286pub struct CustomMsg {
1287    #[br(parse_with=until_eof)]
1288    pub data: Vec<u8>
1289}
1290
1291#[cfg(test)]
1292mod test {
1293    use binrw::BinRead;
1294    use binrw::BinWrite;
1295    use std::io::Cursor;
1296
1297    #[test]
1298    fn print_sizes() {
1299        macro_rules! print_size {
1300            ($($msg:ident = $flag:literal),* $(,)?) => {
1301                println!("=== GM ===");
1302                $(
1303                    println!("  {:30}: {:>4} bytes", stringify!($msg), std::mem::size_of::<super::$msg>());
1304                )*
1305                println!("  {:30}: {:>4} bytes", "MessageType", std::mem::size_of::<super::MessageType>());
1306                println!("  {:30}: {:>4} bytes", "Message", std::mem::size_of::<super::Message>());
1307            };
1308        }
1309        every_game_message_flat_message!(print_size);
1310    }
1311
1312    #[test]
1313    fn test_ai_name_roundtrip() {
1314        let original = super::AIName { name: "TestAI".into() };
1315        let mut writer = Cursor::new(Vec::new());
1316        original.write_le(&mut writer).unwrap();
1317        let bytes = writer.into_inner();
1318        assert_eq!(bytes[0..2], [6, 0], "len should be 6");
1319        assert_eq!(&bytes[2..8], b"TestAI", "name bytes");
1320        assert_eq!(bytes[8], 0, "null terminator");
1321        let roundtripped = super::AIName::read_le(&mut Cursor::new(&bytes)).unwrap();
1322        assert_eq!(roundtripped.name, "TestAI");
1323    }
1324}