subtr_actor/stats/accumulators/
backboard.rs1use super::*;
2
3#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
5#[ts(export)]
6pub struct BackboardPlayerStats {
7 pub count: u32,
8 pub is_last_backboard: bool,
9 pub last_backboard_time: Option<f32>,
10 pub last_backboard_frame: Option<usize>,
11 pub time_since_last_backboard: Option<f32>,
12 pub frames_since_last_backboard: Option<usize>,
13}
14
15#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
17#[ts(export)]
18pub struct BackboardTeamStats {
19 pub count: u32,
20}
21
22#[derive(Debug, Clone, Default, PartialEq)]
24pub struct BackboardStatsAccumulator {
25 player_stats: HashMap<PlayerId, BackboardPlayerStats>,
26 team_zero_stats: BackboardTeamStats,
27 team_one_stats: BackboardTeamStats,
28 current_last_backboard_player: Option<PlayerId>,
29}
30
31impl BackboardStatsAccumulator {
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn player_stats(&self) -> &HashMap<PlayerId, BackboardPlayerStats> {
37 &self.player_stats
38 }
39
40 pub fn team_zero_stats(&self) -> &BackboardTeamStats {
41 &self.team_zero_stats
42 }
43
44 pub fn team_one_stats(&self) -> &BackboardTeamStats {
45 &self.team_one_stats
46 }
47
48 pub fn begin_sample(&mut self, frame: &FrameInfo) {
49 for stats in self.player_stats.values_mut() {
50 stats.is_last_backboard = false;
51 stats.time_since_last_backboard = stats
52 .last_backboard_time
53 .map(|time| (frame.time - time).max(0.0));
54 stats.frames_since_last_backboard = stats
55 .last_backboard_frame
56 .map(|last_frame| frame.frame_number.saturating_sub(last_frame));
57 }
58 }
59
60 pub fn apply_events(&mut self, frame: &FrameInfo, events: &[BackboardBounceEvent]) {
61 for event in events {
62 self.apply_event(frame, event);
63 }
64
65 if let Some(last_event) = events.last() {
66 self.current_last_backboard_player = Some(last_event.player.clone());
67 }
68
69 if let Some(player_id) = self.current_last_backboard_player.as_ref() {
70 if let Some(stats) = self.player_stats.get_mut(player_id) {
71 stats.is_last_backboard = true;
72 }
73 }
74 }
75
76 fn apply_event(&mut self, frame: &FrameInfo, event: &BackboardBounceEvent) {
77 let stats = self.player_stats.entry(event.player.clone()).or_default();
78 stats.count += 1;
79 stats.last_backboard_time = Some(event.time);
80 stats.last_backboard_frame = Some(event.frame);
81 stats.time_since_last_backboard = Some((frame.time - event.time).max(0.0));
82 stats.frames_since_last_backboard = Some(frame.frame_number.saturating_sub(event.frame));
83
84 let team_stats = if event.is_team_0 {
85 &mut self.team_zero_stats
86 } else {
87 &mut self.team_one_stats
88 };
89 team_stats.count += 1;
90 }
91}