Skip to main content

subtr_actor/stats/calculators/
powerslide.rs

1use super::*;
2
3#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
4#[ts(export)]
5pub struct PowerslideStats {
6    pub total_duration: f32,
7    pub press_count: u32,
8}
9
10impl PowerslideStats {
11    pub fn average_duration(&self) -> f32 {
12        if self.press_count == 0 {
13            0.0
14        } else {
15            self.total_duration / self.press_count as f32
16        }
17    }
18}
19
20#[derive(Debug, Clone, Default)]
21pub struct PowerslideCalculator {
22    player_stats: HashMap<PlayerId, PowerslideStats>,
23    team_zero_stats: PowerslideStats,
24    team_one_stats: PowerslideStats,
25    last_active: HashMap<PlayerId, bool>,
26}
27
28impl PowerslideCalculator {
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    pub fn player_stats(&self) -> &HashMap<PlayerId, PowerslideStats> {
34        &self.player_stats
35    }
36
37    pub fn team_zero_stats(&self) -> &PowerslideStats {
38        &self.team_zero_stats
39    }
40
41    pub fn team_one_stats(&self) -> &PowerslideStats {
42        &self.team_one_stats
43    }
44
45    fn is_effective_powerslide(player: &PlayerSample) -> bool {
46        player.powerslide_active
47            && player
48                .position()
49                .map(|position| position.z <= POWERSLIDE_MAX_Z_THRESHOLD)
50                .unwrap_or(false)
51    }
52
53    pub fn update(
54        &mut self,
55        frame: &FrameInfo,
56        players: &PlayerFrameState,
57        live_play: bool,
58    ) -> SubtrActorResult<()> {
59        for player in &players.players {
60            let effective_powerslide = Self::is_effective_powerslide(player);
61            let previous_active = self
62                .last_active
63                .get(&player.player_id)
64                .copied()
65                .unwrap_or(false);
66            let stats = self
67                .player_stats
68                .entry(player.player_id.clone())
69                .or_default();
70            let team_stats = if player.is_team_0 {
71                &mut self.team_zero_stats
72            } else {
73                &mut self.team_one_stats
74            };
75
76            if live_play && effective_powerslide {
77                stats.total_duration += frame.dt;
78                team_stats.total_duration += frame.dt;
79            }
80
81            if live_play && effective_powerslide && !previous_active {
82                stats.press_count += 1;
83                team_stats.press_count += 1;
84            }
85
86            self.last_active
87                .insert(player.player_id.clone(), effective_powerslide);
88        }
89        Ok(())
90    }
91}