Skip to main content

subtr_actor/stats/analysis_graph/nodes/
continuous_ball_control.rs

1use super::*;
2use crate::stats::calculators::*;
3use crate::*;
4
5/// Tracks continuous ball-control sequences per player, emitting completed sequences.
6pub struct ContinuousBallControlNode {
7    tracker: ContinuousBallControlTracker<BallCarryKind>,
8    state: ContinuousBallControlState,
9}
10
11impl ContinuousBallControlNode {
12    pub fn new() -> Self {
13        Self {
14            tracker: ContinuousBallControlTracker::default(),
15            state: ContinuousBallControlState::default(),
16        }
17    }
18}
19
20impl Default for ContinuousBallControlNode {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl AnalysisNode for ContinuousBallControlNode {
27    type State = ContinuousBallControlState;
28
29    fn name(&self) -> &'static str {
30        "continuous_ball_control"
31    }
32
33    fn dependencies(&self) -> Vec<AnalysisDependency> {
34        vec![
35            frame_info_dependency(),
36            ball_frame_state_dependency(),
37            player_frame_state_dependency(),
38            touch_state_dependency(),
39            live_play_dependency(),
40        ]
41    }
42
43    fn evaluate(&mut self, ctx: &AnalysisStateContext<'_>) -> SubtrActorResult<()> {
44        let frame = ctx.get::<FrameInfo>()?;
45        let touch_state = ctx.get::<TouchState>()?;
46        let players = ctx.get::<PlayerFrameState>()?;
47        let candidate = if frame.dt > 0.0 {
48            BallCarryCalculator::control_candidate(
49                ctx.get::<BallFrameState>()?,
50                players,
51                ctx.get::<LivePlayState>()?,
52                touch_state,
53            )
54        } else {
55            None
56        };
57        let player_statuses = BallCarryCalculator::control_player_statuses(players);
58        let touches = BallCarryCalculator::control_touches(touch_state, players);
59        self.state.completed_sequences.extend(self.tracker.update(
60            frame,
61            candidate,
62            &player_statuses,
63            &touches,
64            BallCarryCalculator::min_duration_for_kind,
65            BallCarryCalculator::kind_requires_airborne,
66        ));
67        Ok(())
68    }
69
70    fn finish(&mut self, _ctx: &AnalysisStateContext<'_>) -> SubtrActorResult<()> {
71        if let Some(sequence) = self
72            .tracker
73            .finish(BallCarryCalculator::min_duration_for_kind)
74        {
75            self.state.completed_sequences.push(sequence);
76        }
77        Ok(())
78    }
79
80    fn state(&self) -> &Self::State {
81        &self.state
82    }
83}
84
85pub(crate) fn boxed_default() -> Box<dyn AnalysisNodeDyn> {
86    Box::new(ContinuousBallControlNode::new())
87}