twgame_core/
lib.rs

1pub mod config;
2pub mod console;
3pub mod database;
4mod display;
5pub mod net_msg;
6pub mod replay;
7
8/// [`teehistorian`](https://crates.io/crates/teehistorian) crate
9pub use teehistorian;
10/// [`TwSnap`](https://crates.io/crates/twsnap) crate
11pub use twsnap;
12
13pub use display::DisplayChunk;
14
15use crate::console::Command;
16use serde::{Deserialize, Serialize};
17use twsnap::time::Instant;
18use twsnap::Snap;
19use vek::Vec2;
20
21#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
22pub struct Input {
23    pub direction: i32,
24    pub target_x: i32,
25    pub target_y: i32,
26    pub jump: i32,
27    pub fire: i32,
28    pub hook: i32,
29    /// range 0 - 256
30    pub player_flags: i32,
31    pub wanted_weapon: i32,
32    pub next_weapon: i32,
33    pub prev_weapon: i32,
34}
35
36impl Input {
37    /// Creates new Input with non-centered cursor
38    pub fn new() -> Input {
39        Input {
40            direction: 0,
41            target_x: 0,
42            target_y: -1,
43            jump: 0,
44            fire: 0,
45            hook: 0,
46            player_flags: 0,
47            wanted_weapon: 0,
48            next_weapon: 0,
49            prev_weapon: 0,
50        }
51    }
52
53    fn disallow_target_center(mut self) -> Self {
54        if self.target_x == 0 && self.target_y == 0 {
55            self.target_y = -1;
56        }
57        self
58    }
59
60    // CInputCount CountInput(int Prev, int Cur)
61    fn count_presses(mut cur: i32, mut prev: i32) -> i32 {
62        // TODO: something like (((cur - prev) & 63) + 1) / 2
63        let mut count = 0;
64        prev &= 63; // TODO: INPUT_STATE_MASK
65        cur &= 63;
66        while prev != cur {
67            prev = (prev + 1) & 63;
68            if prev & 1 != 0 {
69                count += 1;
70            }
71        }
72        count
73    }
74
75    /// count how often weapon next was pressed since the last input
76    pub fn count_weapon_next(&self, prev: &Input) -> i32 {
77        Input::count_presses(self.next_weapon, prev.next_weapon)
78    }
79
80    /// count how often weapon prev was pressed since the last input
81    pub fn count_weapon_prev(&self, prev: &Input) -> i32 {
82        Input::count_presses(self.prev_weapon, prev.prev_weapon)
83    }
84
85    /// count how often weapon fire was pressed since the last input
86    pub fn count_weapon_fire(&self, prev: &Input) -> i32 {
87        Input::count_presses(self.fire, prev.fire)
88    }
89
90    /// returns cursor coordinates converted to f32
91    pub fn cursor(&self) -> Vec2<f32> {
92        Vec2::new(self.target_x as f32, self.target_y as f32)
93    }
94
95    /// returns if at the end of the last input firing was hold
96    pub fn firing(&self) -> bool {
97        (self.fire & 1) != 0
98    }
99
100    /// returns the player flag for whether the spec cam is on
101    pub fn spec_cam_active(&self) -> bool {
102        (self.player_flags & (1 << 5)) != 0
103    }
104}
105
106impl From<[i32; 10]> for Input {
107    fn from(input: [i32; 10]) -> Self {
108        Input {
109            direction: input[0],
110            target_x: input[1],
111            target_y: input[2],
112            jump: input[3],
113            fire: input[4],
114            hook: input[5],
115            // range 0 - 256
116            player_flags: input[6],
117            wanted_weapon: input[7],
118            next_weapon: input[8],
119            prev_weapon: input[9],
120        }
121        .disallow_target_center()
122    }
123}
124
125impl Input {
126    pub fn add_input_diff(&mut self, input_diff: [i32; 10]) {
127        self.direction += input_diff[0];
128        self.target_x += input_diff[1];
129        self.target_y += input_diff[2];
130        self.jump += input_diff[3];
131        self.fire += input_diff[4];
132        self.hook += input_diff[5];
133        self.player_flags += input_diff[6];
134        self.wanted_weapon += input_diff[7];
135        self.next_weapon += input_diff[8];
136        self.prev_weapon += input_diff[9];
137    }
138}
139
140pub trait Game {
141    // input functions
142    fn player_join(&mut self, id: u32);
143    fn player_ready(&mut self, id: u32);
144    fn player_input(&mut self, id: u32, input: &Input);
145    fn player_leave(&mut self, id: u32);
146
147    fn on_net_msg(&mut self, id: u32, msg: &net_msg::ClNetMessage);
148    fn on_command(&mut self, id: u32, command: &Command);
149
150    fn swap_tees(&mut self, id1: u32, id2: u32);
151
152    // actions before player position check in teehistorian
153    fn tick(&mut self, cur_time: Instant);
154
155    /// returns whether there are still players or spawnable entities in the world
156    /// When false, tick doesn't need to be called until next `player_join` event.
157    fn is_empty(&self) -> bool;
158}
159
160pub trait Snapper {
161    fn snap(&self, snapshot: &mut Snap);
162}
163
164/// Implement normalize function, because order of operation is important for physics
165/// teehistorian_replayer_res_physics_1_weapon_shotgun_other_right_teehistorian would fail
166/// otherwise. See commit fcdd5ebd375c57cf0bc4ddd92610d813f68126ad
167pub fn normalize(v: Vec2<f32>) -> Vec2<f32> {
168    // float divisor = length(v);
169    let divisor = v.magnitude();
170    // if(divisor == 0.0f)
171    if divisor == 0.0 {
172        // return vector2_base<float>(0.0f, 0.0f);
173        return Vec2::zero();
174    }
175    // float l = (float)(1.0f / divisor);
176    let l = 1.0 / divisor;
177    // return vector2_base<float>(v.x * l, v.y * l);
178    v * l
179}