team04_server/messages/
movement.rs

1use crate::{board::Coord, lobby::state::clients::PlayerId, unit::UnitType};
2
3use super::*;
4
5pub struct Movement {
6    pub(crate) movements_per_match: Vec<PerMatch>,
7}
8#[derive(Serialize)]
9#[serde(rename_all = "camelCase")]
10pub(crate) struct PerMatch {
11    #[serde(rename = "match")]
12    pub(crate) matchup: [PlayerId; 2],
13    pub(crate) current_fight_round: u64,
14    pub(crate) movements: Vec<SingleMovement>,
15}
16#[derive(Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct SingleMovement {
19    pub(crate) unit: UnitType,
20    #[serde(rename = "playerID")]
21    pub(crate) player_id: PlayerId,
22    pub(crate) current_position: [usize; 2],
23    pub(crate) target_position: [usize; 2],
24    // health can drop below zero temporarily on the server side,
25    // but since the standard writes things like (not a direct quote)
26    // "clients should do [...] if health is zero" as opposed to "health <= 0",
27    // it is probably a good idea to represent any negative health (should it
28    // even occur at all) as zero instead of an actual negative number.
29    pub(crate) health: u64,
30}
31impl SingleMovement {
32    pub fn new(
33        unit: UnitType,
34        player_id: PlayerId,
35        current_position: Coord,
36        target_position: Coord,
37        health: u64,
38    ) -> Self {
39        Self {
40            unit,
41            player_id,
42            current_position: [current_position.x, current_position.y],
43            target_position: [target_position.x, target_position.y],
44            health,
45        }
46    }
47}
48
49impl Movement {
50    /// Use this to create a new movement message, then add movements to
51    /// it using [with_movement](Self::with_movement) or [push_movement](Self::push_movement).
52    pub fn new_empty() -> Self {
53        Self {
54            movements_per_match: vec![],
55        }
56    }
57    pub fn is_empty(&self) -> bool {
58        self.movements_per_match.is_empty()
59    }
60    pub(crate) fn coord_to_arr(coord: Coord) -> [usize; 2] {
61        [coord.x, coord.y]
62    }
63    pub fn with_movement(
64        mut self,
65        matchup: (PlayerId, PlayerId),
66        current_fight_round: u64,
67        movement: SingleMovement,
68    ) -> Self {
69        self.push_movement(matchup, current_fight_round, movement);
70        self
71    }
72    pub fn push_movement(
73        &mut self,
74        matchup: (PlayerId, PlayerId),
75        current_fight_round: u64,
76        movement: SingleMovement,
77    ) -> &mut Self {
78        // Note: this code is duplicated, see also messages/attack
79        if let Some(per_match) = self.movements_per_match.iter_mut().find(|m| {
80            (m.matchup[0] == matchup.0 && m.matchup[1] == matchup.1)
81                || (m.matchup[0] == matchup.1 && m.matchup[1] == matchup.0)
82        }) {
83            per_match.movements.push(movement);
84        } else {
85            self.movements_per_match.push(PerMatch {
86                matchup: [matchup.0, matchup.1],
87                current_fight_round,
88                movements: vec![movement],
89            });
90        }
91        self
92    }
93}
94
95impl MessageTx for Movement {
96    fn serialize(&self) -> String {
97        #[derive(Serialize)]
98        #[serde(rename_all = "camelCase")]
99        pub struct Movement<'a> {
100            message_type: &'static str,
101            movements_per_match: &'a [PerMatch],
102        }
103
104        let message = Movement {
105            message_type: "MOVEMENT",
106            movements_per_match: &self.movements_per_match,
107        };
108
109        serialize(&message)
110    }
111}