aboutsummaryrefslogtreecommitdiff
path: root/src/statistics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/statistics.rs')
-rw-r--r--src/statistics.rs170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/statistics.rs b/src/statistics.rs
new file mode 100644
index 0000000..af208ce
--- /dev/null
+++ b/src/statistics.rs
@@ -0,0 +1,170 @@
+//! This module aids in the creation of actual boss statistics.
+use super::*;
+use std::collections::HashMap;
+
+pub type StatResult<T> = Result<T, StatError>;
+
+quick_error! {
+ #[derive(Clone, Debug)]
+ pub enum StatError {
+ }
+}
+
+/// A struct containing the calculated statistics for the log.
+#[derive(Clone, Debug)]
+pub struct Statistics {
+ /// 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 {
+ /// Damage done per target during the fight.
+ ///
+ /// Maps from target address to the damage done to this target.
+ pub per_target_damage: HashMap<u64, DamageStats>,
+ /// Total damage dealt during the fight.
+ pub total_damage: DamageStats,
+ /// Damage directed to the boss.
+ pub boss_damage: DamageStats,
+ /// 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
+ }
+}
+
+/// Damage statistics for a given target.
+#[derive(Debug, Clone, Copy, Default)]
+pub struct DamageStats {
+ /// The total damage of the player, including all minions/pets/...
+ pub total_damage: u64,
+ /// The condition damage that the player dealt.
+ pub condition_damage: u64,
+ /// The power damage that the player dealt.
+ pub power_damage: u64,
+ /// The damage that was done by minions/pets/...
+ pub add_damage: u64,
+}
+
+// A support macro to introduce a new block.
+//
+// Doesn't really require a macro, but it's nicer to look at
+// with! { foo = bar }
+// rather than
+// { let foo = bar; ... }
+macro_rules! with {
+ ($name:ident = $expr:expr => $bl:block) => {{
+ let $name = $expr;
+ $bl
+ }};
+}
+
+/// Calculate the statistics for the given log.
+pub fn calculate(log: &Log) -> StatResult<Statistics> {
+ use super::EventKind::*;
+
+ let mut agent_stats = HashMap::<u64, AgentStats>::new();
+ let mut log_start_time = 0;
+
+ fn get_stats(map: &mut HashMap<u64, AgentStats>, source: u64, target: u64) -> &mut DamageStats {
+ map.entry(source)
+ .or_insert_with(Default::default)
+ .per_target_damage
+ .entry(target)
+ .or_insert_with(Default::default)
+ }
+
+ for event in log.events() {
+ match event.kind {
+ LogStart { .. } => {
+ log_start_time = event.time;
+ }
+
+ EnterCombat { agent_addr, .. } => {
+ agent_stats
+ .entry(agent_addr)
+ .or_insert_with(Default::default)
+ .enter_combat = event.time - log_start_time;
+ }
+
+ ExitCombat { agent_addr } => {
+ agent_stats
+ .entry(agent_addr)
+ .or_insert_with(Default::default)
+ .exit_combat = event.time - log_start_time;
+ }
+
+ Physical {
+ source_agent_addr,
+ destination_agent_addr,
+ damage,
+ ..
+ } => {
+ with! { stats = get_stats(&mut agent_stats, source_agent_addr, destination_agent_addr) => {
+ stats.total_damage += damage as u64;
+ stats.power_damage += damage as u64;
+ }}
+
+ if let Some(master) = log.master_agent(source_agent_addr) {
+ let master_stats =
+ get_stats(&mut agent_stats, master.addr, destination_agent_addr);
+ master_stats.total_damage += damage as u64;
+ master_stats.add_damage += damage as u64;
+ }
+ }
+
+ ConditionTick {
+ source_agent_addr,
+ destination_agent_addr,
+ damage,
+ ..
+ } => {
+ with! { stats = get_stats(&mut agent_stats, source_agent_addr, destination_agent_addr) => {
+ stats.total_damage += damage as u64;
+ stats.condition_damage += damage as u64;
+ }}
+
+ if let Some(master) = log.master_agent(source_agent_addr) {
+ let master_stats =
+ get_stats(&mut agent_stats, master.addr, destination_agent_addr);
+ master_stats.total_damage += damage as u64;
+ master_stats.add_damage += damage as u64;
+ }
+ }
+
+ _ => (),
+ }
+ }
+
+ let boss = log.boss();
+
+ for agent_stat in agent_stats.values_mut() {
+ tally_damage(agent_stat);
+ agent_stat.boss_damage = agent_stat
+ .per_target_damage
+ .get(&boss.addr)
+ .cloned()
+ .unwrap_or_else(Default::default);
+ }
+
+ Ok(Statistics { agent_stats })
+}
+
+/// Takes the per target damage stats and tallies them up into the total damage
+/// stats.
+fn tally_damage(stats: &mut AgentStats) {
+ for damage in stats.per_target_damage.values() {
+ stats.total_damage.total_damage += damage.total_damage;
+ stats.total_damage.power_damage += damage.power_damage;
+ stats.total_damage.condition_damage += damage.condition_damage;
+ stats.total_damage.add_damage += damage.add_damage;
+ }
+}