aboutsummaryrefslogtreecommitdiff
path: root/src/statistics
diff options
context:
space:
mode:
Diffstat (limited to 'src/statistics')
-rw-r--r--src/statistics/boon.rs8
-rw-r--r--src/statistics/mod.rs22
-rw-r--r--src/statistics/trackers.rs119
3 files changed, 146 insertions, 3 deletions
diff --git a/src/statistics/boon.rs b/src/statistics/boon.rs
index 4d0348e..912fdb6 100644
--- a/src/statistics/boon.rs
+++ b/src/statistics/boon.rs
@@ -107,6 +107,9 @@ impl BoonQueue {
///
/// * `duration` - The amount of time (in milliseconds) to simulate.
pub fn simulate(&mut self, duration: u64) {
+ if duration == 0 {
+ return;
+ }
let mut remaining = duration;
match self.boon_type {
BoonType::Duration => {
@@ -138,6 +141,11 @@ impl BoonQueue {
self.queue.clear();
}
+ /// Checks if any stacks are left.
+ pub fn is_empty(&self) -> bool {
+ self.queue.is_empty()
+ }
+
/// Calculate when the stacks will have the next visible change.
///
/// This assumes that the stacks will not be modified during this time.
diff --git a/src/statistics/mod.rs b/src/statistics/mod.rs
index 1ebb517..efb934f 100644
--- a/src/statistics/mod.rs
+++ b/src/statistics/mod.rs
@@ -49,6 +49,13 @@ pub struct AgentStats {
pub total_damage: DamageStats,
/// Damage directed to the boss.
pub boss_damage: DamageStats,
+ /// 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).
@@ -95,6 +102,7 @@ pub fn calculate(log: &Log) -> StatResult<Statistics> {
let mut damage_tracker = trackers::DamageTracker::new(log);
let mut log_start_tracker = trackers::LogStartTracker::new();
let mut combat_time_tracker = trackers::CombatTimeTracker::new();
+ let mut boon_tracker = trackers::BoonTracker::new();
run_trackers(
log,
@@ -102,6 +110,7 @@ pub fn calculate(log: &Log) -> StatResult<Statistics> {
&mut damage_tracker,
&mut log_start_tracker,
&mut combat_time_tracker,
+ &mut boon_tracker,
],
)?;
@@ -139,6 +148,19 @@ pub fn calculate(log: &Log) -> StatResult<Statistics> {
.unwrap_or_else(Default::default);
}
+ let boons = try_tracker!(boon_tracker.finalize());
+ for (agent, boon_map) in &boons {
+ let agent = agent_stats.entry(*agent).or_insert_with(Default::default);
+ let combat_time = agent.combat_time() as f64;
+ if combat_time == 0. {
+ continue;
+ }
+ agent.boon_averages = boon_map
+ .iter()
+ .map(|(id, area)| (*id, *area as f64 / combat_time))
+ .collect();
+ }
+
Ok(Statistics { agent_stats })
}
diff --git a/src/statistics/trackers.rs b/src/statistics/trackers.rs
index 4f2c871..d80c5ee 100644
--- a/src/statistics/trackers.rs
+++ b/src/statistics/trackers.rs
@@ -15,6 +15,7 @@ use std::collections::HashMap;
use std::error::Error;
use super::super::{Event, EventKind, Log};
+use super::boon::{BoonQueue, BoonType};
use super::DamageStats;
// A support macro to introduce a new block.
@@ -189,9 +190,7 @@ pub struct CombatTimeTracker {
impl CombatTimeTracker {
/// Create a new combat time tracker.
pub fn new() -> CombatTimeTracker {
- CombatTimeTracker {
- times: HashMap::new(),
- }
+ Default::default()
}
}
@@ -219,3 +218,117 @@ impl Tracker for CombatTimeTracker {
Ok(self.times)
}
}
+
+/// A tracker that tracks the total "boon area" per agent.
+///
+/// The boon area is defined as the amount of stacks multiplied by the time. So
+/// 1 stack of Might for 1000 milliseconds equals 1000 "stackmilliseconds" of
+/// Might. You can use this boon area to calculate the average amount of stacks
+/// by taking the boon area and dividing it by the combat time.
+///
+/// Note that this also tracks conditions, because internally, they're handled
+/// the same way.
+#[derive(Default)]
+pub struct BoonTracker {
+ boon_areas: HashMap<u64, HashMap<u16, u64>>,
+ boon_queues: HashMap<u64, HashMap<u16, BoonQueue>>,
+ last_time: u64,
+}
+
+impl BoonTracker {
+ const MAX_STACKS: u32 = 25;
+
+ /// Creates a new boon tracker.
+ pub fn new() -> BoonTracker {
+ Default::default()
+ }
+
+ /// Updates the internal boon queues by the given amount of milliseconds.
+ ///
+ /// * `delta_t` - Amount of milliseconds to update.
+ fn update_queues(&mut self, delta_t: u64) {
+ self.boon_queues
+ .values_mut()
+ .flat_map(HashMap::values_mut)
+ .for_each(|queue| queue.simulate(delta_t));
+
+ // Throw away empty boon queues or agents without any boon queue to
+ // improve performance
+ self.boon_queues
+ .values_mut()
+ .for_each(|q| q.retain(|_, v| !v.is_empty()));
+ self.boon_queues.retain(|_, q| !q.is_empty());
+ }
+
+ /// Update the internal tracking areas.
+ ///
+ /// Does not update the boon queues.
+ ///
+ /// * `delta_t` - Amount of milliseconds that passed.
+ fn update_areas(&mut self, delta_t: u64) {
+ for (agent, queues) in &self.boon_queues {
+ for (buff_id, queue) in queues {
+ let current_stacks = queue.current_stacks();
+ let area = self.boon_areas
+ .entry(*agent)
+ .or_insert_with(Default::default)
+ .entry(*buff_id)
+ .or_insert(0);
+ *area += current_stacks as u64 * delta_t;
+ }
+ }
+ }
+
+ /// Get the boon queue for the given agent and buff_id.
+ ///
+ /// If the queue does not yet exist, create it.
+ ///
+ /// * `agent` - The agent.
+ /// * `buff_id` - The buff (or condition) id.
+ fn get_queue(&mut self, agent: u64, buff_id: u16) -> &mut BoonQueue {
+ // XXX: Properly differentiate between intensity and duration based
+ // boons, otherwise the results will be off.
+ self.boon_queues
+ .entry(agent)
+ .or_insert_with(Default::default)
+ .entry(buff_id)
+ .or_insert_with(|| BoonQueue::new(Self::MAX_STACKS, BoonType::Intensity))
+ }
+}
+
+impl Tracker for BoonTracker {
+ type Stat = HashMap<u64, HashMap<u16, u64>>;
+ type Error = !;
+
+ fn feed(&mut self, event: &Event) -> Result<(), Self::Error> {
+ let delta_t = event.time - self.last_time;
+ self.update_queues(delta_t);
+ self.update_areas(delta_t);
+ self.last_time = event.time;
+
+ match event.kind {
+ EventKind::BuffApplication {
+ destination_agent_addr,
+ buff_id,
+ duration,
+ ..
+ } => {
+ self.get_queue(destination_agent_addr, buff_id)
+ .add_stack(duration as u64);
+ }
+
+ _ => (),
+ }
+
+ Ok(())
+ }
+
+ fn finalize(self) -> Result<Self::Stat, Self::Error> {
+ println!("Number of agents: {}", self.boon_queues.len());
+ println!(
+ "Number of boon queues: {}",
+ self.boon_queues.values().flat_map(|qs| qs.values()).count()
+ );
+ Ok(self.boon_areas)
+ }
+}