Skip to main content

subtr_actor/stats/calculators/
dodge_reset.rs

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