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 | 
