subtr_actor/stats/calculators/
frame_input.rs1use crate::*;
2
3use super::{
4 BallFrameState, BallSample, DemoEventSample, FrameEventsState, FrameInfo, GameplayState,
5 LivePlayState, PlayerFrameState, PlayerSample,
6};
7
8#[derive(Debug, Clone)]
9pub struct FrameInput {
10 frame_info: FrameInfo,
11 gameplay_state: GameplayState,
12 ball_frame_state: BallFrameState,
13 player_frame_state: PlayerFrameState,
14 frame_events_state: FrameEventsState,
15 live_play_state: Option<LivePlayState>,
16}
17
18impl FrameInput {
19 pub fn from_parts(
25 frame_info: FrameInfo,
26 gameplay_state: GameplayState,
27 ball_frame_state: BallFrameState,
28 player_frame_state: PlayerFrameState,
29 frame_events_state: FrameEventsState,
30 ) -> Self {
31 Self {
32 frame_info,
33 gameplay_state,
34 ball_frame_state,
35 player_frame_state,
36 frame_events_state,
37 live_play_state: None,
38 }
39 }
40
41 pub fn from_parts_with_live_play_state(
47 frame_info: FrameInfo,
48 gameplay_state: GameplayState,
49 ball_frame_state: BallFrameState,
50 player_frame_state: PlayerFrameState,
51 frame_events_state: FrameEventsState,
52 live_play_state: LivePlayState,
53 ) -> Self {
54 Self {
55 frame_info,
56 gameplay_state,
57 ball_frame_state,
58 player_frame_state,
59 frame_events_state,
60 live_play_state: Some(live_play_state),
61 }
62 }
63
64 pub fn timeline(
65 processor: &dyn ProcessorView,
66 frame_number: usize,
67 current_time: f32,
68 dt: f32,
69 ) -> Self {
70 Self {
71 frame_info: Self::build_frame_info(processor, frame_number, current_time, dt),
72 gameplay_state: Self::build_gameplay_state(processor),
73 ball_frame_state: Self::build_ball_frame_state(processor, current_time),
74 player_frame_state: Self::build_player_frame_state(processor, current_time),
75 frame_events_state: Self::build_current_frame_events_state(processor),
76 live_play_state: None,
77 }
78 }
79
80 pub fn timeline_with_live_play_state(
81 processor: &dyn ProcessorView,
82 frame_number: usize,
83 current_time: f32,
84 dt: f32,
85 live_play_state: LivePlayState,
86 ) -> Self {
87 let mut input = Self::timeline(processor, frame_number, current_time, dt);
88 input.live_play_state = Some(live_play_state);
89 input
90 }
91
92 #[allow(clippy::too_many_arguments)]
93 pub fn aggregate(
94 processor: &dyn ProcessorView,
95 frame_number: usize,
96 current_time: f32,
97 dt: f32,
98 last_demolish_count: usize,
99 last_boost_pad_event_count: usize,
100 last_touch_event_count: usize,
101 last_dodge_refreshed_event_count: usize,
102 last_player_stat_event_count: usize,
103 last_goal_event_count: usize,
104 ) -> Self {
105 Self {
106 frame_info: Self::build_frame_info(processor, frame_number, current_time, dt),
107 gameplay_state: Self::build_gameplay_state(processor),
108 ball_frame_state: Self::build_ball_frame_state(processor, current_time),
109 player_frame_state: Self::build_player_frame_state(processor, current_time),
110 frame_events_state: Self::build_events_since_last_sample(
111 processor,
112 last_demolish_count,
113 last_boost_pad_event_count,
114 last_touch_event_count,
115 last_dodge_refreshed_event_count,
116 last_player_stat_event_count,
117 last_goal_event_count,
118 ),
119 live_play_state: None,
120 }
121 }
122
123 fn build_frame_info(
124 processor: &dyn ProcessorView,
125 frame_number: usize,
126 current_time: f32,
127 dt: f32,
128 ) -> FrameInfo {
129 FrameInfo {
130 frame_number,
131 time: current_time,
132 dt,
133 seconds_remaining: processor.get_seconds_remaining().ok(),
134 }
135 }
136
137 fn build_gameplay_state(processor: &dyn ProcessorView) -> GameplayState {
138 let team_scores = processor.get_team_scores().ok();
139 let possession_team_is_team_0 =
140 processor
141 .get_ball_hit_team_num()
142 .ok()
143 .and_then(|team_num| match team_num {
144 0 => Some(true),
145 1 => Some(false),
146 _ => None,
147 });
148 let scored_on_team_is_team_0 =
149 processor
150 .get_scored_on_team_num()
151 .ok()
152 .and_then(|team_num| match team_num {
153 0 => Some(true),
154 1 => Some(false),
155 _ => None,
156 });
157 GameplayState {
158 game_state: processor.get_replicated_state_name().ok(),
159 ball_has_been_hit: processor.get_ball_has_been_hit().ok(),
160 kickoff_countdown_time: processor.get_replicated_game_state_time_remaining().ok(),
161 team_zero_score: team_scores.map(|scores| scores.0),
162 team_one_score: team_scores.map(|scores| scores.1),
163 possession_team_is_team_0,
164 scored_on_team_is_team_0,
165 current_in_game_team_player_counts: processor.current_in_game_team_player_counts(),
166 }
167 }
168
169 fn build_ball_frame_state(processor: &dyn ProcessorView, current_time: f32) -> BallFrameState {
170 processor
171 .get_interpolated_ball_rigid_body(current_time, 0.0)
172 .ok()
173 .map(|rigid_body| BallSample { rigid_body })
174 .into()
175 }
176
177 fn build_player_frame_state(
178 processor: &dyn ProcessorView,
179 current_time: f32,
180 ) -> PlayerFrameState {
181 let mut players = Vec::new();
182 for player_id in processor.iter_player_ids_in_order() {
183 let Ok(is_team_0) = processor.get_player_is_team_0(player_id) else {
184 continue;
185 };
186 players.push(PlayerSample {
187 player_id: player_id.clone(),
188 is_team_0,
189 rigid_body: processor
190 .get_interpolated_player_rigid_body(player_id, current_time, 0.0)
191 .ok()
192 .filter(|rigid_body| !rigid_body.sleeping),
193 boost_amount: processor.get_player_boost_level(player_id).ok(),
194 last_boost_amount: processor.get_player_last_boost_level(player_id).ok(),
195 boost_active: processor.get_boost_active(player_id).unwrap_or(0) % 2 == 1,
196 dodge_active: processor.get_dodge_active(player_id).unwrap_or(0) % 2 == 1,
197 powerslide_active: processor.get_powerslide_active(player_id).unwrap_or(false),
198 match_goals: processor.get_player_match_goals(player_id).ok(),
199 match_assists: processor.get_player_match_assists(player_id).ok(),
200 match_saves: processor.get_player_match_saves(player_id).ok(),
201 match_shots: processor.get_player_match_shots(player_id).ok(),
202 match_score: processor.get_player_match_score(player_id).ok(),
203 });
204 }
205 PlayerFrameState { players }
206 }
207
208 fn build_active_demo_events(processor: &dyn ProcessorView) -> Vec<DemoEventSample> {
209 let active_demo_events = processor.current_frame_active_demo_events();
210 if !active_demo_events.is_empty() {
211 active_demo_events.to_vec()
212 } else if let Ok(demos) = processor.get_active_demos() {
213 demos
214 .into_iter()
215 .filter_map(|demo| {
216 let attacker = processor
217 .get_player_id_from_car_id(&demo.attacker_actor_id())
218 .ok()?;
219 let victim = processor
220 .get_player_id_from_car_id(&demo.victim_actor_id())
221 .ok()?;
222 Some(DemoEventSample { attacker, victim })
223 })
224 .collect()
225 } else {
226 Vec::new()
227 }
228 }
229
230 fn build_current_frame_events_state(processor: &dyn ProcessorView) -> FrameEventsState {
231 FrameEventsState {
232 active_demos: Self::build_active_demo_events(processor),
233 demo_events: processor.current_frame_demolish_events().to_vec(),
234 boost_pad_events: processor.current_frame_boost_pad_events().to_vec(),
235 touch_events: processor.current_frame_touch_events().to_vec(),
236 dodge_refreshed_events: processor.current_frame_dodge_refreshed_events().to_vec(),
237 player_stat_events: processor.current_frame_player_stat_events().to_vec(),
238 goal_events: processor.current_frame_goal_events().to_vec(),
239 }
240 }
241
242 fn build_events_since_last_sample(
243 processor: &dyn ProcessorView,
244 last_demolish_count: usize,
245 last_boost_pad_event_count: usize,
246 last_touch_event_count: usize,
247 last_dodge_refreshed_event_count: usize,
248 last_player_stat_event_count: usize,
249 last_goal_event_count: usize,
250 ) -> FrameEventsState {
251 FrameEventsState {
252 active_demos: Self::build_active_demo_events(processor),
253 demo_events: processor.demolishes()[last_demolish_count..].to_vec(),
254 boost_pad_events: processor.boost_pad_events()[last_boost_pad_event_count..].to_vec(),
255 touch_events: processor.touch_events()[last_touch_event_count..].to_vec(),
256 dodge_refreshed_events: processor.dodge_refreshed_events()
257 [last_dodge_refreshed_event_count..]
258 .to_vec(),
259 player_stat_events: processor.player_stat_events()[last_player_stat_event_count..]
260 .to_vec(),
261 goal_events: processor.goal_events()[last_goal_event_count..].to_vec(),
262 }
263 }
264
265 pub fn frame_info(&self) -> FrameInfo {
266 self.frame_info.clone()
267 }
268
269 pub fn gameplay_state(&self) -> GameplayState {
270 self.gameplay_state.clone()
271 }
272
273 pub fn ball_frame_state(&self) -> BallFrameState {
274 self.ball_frame_state.clone()
275 }
276
277 pub fn player_frame_state(&self) -> PlayerFrameState {
278 self.player_frame_state.clone()
279 }
280
281 pub fn frame_events_state(&self) -> FrameEventsState {
282 self.frame_events_state.clone()
283 }
284
285 pub fn live_play_state(&self) -> Option<LivePlayState> {
286 self.live_play_state.clone()
287 }
288}