Skip to main content

subtr_actor/stats/calculators/
possession_state.rs

1use super::*;
2
3/// Shared current ball-possession state.
4#[derive(Debug, Clone, Default)]
5pub struct PossessionState {
6    pub active_team_before_sample: Option<bool>,
7    pub current_team_is_team_0: Option<bool>,
8    pub active_player_before_sample: Option<PlayerId>,
9    pub current_player: Option<PlayerId>,
10    /// Team-possession segments finalized by the backdating resolver this frame.
11    pub(crate) newly_resolved: Vec<ResolvedPossession>,
12    /// The resolver's current open (unresolved) segment, if live.
13    pub(crate) open_possession: Option<OpenPossession>,
14}
15
16/// Maintains shared ball-possession state from touches and live play.
17#[derive(Default)]
18pub struct PossessionStateCalculator {
19    tracker: PossessionTracker,
20    was_live: bool,
21}
22
23impl PossessionStateCalculator {
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    pub fn update(
29        &mut self,
30        frame: &FrameInfo,
31        touch_state: &TouchState,
32        live_play_state: &LivePlayState,
33    ) -> PossessionState {
34        if !live_play_state.is_live_play {
35            // On the falling edge, flush the open segment so the just-ended live
36            // stretch is fully resolved (the trailing held tail goes neutral).
37            let newly_resolved = if self.was_live {
38                self.tracker.flush_resolver(frame)
39            } else {
40                Vec::new()
41            };
42            self.tracker.reset();
43            self.was_live = false;
44            return PossessionState {
45                newly_resolved,
46                ..PossessionState::default()
47            };
48        }
49
50        if !self.was_live {
51            self.tracker.begin_resolver(frame);
52            self.was_live = true;
53        }
54
55        self.tracker.update(frame, &touch_state.touch_events)
56    }
57}