1pub mod config;
2pub mod console;
3pub mod database;
4mod display;
5pub mod net_msg;
6pub mod replay;
7
8pub use teehistorian;
10pub use twsnap;
12
13pub use display::DisplayChunk;
14
15use crate::console::Command;
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18use twsnap::time::Instant;
19use twsnap::Snap;
20use uuid::Uuid;
21use vek::Vec2;
22
23#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
24#[repr(C)]
25pub struct Input {
26 pub direction: i32,
27 pub target_x: i32,
28 pub target_y: i32,
29 pub jump: i32,
30 pub fire: i32,
31 pub hook: i32,
32 pub player_flags: i32,
34 pub wanted_weapon: i32,
35 pub next_weapon: i32,
36 pub prev_weapon: i32,
37}
38
39impl Input {
40 pub fn new() -> Input {
42 Input {
43 direction: 0,
44 target_x: 0,
45 target_y: -1,
46 jump: 0,
47 fire: 0,
48 hook: 0,
49 player_flags: 0,
50 wanted_weapon: 0,
51 next_weapon: 0,
52 prev_weapon: 0,
53 }
54 }
55
56 fn disallow_target_center(mut self) -> Self {
57 if self.target_x == 0 && self.target_y == 0 {
58 self.target_y = -1;
59 }
60 self
61 }
62
63 fn count_presses(mut cur: i32, mut prev: i32) -> i32 {
65 let mut count = 0;
67 prev &= 63; cur &= 63;
69 while prev != cur {
70 prev = (prev + 1) & 63;
71 if prev & 1 != 0 {
72 count += 1;
73 }
74 }
75 count
76 }
77
78 pub fn count_weapon_next(&self, prev: &Input) -> i32 {
80 Input::count_presses(self.next_weapon, prev.next_weapon)
81 }
82
83 pub fn count_weapon_prev(&self, prev: &Input) -> i32 {
85 Input::count_presses(self.prev_weapon, prev.prev_weapon)
86 }
87
88 pub fn count_weapon_fire(&self, prev: &Input) -> i32 {
90 Input::count_presses(self.fire, prev.fire)
91 }
92
93 pub fn cursor(&self) -> Vec2<f32> {
95 Vec2::new(self.target_x as f32, self.target_y as f32)
96 }
97
98 pub fn firing(&self) -> bool {
100 (self.fire & 1) != 0
101 }
102
103 pub fn spec_cam_active(&self) -> bool {
105 (self.player_flags & (1 << 5)) != 0
106 }
107}
108
109impl From<[i32; 10]> for Input {
110 fn from(input: [i32; 10]) -> Self {
111 Input {
112 direction: input[0],
113 target_x: input[1],
114 target_y: input[2],
115 jump: input[3],
116 fire: input[4],
117 hook: input[5],
118 player_flags: input[6],
120 wanted_weapon: input[7],
121 next_weapon: input[8],
122 prev_weapon: input[9],
123 }
124 .disallow_target_center()
125 }
126}
127
128impl Input {
129 pub fn add_input_diff(&mut self, input_diff: [i32; 10]) {
130 self.direction += input_diff[0];
131 self.target_x += input_diff[1];
132 self.target_y += input_diff[2];
133 self.jump += input_diff[3];
134 self.fire += input_diff[4];
135 self.hook += input_diff[5];
136 self.player_flags += input_diff[6];
137 self.wanted_weapon += input_diff[7];
138 self.next_weapon += input_diff[8];
139 self.prev_weapon += input_diff[9];
140 }
141}
142
143pub trait Game {
144 fn player_join(&mut self, id: u32);
146 fn player_ready(&mut self, id: u32);
147 fn player_input(&mut self, id: u32, input: &Input);
148 fn player_leave(&mut self, id: u32);
149
150 fn on_net_msg(&mut self, id: u32, msg: &net_msg::ClNetMessage);
151 fn on_command(&mut self, id: u32, command: &Command);
152
153 fn swap_tees(&mut self, id1: u32, id2: u32);
154
155 fn tick(&mut self, cur_time: Instant);
157
158 fn is_empty(&self) -> bool;
161}
162
163pub trait Snapper {
164 fn snap(&self, snapshot: &mut Snap);
165}
166
167#[derive(Deserialize, Debug)]
168pub struct ThHeader {
169 pub game_uuid: Uuid,
170 pub server_version: String,
171 pub prng_description: Option<String>,
172 pub start_time: String,
173 pub map_name: String,
174 pub map_sha256: Option<String>,
175 pub map_crc: String,
176 pub map_size: String,
177 pub version_minor: Option<String>,
178 pub config: HashMap<String, String>,
179 pub tuning: HashMap<String, String>,
180}
181
182impl ThHeader {
183 pub fn from_buf(buf: &[u8]) -> Self {
184 let buf = String::from_utf8_lossy(buf);
185 serde_json::from_str(&buf).unwrap()
186 }
187}
188
189pub fn normalize(v: Vec2<f32>) -> Vec2<f32> {
193 let divisor = v.magnitude();
195 if divisor == 0.0 {
197 return Vec2::zero();
199 }
200 let l = 1.0 / divisor;
202 v * l
204}