aboutsummaryrefslogtreecommitdiff
path: root/src/statistics/mod.rs
blob: 5f2f2880b4f35e1d05fa9984b37073eaef205ef6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! This module aids in the creation of actual boss statistics.
use super::*;
use std::collections::HashMap;
use std::error::Error;

pub mod boon;
pub mod damage;
pub mod gamedata;
pub mod math;
pub mod trackers;

use self::damage::DamageLog;
use self::trackers::{RunnableTracker, Tracker};

pub type StatResult<T> = Result<T, StatError>;

quick_error! {
    #[derive(Debug)]
    pub enum StatError {
        TrackerError(err: Box<Error>) {
            description("tracker error")
            display("tracker returned an error: {}", err)
            cause(&**err)
        }
    }
}

macro_rules! try_tracker {
    ($expr:expr) => {
        #[allow(unreachable_code)]
        match $expr {
            Ok(e) => e,
            Err(e) => return Err(StatError::TrackerError(e)),
        }
    };
}

/// A struct containing the calculated statistics for the log.
#[derive(Clone, Debug)]
pub struct Statistics {
    /// The complete damage log.
    pub damage_log: DamageLog,
    /// A map mapping agent addresses to their stats.
    pub agent_stats: HashMap<u64, AgentStats>,
}

/// A struct describing the agent statistics.
#[derive(Clone, Debug, Default)]
pub struct AgentStats {
    /// Average stacks of boons.
    ///
    /// This also includes conditions.
    ///
    /// For duration-based boons, the average amount of stacks is the same as
    /// the uptime.
    pub boon_averages: HashMap<u16, f64>,
    /// Time when the agent has entered combat (millseconds since log start).
    pub enter_combat: u64,
    /// Time when the agent has left combat (millseconds since log start).
    pub exit_combat: u64,
}

impl AgentStats {
    /// Returns the combat time of this agent in milliseconds.
    pub fn combat_time(&self) -> u64 {
        self.exit_combat - self.enter_combat
    }
}

/// Takes a bunch of trackers and runs them on the given log.
///
/// This method returns "nothing", as the statistics are saved in the trackers.
/// It's the job of the caller to extract them appropriately.
pub fn run_trackers(log: &Log, trackers: &mut [&mut RunnableTracker]) -> StatResult<()> {
    for event in log.events() {
        for mut tracker in trackers.iter_mut() {
            try_tracker!((*tracker).run_feed(event));
        }
    }
    Ok(())
}

/// Calculate the statistics for the given log.
pub fn calculate(log: &Log) -> StatResult<Statistics> {
    let mut agent_stats = HashMap::<u64, AgentStats>::new();

    let mut damage_tracker = trackers::DamageTracker::new(log);
    let mut log_start_tracker = trackers::LogStartTracker::new();
    let mut combat_time_tracker = trackers::CombatTimeTracker::new();

    run_trackers(
        log,
        &mut [
            &mut damage_tracker,
            &mut log_start_tracker,
            &mut combat_time_tracker,
        ],
    )?;

    let log_start_time = try_tracker!(log_start_tracker.finalize());

    let combat_times = try_tracker!(combat_time_tracker.finalize());
    for (agent_addr, &(enter_time, exit_time)) in &combat_times {
        let agent = agent_stats
            .entry(*agent_addr)
            .or_insert_with(Default::default);
        if enter_time != 0 {
            agent.enter_combat = enter_time - log_start_time;
        }
        if exit_time != 0 {
            agent.exit_combat = exit_time - log_start_time;
        }
    }

    let damage_log = try_tracker!(damage_tracker.finalize());

    Ok(Statistics {
        damage_log,
        agent_stats,
    })
}