Skip to main content

subtr_actor/stats/analysis_graph/nodes/
match_stats.rs

1use super::*;
2use crate::stats::calculators::*;
3use crate::*;
4
5/// Accumulates match-level stats and goal contexts; attaches per-goal territorial pressure at finish.
6pub struct MatchStatsNode {
7    calculator: MatchStatsCalculator,
8}
9
10impl MatchStatsNode {
11    pub fn new() -> Self {
12        Self {
13            calculator: MatchStatsCalculator::new(),
14        }
15    }
16}
17
18impl Default for MatchStatsNode {
19    fn default() -> Self {
20        Self::new()
21    }
22}
23
24impl AnalysisNode for MatchStatsNode {
25    type State = MatchStatsCalculator;
26
27    fn name(&self) -> &'static str {
28        "match_stats"
29    }
30
31    fn emitted_events(&self) -> &'static [crate::stats::calculators::EmittedEvent] {
32        crate::stats::calculators::MATCH_STATS_EMITTED_EVENTS
33    }
34
35    fn dependencies(&self) -> Vec<AnalysisDependency> {
36        vec![
37            frame_info_dependency(),
38            gameplay_state_dependency(),
39            ball_frame_state_dependency(),
40            player_frame_state_dependency(),
41            frame_events_state_dependency(),
42            live_play_dependency(),
43            touch_state_dependency(),
44            // Not consumed per-frame; needed at finish to attach per-goal pressure.
45            territorial_pressure_dependency(),
46        ]
47    }
48
49    fn evaluate(&mut self, ctx: &AnalysisStateContext<'_>) -> SubtrActorResult<()> {
50        self.calculator.update_parts(
51            ctx.get::<FrameInfo>()?,
52            ctx.get::<GameplayState>()?,
53            ctx.get::<BallFrameState>()?,
54            ctx.get::<PlayerFrameState>()?,
55            ctx.get::<FrameEventsState>()?,
56            ctx.get::<LivePlayState>()?,
57            ctx.get::<TouchState>()?,
58        )
59    }
60
61    fn finish(&mut self, ctx: &AnalysisStateContext<'_>) -> SubtrActorResult<()> {
62        // Finalize goal contexts first, then attach pressure duration from the
63        // now-final territorial-pressure sessions.
64        self.calculator.finish()?;
65        let territorial_pressure = ctx.get::<TerritorialPressureCalculator>()?;
66        self.calculator
67            .attach_goal_pressure_durations(&territorial_pressure.projected_events());
68        Ok(())
69    }
70
71    fn state(&self) -> &Self::State {
72        &self.calculator
73    }
74}
75
76pub(crate) fn boxed_default() -> Box<dyn AnalysisNodeDyn> {
77    Box::new(MatchStatsNode::new())
78}