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,
})
}
|