subtr_actor/stats/accumulators/
one_timer.rs1use super::*;
2
3#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
5#[ts(export)]
6pub struct OneTimerPlayerStats {
7 pub count: u32,
8 pub total_ball_speed: f32,
9 pub fastest_ball_speed: f32,
10 pub total_pass_distance: f32,
11 pub is_last_one_timer: bool,
12 pub last_one_timer_time: Option<f32>,
13 pub last_one_timer_frame: Option<usize>,
14 pub time_since_last_one_timer: Option<f32>,
15 pub frames_since_last_one_timer: Option<usize>,
16}
17
18impl OneTimerPlayerStats {
19 pub fn average_ball_speed(&self) -> f32 {
20 if self.count == 0 {
21 0.0
22 } else {
23 self.total_ball_speed / self.count as f32
24 }
25 }
26
27 pub fn average_pass_distance(&self) -> f32 {
28 if self.count == 0 {
29 0.0
30 } else {
31 self.total_pass_distance / self.count as f32
32 }
33 }
34}
35
36#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
38#[ts(export)]
39pub struct OneTimerTeamStats {
40 pub count: u32,
41 pub total_ball_speed: f32,
42 pub fastest_ball_speed: f32,
43}
44
45impl OneTimerTeamStats {
46 pub fn average_ball_speed(&self) -> f32 {
47 if self.count == 0 {
48 0.0
49 } else {
50 self.total_ball_speed / self.count as f32
51 }
52 }
53}
54
55#[derive(Debug, Clone, Default, PartialEq)]
57pub struct OneTimerStatsAccumulator {
58 player_stats: HashMap<PlayerId, OneTimerPlayerStats>,
59 team_zero_stats: OneTimerTeamStats,
60 team_one_stats: OneTimerTeamStats,
61 current_last_one_timer_player: Option<PlayerId>,
62}
63
64impl OneTimerStatsAccumulator {
65 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub fn player_stats(&self) -> &HashMap<PlayerId, OneTimerPlayerStats> {
70 &self.player_stats
71 }
72
73 pub fn team_zero_stats(&self) -> &OneTimerTeamStats {
74 &self.team_zero_stats
75 }
76
77 pub fn team_one_stats(&self) -> &OneTimerTeamStats {
78 &self.team_one_stats
79 }
80
81 pub fn begin_sample(&mut self, frame: &FrameInfo) {
82 for stats in self.player_stats.values_mut() {
83 stats.is_last_one_timer = false;
84 stats.time_since_last_one_timer = stats
85 .last_one_timer_time
86 .map(|time| (frame.time - time).max(0.0));
87 stats.frames_since_last_one_timer = stats
88 .last_one_timer_frame
89 .map(|last_frame| frame.frame_number.saturating_sub(last_frame));
90 }
91 }
92
93 pub fn clear_current_last(&mut self) {
94 self.current_last_one_timer_player = None;
95 }
96
97 pub fn apply_event(&mut self, frame: &FrameInfo, event: &OneTimerEvent) {
98 let player_stats = self.player_stats.entry(event.player.clone()).or_default();
99 player_stats.count += 1;
100 player_stats.total_ball_speed += event.ball_speed;
101 player_stats.fastest_ball_speed = player_stats.fastest_ball_speed.max(event.ball_speed);
102 player_stats.total_pass_distance += event.pass_travel_distance;
103 player_stats.last_one_timer_time = Some(event.time);
104 player_stats.last_one_timer_frame = Some(event.frame);
105 player_stats.time_since_last_one_timer = Some((frame.time - event.time).max(0.0));
106 player_stats.frames_since_last_one_timer =
107 Some(frame.frame_number.saturating_sub(event.frame));
108
109 let team_stats = if event.is_team_0 {
110 &mut self.team_zero_stats
111 } else {
112 &mut self.team_one_stats
113 };
114 team_stats.count += 1;
115 team_stats.total_ball_speed += event.ball_speed;
116 team_stats.fastest_ball_speed = team_stats.fastest_ball_speed.max(event.ball_speed);
117
118 self.current_last_one_timer_player = Some(event.player.clone());
119 }
120
121 pub fn finish_sample(&mut self) {
122 if let Some(player_id) = self.current_last_one_timer_player.as_ref() {
123 if let Some(stats) = self.player_stats.get_mut(player_id) {
124 stats.is_last_one_timer = true;
125 }
126 }
127 }
128}