subtr_actor/stats/calculators/
frame_components.rs1use crate::*;
2
3use super::{BallSample, DemoEventSample, PlayerSample};
4
5#[derive(Debug, Clone, Default)]
7pub struct FrameInfo {
8 pub frame_number: usize,
9 pub time: f32,
10 pub dt: f32,
11 pub seconds_remaining: Option<i32>,
12}
13
14#[derive(Debug, Clone, Default)]
16pub struct GameplayState {
17 pub game_state: Option<i32>,
18 pub ball_has_been_hit: Option<bool>,
19 pub kickoff_countdown_time: Option<i32>,
20 pub team_zero_score: Option<i32>,
21 pub team_one_score: Option<i32>,
22 pub possession_team_is_team_0: Option<bool>,
23 pub scored_on_team_is_team_0: Option<bool>,
24 pub current_in_game_team_player_counts: [usize; 2],
25}
26
27impl GameplayState {
28 pub fn is_live_play(&self) -> bool {
29 !self.kickoff_phase_active() && self.game_state != Some(GAME_STATE_GOAL_SCORED_REPLAY)
30 }
31
32 pub fn current_score(&self) -> Option<(i32, i32)> {
33 Some((self.team_zero_score?, self.team_one_score?))
34 }
35
36 pub fn kickoff_countdown_active(&self) -> bool {
37 self.kickoff_countdown_time
38 .is_some_and(|time| (1..=3).contains(&time))
39 || (self.game_state == Some(GAME_STATE_KICKOFF_COUNTDOWN)
40 && self.kickoff_countdown_time.is_none())
41 }
42
43 pub fn kickoff_phase_active(&self) -> bool {
44 self.game_state != Some(GAME_STATE_GOAL_SCORED_REPLAY)
45 && (self.kickoff_countdown_active() || self.ball_has_been_hit == Some(false))
46 }
47
48 pub fn current_in_game_team_player_count(&self, is_team_0: bool) -> usize {
49 self.current_in_game_team_player_counts[usize::from(!is_team_0)]
50 }
51}
52
53#[derive(Debug, Clone, Default)]
55pub enum BallFrameState {
56 #[default]
57 Missing,
58 Present(BallSample),
59}
60
61impl BallFrameState {
62 pub fn sample(&self) -> Option<&BallSample> {
63 match self {
64 Self::Missing => None,
65 Self::Present(ball) => Some(ball),
66 }
67 }
68
69 pub fn into_sample(self) -> Option<BallSample> {
70 match self {
71 Self::Missing => None,
72 Self::Present(ball) => Some(ball),
73 }
74 }
75
76 pub fn position(&self) -> Option<glam::Vec3> {
77 self.sample().map(BallSample::position)
78 }
79
80 pub fn velocity(&self) -> Option<glam::Vec3> {
81 self.sample().map(BallSample::velocity)
82 }
83}
84
85impl From<BallSample> for BallFrameState {
86 fn from(ball: BallSample) -> Self {
87 Self::Present(ball)
88 }
89}
90
91impl From<Option<BallSample>> for BallFrameState {
92 fn from(ball: Option<BallSample>) -> Self {
93 match ball {
94 Some(ball) => Self::Present(ball),
95 None => Self::Missing,
96 }
97 }
98}
99
100#[derive(Debug, Clone, Default)]
102pub struct PlayerFrameState {
103 pub players: Vec<PlayerSample>,
104}
105
106impl PlayerFrameState {
107 pub fn player(&self, player_id: &PlayerId) -> Option<&PlayerSample> {
108 self.players
109 .iter()
110 .find(|player| &player.player_id == player_id)
111 }
112
113 pub fn player_position(&self, player_id: &PlayerId) -> Option<[f32; 3]> {
114 self.player(player_id)
115 .and_then(PlayerSample::position)
116 .map(|position| position.to_array())
117 }
118}
119
120#[derive(Debug, Clone, Default)]
122pub struct FrameEventsState {
123 pub active_demos: Vec<DemoEventSample>,
124 pub demo_events: Vec<DemolishInfo>,
125 pub boost_pad_events: Vec<BoostPadEvent>,
126 pub touch_events: Vec<TouchEvent>,
127 pub dodge_refreshed_counter_available: bool,
132 pub dodge_refreshed_events: Vec<DodgeRefreshedEvent>,
133 pub player_stat_events: Vec<PlayerStatEvent>,
134 pub goal_events: Vec<GoalEvent>,
135}
136
137pub(crate) const GAME_STATE_KICKOFF_COUNTDOWN: i32 = 53;
138pub(crate) const GAME_STATE_GOAL_SCORED_REPLAY: i32 = 67;