subtr_actor/stats/calculators/
frame_input.rs1use 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 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}