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