diff options
-rw-r--r-- | src/event.rs | 140 | ||||
-rw-r--r-- | src/gamedata.rs | 14 | ||||
-rw-r--r-- | src/raw/types.rs | 73 |
3 files changed, 222 insertions, 5 deletions
diff --git a/src/event.rs b/src/event.rs index 0a904bc..27ed2db 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,7 +1,7 @@ //! Event definitions. //! //! This module contains the different types of events in their high-level form. -use super::raw; +use super::{gamedata::Ruleset, raw}; use std::convert::TryFrom; use std::fmt::Write; @@ -22,10 +22,12 @@ pub enum FromRawEventError { UnknownDamageEvent, #[error("an unknown language byte was found")] UnknownLanguage, + #[error("an unknown ruleset bit was found")] + UnknownRuleset, #[error("the event contains invalid text")] InvalidText, - #[error("an unexpected REPLINFO was found")] - UnexpectedReplInfo, + #[error("an unexpected internal event was found")] + UnexpectedInternalEvent, } /// A rusty enum for all possible combat events. @@ -214,6 +216,69 @@ pub enum EventKind { /// Note that the tag id is volatile and depends on the game build. Do not rely on the actual /// value of this! Tag { agent_addr: u64, tag_id: i32 }, + + /// The given agent has a new barrier percentage. + /// + /// The percentage is given multiplied by 100, so 95% is given as 9500. + BarrierUpdate { + agent_addr: u64, + percentage: u32, + }, + + /// The arcdps stats have been reset. + /// + /// The given ID is the species ID of the agent that triggered the reset. + StatReset { species_id: u64 }, + + /// When the map instance has been started. + /// + /// The instance age is given in milliseconds. + InstanceStart { + age: u64, + }, + + /// The log's boss agent changed. + LogNpcUpdate { + species_id: u64, + agent_address: u64, + timestamp: u32, + }, + + /// The fractal scale. + FractalScale { scale: u32 }, + + /// Which ruleset is active. + Ruleset { ruleset: Ruleset }, + + /// A squad ground marker has been placed. + /// + /// `x`/`y`/`z` give the coordinates. If they are zero or infinite, the marker has been + /// removed. + /// + /// `marker_index` gives the index of the marker (e.g. 0 is arrow). + SquadMarker { + x: f32, + y: f32, + z: f32, + marker_index: u8, + }, + + /// Version string of the arcdps build. + ArcBuild { + build: String, + }, + + /// Glider state of the given agent. + Glider { + agent_addr: u64, + deployed: bool, + }, + + /// A stun has been broken early. + Stunbreak { + agent_addr: u64, + remaining_duration: i32, + } } /// A higher-level representation of a combat event. @@ -414,7 +479,10 @@ impl TryFrom<&raw::CbtEvent> for Event { }, // The README says "internal use, won't see anywhere", so if we find one, we treat it // as an error. - CbtStateChange::ReplInfo => return Err(FromRawEventError::UnexpectedReplInfo), + CbtStateChange::ReplInfo + | CbtStateChange::IdleEvent => { + return Err(FromRawEventError::UnexpectedInternalEvent) + }, CbtStateChange::StackActive => EventKind::StackActive { agent_addr: raw_event.src_agent, stack_id: raw_event.dst_agent as u32, @@ -424,6 +492,59 @@ impl TryFrom<&raw::CbtEvent> for Event { stack_id: raw_event.padding_end, duration: raw_event.value, }, + CbtStateChange::BarrierUpdate => EventKind::BarrierUpdate { + agent_addr: raw_event.src_agent, + percentage: raw_event.dst_agent as u32, + }, + CbtStateChange::StatReset => EventKind::StatReset { + species_id: raw_event.src_agent + }, + CbtStateChange::InstanceStart => EventKind::InstanceStart { + age: raw_event.src_agent, + }, + CbtStateChange::LogNpcUpdate => EventKind::LogNpcUpdate { + species_id: raw_event.src_agent, + agent_address: raw_event.dst_agent, + timestamp: u32::from_le_bytes(raw_event.value.to_le_bytes()), + }, + CbtStateChange::FractalScale => EventKind::FractalScale { + scale: raw_event.src_agent as u32, + }, + CbtStateChange::Ruleset => EventKind::Ruleset { + ruleset: if raw_event.src_agent & 1 > 0 { + Ruleset::PvE + } else if raw_event.src_agent & 2 > 0 { + Ruleset::WvW + } else if raw_event.src_agent & 4 > 0 { + Ruleset::PvP + } else { + return Err(FromRawEventError::UnknownRuleset); + } + }, + CbtStateChange::SquadMarker => EventKind::SquadMarker { + x: f32::from_bits((raw_event.src_agent >> 32) as u32), + y: f32::from_bits((raw_event.src_agent & 0xffff_ffff) as u32), + z: f32::from_bits((raw_event.dst_agent >> 32) as u32), + marker_index: raw_event.skillid as u8, + }, + CbtStateChange::ArcBuild => { + // Skip 8 bytes because the text starts at src_agent, and not at time. + let data = &get_error_bytes(raw_event)[8..]; + EventKind::ArcBuild { + build: raw::cstr_up_to_nul(data) + .ok_or(FromRawEventError::InvalidText)? + .to_string_lossy() + .into_owned(), + } + }, + CbtStateChange::Glider => EventKind::Glider { + agent_addr: raw_event.src_agent, + deployed: raw_event.value > 0, + }, + CbtStateChange::Stunbreak => EventKind::Stunbreak { + agent_addr: raw_event.src_agent, + remaining_duration: raw_event.value, + }, // XXX: implement proper handling of those events! CbtStateChange::BuffInfo | CbtStateChange::BuffFormula @@ -431,7 +552,16 @@ impl TryFrom<&raw::CbtEvent> for Event { | CbtStateChange::SkillTiming | CbtStateChange::BreakbarState | CbtStateChange::BreakbarPercent - | CbtStateChange::BarrierUpdate => { + | CbtStateChange::RateHealth + | CbtStateChange::IdToGuid + | CbtStateChange::Effect2 + // Probably not too useful for us: + | CbtStateChange::Extension + | CbtStateChange::ApiDelayed + | CbtStateChange::ExtensionCombat + // Retired, would need to find docs first: + | CbtStateChange::Last90BeforeDown + | CbtStateChange::Effect => { return Err(FromRawEventError::UnknownStateChange( raw_event.is_statechange, )) diff --git a/src/gamedata.rs b/src/gamedata.rs index c6f3040..8fcd45f 100644 --- a/src/gamedata.rs +++ b/src/gamedata.rs @@ -8,6 +8,20 @@ use std::{ }; use thiserror::Error; +/// The different rulesets, affecting skill & trait balancing. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum Ruleset { + /// Player-versus-Environment. + /// + /// Active in open world, raids, strikes, fractals, ... + PvE, + /// World-versus-World. + WvW, + /// (Structured) Player-versus-Player. + PvP, +} + /// The game mode in which a log was produced. /// /// Note that the distinction made here is relatively arbitrary, but hopefully still useful. In diff --git a/src/raw/types.rs b/src/raw/types.rs index 1bc3d08..b272c9b 100644 --- a/src/raw/types.rs +++ b/src/raw/types.rs @@ -191,12 +191,85 @@ pub enum CbtStateChange { /// `src_agent` is agent, `value` is float with percent BreakbarPercent, /// `time` is the start of the error string. + /// + /// In modern arcdps, this is called `CBTS_INTEGRITY`, but we leave it for backward + /// compatibility. Error, /// `src_agent` is the agent, `value` is the tag id + /// + /// In modern arcdps, this is called `CBTS_MARKER`, but we leave it for backward compatibility. Tag, /// `src_agent` is at barrier percent, `dst_agent` is the percentage times 10000 (so 99.5% /// will be 9950). BarrierUpdate, + /// `src_agent` is the species id of the agent that triggered the reset. + StatReset, + /// For extension use, not managed by arcdps. + Extension, + /// For events that are deemed unsafe to report real-time. + /// + /// Should not appear in evtc files. + ApiDelayed, + /// Map instance start. + /// + /// `src_agent` are milliseconds since the instance was created. + InstanceStart, + /// Tick health. + /// + /// `src_agent` is 25 - tickrate, when tickrate <= 20 + RateHealth, + /// No longer used. + Last90BeforeDown, + /// No longer used. + Effect, + /// content id to guid association for volatile types + /// + /// src_agent: (uint8_t*)&src_agent is uint8_t[16] guid of content + /// overstack_value: is of enum contentlocal + IdToGuid, + /// Log boss agent changed. + /// + /// src_agent: species id of agent + /// dst_agent: related to agent + /// value: as uint32_t, server unix timestamp + LogNpcUpdate, + /// Internal use. + IdleEvent, + /// For extension use. + ExtensionCombat, + /// Fractal scale. + /// + /// `src_agent` will be the fractal scale. + FractalScale, + /// Play graphical effect. + /// + /// src_agent: related to agent + /// dst_agent: effect at location of agent (if applicable) + /// value: (float*)&value is float[3], location x/y/z (if not at agent location) + /// iff: (uint32_t*)&iff is uint32_t[1], effect duration + /// buffremove: (uint32_t*)&buffremove is uint32_t[1], trackable id of effect. id dst_agent and location is 0/0/0, effect was stopped + /// is_shields: (int16_t*)&is_shields is int16_t[3], orientation x/y/z, values are original*1000 + /// is_flanking: effect is on a non-static platform + Effect2, + /// Ruleset for self. + /// + /// `src_agent` has bit 0 if PvE, bit 1 if WvW and bit 2 if PvP. + Ruleset, + /// Squad ground markers. + /// + /// src_agent: (float*)&src_agent is float[3], x/y/z of marker location. if values are all zero or infinity, this marker is removed + /// skillid: index of marker eg. 0 is arrow + SquadMarker, + /// Arcdps build info. + /// + /// `src_agent` is a null-terminated string matching the full build string. + ArcBuild, + /// Glider status change. + /// + /// `src_agent` is the agent, `value` 1 is deployed and 0 is stowed. + Glider, + /// `src_agent` is the agent, `value` is the remaining duration. + Stunbreak, } /// Combat buff remove type |