Skip to main content

subtr_actor/stats/calculators/
dodge_reset.rs

1use super::*;
2
3#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
4#[ts(export)]
5pub struct DodgeResetStats {
6    pub count: u32,
7    pub on_ball_count: u32,
8}
9
10#[derive(Debug, Clone, Default, PartialEq)]
11pub struct DodgeResetCalculator {
12    player_stats: HashMap<PlayerId, DodgeResetStats>,
13}
14
15impl DodgeResetCalculator {
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    pub fn player_stats(&self) -> &HashMap<PlayerId, DodgeResetStats> {
21        &self.player_stats
22    }
23
24    fn on_ball_dodge_reset(
25        ball: &BallFrameState,
26        players: &PlayerFrameState,
27        player_id: &PlayerId,
28    ) -> bool {
29        const MIN_PLAYER_HEIGHT: f32 = 95.0;
30        const MIN_BALL_HEIGHT: f32 = 80.0;
31        const MAX_CENTER_DISTANCE: f32 = 180.0;
32        const MAX_LOCAL_VERTICAL_OFFSET: f32 = 140.0;
33
34        let Some(ball) = ball.sample() else {
35            return false;
36        };
37        let Some(player) = players
38            .players
39            .iter()
40            .find(|player| &player.player_id == player_id)
41        else {
42            return false;
43        };
44        let Some(player_rigid_body) = &player.rigid_body else {
45            return false;
46        };
47
48        let ball_position = vec_to_glam(&ball.rigid_body.location);
49        let player_position = vec_to_glam(&player_rigid_body.location);
50        if player_position.z < MIN_PLAYER_HEIGHT || ball_position.z < MIN_BALL_HEIGHT {
51            return false;
52        }
53
54        let relative_ball_position = ball_position - player_position;
55        let center_distance = relative_ball_position.length();
56        if !center_distance.is_finite() || center_distance > MAX_CENTER_DISTANCE {
57            return false;
58        }
59
60        let player_rotation = quat_to_glam(&player_rigid_body.rotation);
61        let local_ball_position = player_rotation.inverse() * relative_ball_position;
62        local_ball_position.z <= MAX_LOCAL_VERTICAL_OFFSET
63    }
64
65    pub fn update(
66        &mut self,
67        ball: &BallFrameState,
68        players: &PlayerFrameState,
69        events: &FrameEventsState,
70    ) -> SubtrActorResult<()> {
71        for event in &events.dodge_refreshed_events {
72            let on_ball = Self::on_ball_dodge_reset(ball, players, &event.player);
73            let stats = self.player_stats.entry(event.player.clone()).or_default();
74            stats.count += 1;
75            if on_ball {
76                stats.on_ball_count += 1;
77            }
78        }
79        Ok(())
80    }
81}