Skip to main content

subtr_actor/stats/calculators/
frame_input.rs

1use crate::*;
2
3use super::{
4    BallFrameState, BallSample, DemoEventSample, FrameEventsState, FrameInfo, GameplayState,
5    PlayerFrameState, PlayerSample,
6};
7
8#[derive(Debug, Clone, Copy)]
9enum EventWindow {
10    CurrentFrame,
11    SinceLastSample {
12        last_demolish_count: usize,
13        last_boost_pad_event_count: usize,
14        last_touch_event_count: usize,
15        last_player_stat_event_count: usize,
16        last_goal_event_count: usize,
17    },
18}
19
20#[derive(Debug, Clone, Copy)]
21pub struct FrameInput {
22    processor: *const (),
23    frame_number: usize,
24    current_time: f32,
25    dt: f32,
26    event_window: EventWindow,
27}
28
29impl FrameInput {
30    pub fn timeline(
31        processor: &ReplayProcessor,
32        frame_number: usize,
33        current_time: f32,
34        dt: f32,
35    ) -> Self {
36        Self {
37            processor: processor as *const ReplayProcessor<'_> as *const (),
38            frame_number,
39            current_time,
40            dt,
41            event_window: EventWindow::CurrentFrame,
42        }
43    }
44
45    #[allow(clippy::too_many_arguments)]
46    pub fn aggregate(
47        processor: &ReplayProcessor,
48        frame_number: usize,
49        current_time: f32,
50        dt: f32,
51        last_demolish_count: usize,
52        last_boost_pad_event_count: usize,
53        last_touch_event_count: usize,
54        last_player_stat_event_count: usize,
55        last_goal_event_count: usize,
56    ) -> Self {
57        Self {
58            processor: processor as *const ReplayProcessor<'_> as *const (),
59            frame_number,
60            current_time,
61            dt,
62            event_window: EventWindow::SinceLastSample {
63                last_demolish_count,
64                last_boost_pad_event_count,
65                last_touch_event_count,
66                last_player_stat_event_count,
67                last_goal_event_count,
68            },
69        }
70    }
71
72    fn processor(&self) -> &ReplayProcessor<'_> {
73        // `FrameInput` is only used while evaluating the graph, so the replay
74        // processor borrowed by the collector outlives this ephemeral wrapper.
75        unsafe { &*(self.processor as *const ReplayProcessor<'_>) }
76    }
77
78    pub fn frame_info(&self) -> FrameInfo {
79        FrameInfo {
80            frame_number: self.frame_number,
81            time: self.current_time,
82            dt: self.dt,
83            seconds_remaining: self.processor().get_seconds_remaining().ok(),
84        }
85    }
86
87    pub fn gameplay_state(&self) -> GameplayState {
88        let processor = self.processor();
89        let team_scores = processor.get_team_scores().ok();
90        let possession_team_is_team_0 =
91            processor
92                .get_ball_hit_team_num()
93                .ok()
94                .and_then(|team_num| match team_num {
95                    0 => Some(true),
96                    1 => Some(false),
97                    _ => None,
98                });
99        let scored_on_team_is_team_0 =
100            processor
101                .get_scored_on_team_num()
102                .ok()
103                .and_then(|team_num| match team_num {
104                    0 => Some(true),
105                    1 => Some(false),
106                    _ => None,
107                });
108        GameplayState {
109            game_state: processor.get_replicated_state_name().ok(),
110            ball_has_been_hit: processor.get_ball_has_been_hit().ok(),
111            kickoff_countdown_time: processor.get_replicated_game_state_time_remaining().ok(),
112            team_zero_score: team_scores.map(|scores| scores.0),
113            team_one_score: team_scores.map(|scores| scores.1),
114            possession_team_is_team_0,
115            scored_on_team_is_team_0,
116            current_in_game_team_player_counts: processor.current_in_game_team_player_counts(),
117        }
118    }
119
120    pub fn ball_frame_state(&self) -> BallFrameState {
121        self.processor()
122            .get_interpolated_ball_rigid_body(self.current_time, 0.0)
123            .ok()
124            .map(|rigid_body| BallSample { rigid_body })
125            .into()
126    }
127
128    pub fn player_frame_state(&self) -> PlayerFrameState {
129        let processor = self.processor();
130        let mut players = Vec::new();
131        for player_id in processor.iter_player_ids_in_order() {
132            let Ok(is_team_0) = processor.get_player_is_team_0(player_id) else {
133                continue;
134            };
135            players.push(PlayerSample {
136                player_id: player_id.clone(),
137                is_team_0,
138                rigid_body: processor
139                    .get_interpolated_player_rigid_body(player_id, self.current_time, 0.0)
140                    .ok()
141                    .filter(|rigid_body| !rigid_body.sleeping),
142                boost_amount: processor.get_player_boost_level(player_id).ok(),
143                last_boost_amount: processor.get_player_last_boost_level(player_id).ok(),
144                boost_active: processor.get_boost_active(player_id).unwrap_or(0) % 2 == 1,
145                dodge_active: processor.get_dodge_active(player_id).unwrap_or(0) % 2 == 1,
146                powerslide_active: processor.get_powerslide_active(player_id).unwrap_or(false),
147                match_goals: processor.get_player_match_goals(player_id).ok(),
148                match_assists: processor.get_player_match_assists(player_id).ok(),
149                match_saves: processor.get_player_match_saves(player_id).ok(),
150                match_shots: processor.get_player_match_shots(player_id).ok(),
151                match_score: processor.get_player_match_score(player_id).ok(),
152            });
153        }
154        PlayerFrameState { players }
155    }
156
157    pub fn frame_events_state(&self) -> FrameEventsState {
158        let processor = self.processor();
159        let active_demos = if let Ok(demos) = processor.get_active_demos() {
160            demos
161                .filter_map(|demo| {
162                    let attacker = processor
163                        .get_player_id_from_car_id(&demo.attacker_actor_id())
164                        .ok()?;
165                    let victim = processor
166                        .get_player_id_from_car_id(&demo.victim_actor_id())
167                        .ok()?;
168                    Some(DemoEventSample { attacker, victim })
169                })
170                .collect()
171        } else {
172            Vec::new()
173        };
174        let mut events = FrameEventsState {
175            active_demos,
176            demo_events: Vec::new(),
177            boost_pad_events: processor.current_frame_boost_pad_events().to_vec(),
178            touch_events: processor.current_frame_touch_events().to_vec(),
179            dodge_refreshed_events: processor.current_frame_dodge_refreshed_events().to_vec(),
180            player_stat_events: processor.current_frame_player_stat_events().to_vec(),
181            goal_events: processor.current_frame_goal_events().to_vec(),
182        };
183        if let EventWindow::SinceLastSample {
184            last_demolish_count,
185            last_boost_pad_event_count,
186            last_touch_event_count,
187            last_player_stat_event_count,
188            last_goal_event_count,
189        } = self.event_window
190        {
191            events.active_demos.clear();
192            events.demo_events = processor.demolishes[last_demolish_count..].to_vec();
193            events.boost_pad_events =
194                processor.boost_pad_events[last_boost_pad_event_count..].to_vec();
195            events.touch_events = processor.touch_events[last_touch_event_count..].to_vec();
196            events.player_stat_events =
197                processor.player_stat_events[last_player_stat_event_count..].to_vec();
198            events.goal_events = processor.goal_events[last_goal_event_count..].to_vec();
199        }
200        events
201    }
202}