team04_server/messages/
game_state.rs

1use player_character::PlayerCharacter;
2
3use crate::{
4    lobby::{
5        game::next_phases,
6        state::{LobbyPhase, LobbyState, clients::PlayerId},
7    },
8    server::state::LobbyId,
9    unit::UnitType,
10};
11
12use super::*;
13
14pub struct GameState<'a> {
15    current_round: u64,
16    current_phase: LobbyPhase,
17    next_phases: Vec<LobbyPhase>,
18    lobby_id: LobbyId,
19    is_paused: bool,
20    players: Vec<Player<'a>>,
21    matches: Vec<[PlayerId; 2]>,
22}
23#[derive(Serialize)]
24#[serde(rename_all = "camelCase")]
25pub struct Player<'a> {
26    #[serde(rename = "playerID")]
27    player_id: PlayerId,
28    name: &'a str,
29    character: PlayerCharacter,
30    is_active: bool,
31    lives: i64,
32    unit_placement: Vec<PlacedUnit>,
33    unit_bank: &'a [UnitType],
34    lightsabers: Lightsabers<'a>,
35}
36
37#[derive(Serialize)]
38struct PlacedUnit {
39    r#type: UnitType,
40    position: [usize; 2],
41}
42
43#[derive(Serialize)]
44#[serde(rename = "SCREAMING_SNAKE_CASE")]
45struct Lightsabers<'a> {
46    red: &'a [f64],
47    green: &'a [f64],
48    blue: &'a [f64],
49}
50
51impl<'a> GameState<'a> {
52    /// This only works on games that have already started!
53    pub fn new_from_lobby_state(lobby_state: &'a LobbyState) -> Self {
54        assert!(lobby_state.game_started());
55        Self {
56            current_round: lobby_state.round,
57            current_phase: lobby_state.phase,
58            next_phases: next_phases(
59                lobby_state.round,
60                lobby_state.configs.game_config.max_rounds,
61                lobby_state.phase,
62            )
63            .take(5)
64            .collect(),
65            lobby_id: lobby_state.id(),
66            is_paused: lobby_state.paused(),
67            players: lobby_state
68                .clients
69                .players
70                .iter_all()
71                .map(|(id, player)| Player {
72                    player_id: *id,
73                    name: player.name(),
74                    character: player.character.unwrap(),
75                    is_active: player.con.as_ref().is_some_and(|con| con.is_active()),
76                    lives: player.lives,
77                    lightsabers: Lightsabers {
78                        red: &player.red_lightsabers,
79                        green: &player.green_lightsabers,
80                        blue: &player.blue_lightsabers,
81                    },
82                    unit_bank: &player.unit_bank,
83                    unit_placement: player
84                        .unit_placement
85                        .iter()
86                        .map(|v| PlacedUnit {
87                            r#type: v.unit_type,
88                            position: [v.position.x, v.position.y],
89                        })
90                        .collect(),
91                })
92                .collect(),
93            matches: lobby_state
94                .matchups
95                .iter()
96                .map(|(p1, p2, _)| [*p1, *p2])
97                .collect(),
98        }
99    }
100}
101
102impl MessageTx for GameState<'_> {
103    fn serialize(&self) -> String {
104        #[derive(Serialize)]
105        #[serde(rename_all = "camelCase")]
106        pub struct GameState<'a, 'b> {
107            message_type: &'static str,
108            current_round: u64,
109            current_phase: LobbyPhase,
110            next_phases: &'b [LobbyPhase],
111            #[serde(rename = "lobbyID")]
112            lobby_id: LobbyId,
113            is_paused: bool,
114            players: &'b [Player<'a>],
115            matches: &'b [[PlayerId; 2]],
116        }
117
118        let message = GameState {
119            message_type: "GAMESTATE",
120            current_round: self.current_round,
121            current_phase: self.current_phase,
122            next_phases: &self.next_phases,
123            lobby_id: self.lobby_id,
124            is_paused: self.is_paused,
125            players: &self.players,
126            matches: &self.matches,
127        };
128
129        serialize(&message)
130    }
131}