aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2018-07-07 03:23:25 +0200
committerDaniel Schadt <kingdread@gmx.de>2018-07-07 03:23:25 +0200
commit22051473844bddb60c8c062f511fd4b1f90d48bd (patch)
tree8001a9ff97ae7d065c2e6c56cf5fe5a98d44e6b4
parent4ea1d4f3e5082925874a271d14cc143ebf80912f (diff)
downloadevtclib-22051473844bddb60c8c062f511fd4b1f90d48bd.tar.gz
evtclib-22051473844bddb60c8c062f511fd4b1f90d48bd.tar.bz2
evtclib-22051473844bddb60c8c062f511fd4b1f90d48bd.zip
base for mechanic tracking
-rw-r--r--src/lib.rs27
-rw-r--r--src/main.rs24
-rw-r--r--src/statistics/gamedata.rs20
-rw-r--r--src/statistics/mechanics.rs42
-rw-r--r--src/statistics/mod.rs11
-rw-r--r--src/statistics/trackers.rs38
6 files changed, 122 insertions, 40 deletions
diff --git a/src/lib.rs b/src/lib.rs
index ab32bd1..98f47ae 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -95,27 +95,27 @@ pub enum AgentName {
/// An agent.
#[derive(Debug, Clone, Getters)]
pub struct Agent {
- #[get="pub"]
+ #[get = "pub"]
addr: u64,
- #[get="pub"]
+ #[get = "pub"]
kind: AgentKind,
- #[get="pub"]
+ #[get = "pub"]
toughness: i16,
- #[get="pub"]
+ #[get = "pub"]
concentration: i16,
- #[get="pub"]
+ #[get = "pub"]
healing: i16,
- #[get="pub"]
+ #[get = "pub"]
condition: i16,
- #[get="pub"]
+ #[get = "pub"]
name: AgentName,
- #[get="pub"]
+ #[get = "pub"]
instance_id: u16,
- #[get="pub"]
+ #[get = "pub"]
first_aware: u64,
- #[get="pub"]
+ #[get = "pub"]
last_aware: u64,
- #[get="pub"]
+ #[get = "pub"]
master_agent: Option<u64>,
}
@@ -196,6 +196,11 @@ impl Log {
self.boss_agents().into_iter().any(|a| *a.addr() == addr)
}
+ /// Returns the boss/encounter id.
+ pub fn boss_id(&self) -> u16 {
+ self.boss_id
+ }
+
/// Return all events present in this log.
pub fn events(&self) -> &[Event] {
&self.events
diff --git a/src/main.rs b/src/main.rs
index c4f4e0a..81ab36c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -143,11 +143,16 @@ pub fn main() -> Result<(), evtclib::raw::parser::ParseError> {
let my_addr = me(&processed);
let mine = stats.agent_stats.get(&my_addr).expect("My stats not found");
- let my_damage = stats.damage_log.damage(|m| m.source == my_addr && processed.is_boss(m.target));
+ let my_damage = stats
+ .damage_log
+ .damage(|m| m.source == my_addr && processed.is_boss(m.target));
- let combat_time = (mine.exit_combat - mine.enter_combat) as f32/ 1000.;
+ let combat_time = (mine.exit_combat - mine.enter_combat) as f32 / 1000.;
println!("Damages: {:?}", stats.damage_log);
- println!("Combat time: {} ({} till {})", combat_time, mine.enter_combat, mine.exit_combat);
+ println!(
+ "Combat time: {} ({} till {})",
+ combat_time, mine.enter_combat, mine.exit_combat
+ );
println!("My boss dps: {:?}", my_damage.0 as f32 / combat_time);
for boon in evtclib::statistics::gamedata::BOONS {
@@ -157,6 +162,19 @@ pub fn main() -> Result<(), evtclib::raw::parser::ParseError> {
println!("{}: {}", boon.1, avg);
}
+ for agent in processed.players() {
+ println!("{:?}", agent.name());
+ for mechanic in evtclib::statistics::gamedata::get_mechanics(processed.boss_id()) {
+ println!(
+ "{}: {}",
+ mechanic.2,
+ stats
+ .mechanic_log
+ .count(|m, a| m == mechanic && a == *agent.addr())
+ );
+ }
+ }
+
//println!("NPCs: {:#?}", processed.npcs().collect::<Vec<_>>());
println!("Bosses: {:#?}", processed.boss_agents());
diff --git a/src/statistics/gamedata.rs b/src/statistics/gamedata.rs
index 1b5fec6..3bd895e 100644
--- a/src/statistics/gamedata.rs
+++ b/src/statistics/gamedata.rs
@@ -13,6 +13,8 @@ pub enum Boss {
/// second phase. This agent will have another ID, see
/// [`XERA_PHASE2_ID`](constant.XERA_PHASE2_ID.html).
Xera = 0x3F76,
+
+ Samarog = 0x4324,
}
/// ID for Xera in the second phase.
@@ -112,15 +114,23 @@ pub enum Trigger {
pub struct Mechanic(pub u16, pub Trigger, pub &'static str);
macro_rules! mechanics {
- ($boss_id:expr => [ $($name:expr => $trigger:expr,)* ]) => {
- $(Mechanic($boss_id as u16, $trigger, $name)),*
+ ( $( $boss_id:expr => [ $($name:expr => $trigger:expr,)* ], )* ) => {
+ &[
+ $( $(Mechanic($boss_id as u16, $trigger, $name)),* ),*
+ ]
}
}
/// A slice of all mechanics that we know about.
-pub static MECHANICS: &[Mechanic] = &[mechanics! { Boss::ValeGuardian => [
- "Unstable Magic Spike" => Trigger::SkillOnPlayer(31860),
-]}];
+pub static MECHANICS: &[Mechanic] = mechanics! {
+ Boss::ValeGuardian => [
+ "Unstable Magic Spike" => Trigger::SkillOnPlayer(31860),
+ ],
+ Boss::Samarog => [
+ "Prisoner Sweep" => Trigger::SkillOnPlayer(38168),
+ "Shockwave" => Trigger::SkillOnPlayer(37996),
+ ],
+};
/// Get all mechanics that belong to the given boss.
pub fn get_mechanics(boss_id: u16) -> Vec<&'static Mechanic> {
diff --git a/src/statistics/mechanics.rs b/src/statistics/mechanics.rs
new file mode 100644
index 0000000..5a16204
--- /dev/null
+++ b/src/statistics/mechanics.rs
@@ -0,0 +1,42 @@
+use super::gamedata::Mechanic;
+use super::math::{Monoid, RecordFunc, Semigroup};
+
+use std::fmt;
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Counter(u32);
+
+impl Semigroup for Counter {
+ #[inline]
+ fn combine(&self, other: &Counter) -> Counter {
+ Counter(self.0 + other.0)
+ }
+}
+
+impl Monoid for Counter {
+ #[inline]
+ fn mempty() -> Counter {
+ Counter(0)
+ }
+}
+
+#[derive(Clone, Default)]
+pub struct MechanicLog {
+ inner: RecordFunc<u64, (&'static Mechanic, u64), Counter>,
+}
+
+impl MechanicLog {
+ pub fn increase(&mut self, time: u64, mechanic: &'static Mechanic, agent: u64) {
+ self.inner.insert(time, (mechanic, agent), Counter(1));
+ }
+
+ pub fn count<F: FnMut(&'static Mechanic, u64) -> bool>(&self, mut filter: F) -> u32 {
+ self.inner.tally_only(|(a, b)| filter(a, *b)).0
+ }
+}
+
+impl fmt::Debug for MechanicLog {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "MechanicLog {{ ... }}")
+ }
+}
diff --git a/src/statistics/mod.rs b/src/statistics/mod.rs
index a46fc27..f46f778 100644
--- a/src/statistics/mod.rs
+++ b/src/statistics/mod.rs
@@ -7,10 +7,12 @@ pub mod boon;
pub mod damage;
pub mod gamedata;
pub mod math;
+pub mod mechanics;
pub mod trackers;
use self::boon::BoonLog;
use self::damage::DamageLog;
+use self::mechanics::MechanicLog;
use self::trackers::{RunnableTracker, Tracker};
pub type StatResult<T> = Result<T, StatError>;
@@ -41,6 +43,8 @@ macro_rules! try_tracker {
pub struct Statistics {
/// The complete damage log.
pub damage_log: DamageLog,
+ /// The complete mechanics log.
+ pub mechanic_log: MechanicLog,
/// A map mapping agent addresses to their stats.
pub agent_stats: HashMap<u64, AgentStats>,
}
@@ -90,6 +94,10 @@ pub fn calculate(log: &Log) -> StatResult<Statistics> {
let mut combat_time_tracker = trackers::CombatTimeTracker::new();
let mut boon_tracker = trackers::BoonTracker::new();
+ let mechanics = gamedata::get_mechanics(log.boss_id);
+ let boss_addr = log.boss_agents().into_iter().map(|x| *x.addr()).collect();
+ let mut mechanic_tracker = trackers::MechanicTracker::new(boss_addr, mechanics);
+
run_trackers(
log,
&mut [
@@ -97,6 +105,7 @@ pub fn calculate(log: &Log) -> StatResult<Statistics> {
&mut log_start_tracker,
&mut combat_time_tracker,
&mut boon_tracker,
+ &mut mechanic_tracker,
],
)?;
@@ -131,9 +140,11 @@ pub fn calculate(log: &Log) -> StatResult<Statistics> {
}
let damage_log = try_tracker!(damage_tracker.finalize());
+ let mechanic_log = try_tracker!(mechanic_tracker.finalize());
Ok(Statistics {
damage_log,
+ mechanic_log,
agent_stats,
})
}
diff --git a/src/statistics/trackers.rs b/src/statistics/trackers.rs
index d18e9c5..93c7f49 100644
--- a/src/statistics/trackers.rs
+++ b/src/statistics/trackers.rs
@@ -23,6 +23,7 @@ use super::super::{Event, EventKind, Log};
use super::boon::{BoonLog, BoonQueue};
use super::damage::{DamageLog, DamageType};
use super::gamedata::{self, Mechanic, Trigger};
+use super::mechanics::MechanicLog;
use fnv::FnvHashMap;
@@ -354,37 +355,33 @@ impl Tracker for BoonTracker {
/// A tracker that tracks boss mechanics for each player.
pub struct MechanicTracker {
- mechanics: HashMap<&'static Mechanic, u32>,
+ log: MechanicLog,
available_mechanics: Vec<&'static Mechanic>,
- boss_address: u64,
- agent_address: u64,
+ boss_addresses: Vec<u64>,
}
impl MechanicTracker {
/// Create a new mechanic tracker that watches over the given mechanics.
- pub fn new(
- agent_address: u64,
- boss_address: u64,
- mechanics: Vec<&'static Mechanic>,
- ) -> MechanicTracker {
+ pub fn new(boss_addresses: Vec<u64>, mechanics: Vec<&'static Mechanic>) -> MechanicTracker {
MechanicTracker {
- mechanics: HashMap::new(),
+ log: MechanicLog::default(),
available_mechanics: mechanics,
- boss_address,
- agent_address,
+ boss_addresses,
}
}
}
+impl MechanicTracker {
+ fn is_boss(&self, addr: u64) -> bool {
+ self.boss_addresses.contains(&addr)
+ }
+}
+
impl Tracker for MechanicTracker {
- type Stat = HashMap<&'static Mechanic, u32>;
+ type Stat = MechanicLog;
type Error = !;
fn feed(&mut self, event: &Event) -> Result<(), Self::Error> {
- fn increase(map: &mut HashMap<&'static Mechanic, u32>, entry: &'static Mechanic) {
- *map.entry(entry).or_insert(0) += 1;
- }
-
for mechanic in &self.available_mechanics {
match (&event.kind, &mechanic.1) {
(
@@ -395,11 +392,10 @@ impl Tracker for MechanicTracker {
..
},
Trigger::SkillOnPlayer(trigger_id),
- ) if skill_id == trigger_id
- && *source_agent_addr == self.boss_address
- && *destination_agent_addr == self.agent_address =>
+ ) if skill_id == trigger_id && self.is_boss(*source_agent_addr) =>
{
- increase(&mut self.mechanics, mechanic);
+ self.log
+ .increase(event.time, mechanic, *destination_agent_addr);
}
_ => (),
}
@@ -408,6 +404,6 @@ impl Tracker for MechanicTracker {
}
fn finalize(self) -> Result<Self::Stat, Self::Error> {
- Ok(self.mechanics)
+ Ok(self.log)
}
}