diff --git a/evtc/Cargo.toml b/evtc/Cargo.toml index 551b3080b6..4bfb19054d 100644 --- a/evtc/Cargo.toml +++ b/evtc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "evtc" -version = "0.8.0" +version = "0.9.0" authors = ["Zerthox"] edition = "2021" description = "Rust bindings for the ArcDPS EVTC API" diff --git a/evtc/src/agent/attack_target.rs b/evtc/src/agent/attack_target.rs new file mode 100644 index 0000000000..e42946f87a --- /dev/null +++ b/evtc/src/agent/attack_target.rs @@ -0,0 +1,40 @@ +use crate::{extract::Extract, AgentId, Event, StateChange, TryExtract}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Agent is now an attack target. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct AttackTargetEvent { + /// Time of registering the attack target. + pub time: u64, + + /// Agent that is an attack target. + pub agent: AgentId, + + /// Parent gadget agent. + pub parent: AgentId, + + /// Current targetable state. + pub targetable: bool, +} + +impl Extract for AttackTargetEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + parent: AgentId::from_dst(event), + targetable: event.value != 0, + } + } +} + +impl TryExtract for AttackTargetEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::AttackTarget + } +} diff --git a/evtc/src/agent/combat.rs b/evtc/src/agent/combat.rs new file mode 100644 index 0000000000..03e4e6b3e1 --- /dev/null +++ b/evtc/src/agent/combat.rs @@ -0,0 +1,47 @@ +use crate::{ + extract::Extract, AgentId, Event, Profession, Specialization, StateChange, TryExtract, +}; +use std::mem; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Agent entered combat. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct EnterCombatEvent { + /// Time of registering the combat enter. + pub time: u64, + + /// Agent that entered combat. + pub agent: AgentId, + + /// Agent subgroup. + pub subgroup: u64, + + /// Agent profession. + pub profession: Profession, + + /// Agent elite specialization. + pub elite: Specialization, +} + +impl Extract for EnterCombatEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + subgroup: event.dst_agent, + profession: unsafe { mem::transmute::(event.value) }.into(), + elite: unsafe { mem::transmute::(event.buff_dmg) }.into(), + } + } +} + +impl TryExtract for EnterCombatEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::EnterCombat + } +} diff --git a/evtc/src/agent/glider.rs b/evtc/src/agent/glider.rs new file mode 100644 index 0000000000..3fff246cd0 --- /dev/null +++ b/evtc/src/agent/glider.rs @@ -0,0 +1,35 @@ +use crate::{extract::Extract, AgentId, Event, StateChange, TryExtract}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Agent gliding state change. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GliderEvent { + /// Time of registering the gliding state. + pub time: u64, + + /// Agent that changed gliding state. + pub agent: AgentId, + + pub deployed: bool, +} + +impl Extract for GliderEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + deployed: event.value != 0, + } + } +} + +impl TryExtract for GliderEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::Glider + } +} diff --git a/evtc/src/agent/health.rs b/evtc/src/agent/health.rs new file mode 100644 index 0000000000..f4f7f5bd4b --- /dev/null +++ b/evtc/src/agent/health.rs @@ -0,0 +1,110 @@ +use crate::{extract::Extract, AgentId, Event, StateChange, TryExtract}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Agent max health change. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MaxHealthEvent { + /// Time of registering the max health change. + pub time: u64, + + /// Agent that had their max health changed. + pub agent: AgentId, + + /// New agent max health. + pub max_health: u64, +} + +impl Extract for MaxHealthEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + max_health: event.dst_agent, + } + } +} + +impl TryExtract for MaxHealthEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::MaxHealthUpdate + } +} + +/// Agent health percent change. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct HealthUpdateEvent { + /// Time of registering the health percent change. + pub time: u64, + + /// Agent that had their health percent changed. + pub agent: AgentId, + + /// Current health percent with `1.0` being max. + pub health: f32, +} + +impl HealthUpdateEvent { + /// Conversion ratio. + pub const CONVERT: f32 = 10_000.0; +} + +impl Extract for HealthUpdateEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + health: event.dst_agent as f32 / Self::CONVERT, + } + } +} + +impl TryExtract for HealthUpdateEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::HealthUpdate + } +} + +/// Agent barrier percent change. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BarrierUpdateEvent { + /// Time of registering the barrier change. + pub time: u64, + + /// Agent that had their current barrier changed. + pub agent: AgentId, + + /// Current barrier percent with `1.0` being max. + pub barrier: f32, +} + +impl BarrierUpdateEvent { + /// Conversion ratio. + pub const CONVERT: f32 = 10_000.0; +} + +impl Extract for BarrierUpdateEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + barrier: event.dst_agent as f32 / Self::CONVERT, + } + } +} + +impl TryExtract for BarrierUpdateEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::BarrierUpdate + } +} diff --git a/evtc/src/agent/mod.rs b/evtc/src/agent/mod.rs index 87f6ba3245..7b202d01fc 100644 --- a/evtc/src/agent/mod.rs +++ b/evtc/src/agent/mod.rs @@ -4,12 +4,17 @@ mod affinity; mod agent_kind; +mod attack_target; mod breakbar; +mod combat; +mod glider; +mod health; mod id; mod status; +mod targetable; +mod team; -pub use self::affinity::*; -pub use self::agent_kind::*; -pub use self::breakbar::*; -pub use self::id::*; -pub use self::status::*; +pub use self::{ + affinity::*, agent_kind::*, attack_target::*, breakbar::*, combat::*, glider::*, health::*, + id::*, status::*, targetable::*, team::*, +}; diff --git a/evtc/src/agent/status.rs b/evtc/src/agent/status.rs index d63e4c6be5..8c08e71002 100644 --- a/evtc/src/agent/status.rs +++ b/evtc/src/agent/status.rs @@ -42,177 +42,7 @@ impl TryExtract for AgentStatusEvent { } } -/// Agent entered combat. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct EnterCombatEvent { - /// Time of registering the combat enter. - pub time: u64, - - /// Agent that entered combat. - pub agent: AgentId, - - /// Agent subgroup. - pub subgroup: u64, -} - -impl Extract for EnterCombatEvent { - #[inline] - unsafe fn extract(event: &Event) -> Self { - Self { - time: event.time, - agent: AgentId::from_src(event), - subgroup: event.dst_agent, - } - } -} - -impl TryExtract for EnterCombatEvent { - #[inline] - fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::EnterCombat - } -} - -/// Agent max health change. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct MaxHealthEvent { - /// Time of registering the max health change. - pub time: u64, - - /// Agent that had their max health changed. - pub agent: AgentId, - - /// New agent max health. - pub max_health: u64, -} - -impl Extract for MaxHealthEvent { - #[inline] - unsafe fn extract(event: &Event) -> Self { - Self { - time: event.time, - agent: AgentId::from_src(event), - max_health: event.dst_agent, - } - } -} - -impl TryExtract for MaxHealthEvent { - #[inline] - fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::MaxHealthUpdate - } -} - -/// Agent health percent change. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct HealthUpdateEvent { - /// Time of registering the health percent change. - pub time: u64, - - /// Agent that had their health percent changed. - pub agent: AgentId, - - /// Current health percent with `1.0` being max. - pub health: f32, -} - -impl HealthUpdateEvent { - /// Conversion ratio. - pub const CONVERT: f32 = 10_000.0; -} - -impl Extract for HealthUpdateEvent { - #[inline] - unsafe fn extract(event: &Event) -> Self { - Self { - time: event.time, - agent: AgentId::from_src(event), - health: event.dst_agent as f32 / Self::CONVERT, - } - } -} - -impl TryExtract for HealthUpdateEvent { - #[inline] - fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::HealthUpdate - } -} - -/// Agent barrier percent change. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct BarrierUpdateEvent { - /// Time of registering the barrier change. - pub time: u64, - - /// Agent that had their current barrier changed. - pub agent: AgentId, - - /// Current barrier percent with `1.0` being max. - pub barrier: f32, -} - -impl BarrierUpdateEvent { - /// Conversion ratio. - pub const CONVERT: f32 = 10_000.0; -} - -impl Extract for BarrierUpdateEvent { - #[inline] - unsafe fn extract(event: &Event) -> Self { - Self { - time: event.time, - agent: AgentId::from_src(event), - barrier: event.dst_agent as f32 / Self::CONVERT, - } - } -} - -impl TryExtract for BarrierUpdateEvent { - #[inline] - fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::BarrierUpdate - } -} - -/// Agent team change. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct TeamChangeEvent { - /// Time of registering the team change. - pub time: u64, - - /// Agent that had their team changed. - pub agent: AgentId, - - /// New team id. - pub team: u64, -} - -impl Extract for TeamChangeEvent { - #[inline] - unsafe fn extract(event: &Event) -> Self { - Self { - time: event.time, - agent: AgentId::from_src(event), - team: event.dst_agent, - } - } -} - -impl TryExtract for TeamChangeEvent { - #[inline] - fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::TeamChange - } -} - -/// Agent down contribution event. +/// Agent down contribution event (retired). #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DownContributionEvent { @@ -243,71 +73,3 @@ impl TryExtract for DownContributionEvent { event.get_statechange() == StateChange::Last90BeforeDown } } - -/// Agent is now an attack target. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct AttackTargetEvent { - /// Time of registering the attack target. - pub time: u64, - - /// Agent that is an attack target. - pub agent: AgentId, - - /// Parent gadget agent. - pub parent: AgentId, - - /// Current targetable state. - pub targetable: i32, -} - -impl Extract for AttackTargetEvent { - #[inline] - unsafe fn extract(event: &Event) -> Self { - Self { - time: event.time, - agent: AgentId::from_src(event), - parent: AgentId::from_dst(event), - targetable: event.value, - } - } -} - -impl TryExtract for AttackTargetEvent { - #[inline] - fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::AttackTarget - } -} - -/// Agent targetibility change. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct TargetableEvent { - /// Time of registering the tergetability change. - pub time: u64, - - /// Agent that had their targetability changed. - pub agent: AgentId, - - /// New targetable state. - pub targetable: i32, -} - -impl Extract for TargetableEvent { - #[inline] - unsafe fn extract(event: &Event) -> Self { - Self { - time: event.time, - agent: AgentId::from_src(event), - targetable: event.value, - } - } -} - -impl TryExtract for TargetableEvent { - #[inline] - fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::Targetable - } -} diff --git a/evtc/src/agent/targetable.rs b/evtc/src/agent/targetable.rs new file mode 100644 index 0000000000..1090eff77c --- /dev/null +++ b/evtc/src/agent/targetable.rs @@ -0,0 +1,36 @@ +use crate::{extract::Extract, AgentId, Event, StateChange, TryExtract}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Agent targetable state change. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct TargetableEvent { + /// Time of registering the targetable state change. + pub time: u64, + + /// Agent that had their targetable state changed. + pub agent: AgentId, + + /// New targetable state. + pub targetable: bool, +} + +impl Extract for TargetableEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + targetable: event.value != 0, + } + } +} + +impl TryExtract for TargetableEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::Targetable + } +} diff --git a/evtc/src/agent/team.rs b/evtc/src/agent/team.rs new file mode 100644 index 0000000000..ea278e0d7b --- /dev/null +++ b/evtc/src/agent/team.rs @@ -0,0 +1,36 @@ +use crate::{extract::Extract, AgentId, Event, StateChange, TryExtract}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Agent team change. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct TeamChangeEvent { + /// Time of registering the team change. + pub time: u64, + + /// Agent that had their team changed. + pub agent: AgentId, + + /// New team id. + pub team: u64, +} + +impl Extract for TeamChangeEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + Self { + time: event.time, + agent: AgentId::from_src(event), + team: event.dst_agent, + } + } +} + +impl TryExtract for TeamChangeEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::TeamChange + } +} diff --git a/evtc/src/buff/formula.rs b/evtc/src/buff/formula.rs index df882191c2..9356387951 100644 --- a/evtc/src/buff/formula.rs +++ b/evtc/src/buff/formula.rs @@ -21,11 +21,12 @@ pub struct BuffFormula { pub trait_self: u32, pub buff_src: u32, pub buff_self: u32, + pub content_reference: f32, // TODO: appropriate type? pub not_npc: bool, pub not_player: bool, pub is_break: bool, - pub value: u32, pub value_type: u8, + pub value: u32, } impl BuffFormula { @@ -65,11 +66,12 @@ impl From for BuffFormula { trait_self: raw.trait_self as _, buff_src: raw.buff_src as _, buff_self: raw.buff_self as _, + content_reference: raw.content_reference, not_npc: raw.not_npc != 0, not_player: raw.not_player != 0, is_break: raw.is_break != 0, - value: raw.value, value_type: raw.value_type, + value: raw.value, } } } @@ -89,11 +91,12 @@ pub struct RawBuffFormula { pub trait_self: f32, pub buff_src: f32, pub buff_self: f32, + pub content_reference: f32, pub not_npc: u8, pub not_player: u8, pub is_break: u8, - pub value: u32, pub value_type: u8, + pub value: u32, } impl RawBuffFormula { @@ -110,8 +113,8 @@ impl RawBuffFormula { impl Extract for RawBuffFormula { #[inline] unsafe fn extract(event: &Event) -> Self { - let [kind, attr1, attr2, param1, param2, param3, trait_src, trait_self] = - transmute_field!(event.time as [f32; 8]); + let [kind, attr1, attr2, param1, param2, param3, trait_src, trait_self, content_reference] = + transmute_field!(event.time as [f32; 9]); let [buff_src, buff_self] = transmute_field!(event.src_instance_id as [f32; 2]); Self { @@ -126,11 +129,12 @@ impl Extract for RawBuffFormula { trait_self, buff_src, buff_self, + content_reference, not_npc: event.is_flanking, not_player: event.is_shields, is_break: event.is_offcycle, - value: event.overstack_value, value_type: event.pad61, + value: event.overstack_value, } } } diff --git a/evtc/src/buff/stack.rs b/evtc/src/buff/stack.rs index 63f1f0299e..856b855194 100644 --- a/evtc/src/buff/stack.rs +++ b/evtc/src/buff/stack.rs @@ -18,6 +18,9 @@ pub struct StackActiveEvent { /// Stack id of new active stack. pub stack_id: u64, + + /// Current buff duration. + pub duration: i32, } impl Extract for StackActiveEvent { @@ -27,6 +30,7 @@ impl Extract for StackActiveEvent { time: event.time, agent: AgentId::from_src(event), stack_id: event.dst_agent, + duration: event.value, } } } @@ -42,9 +46,16 @@ impl TryExtract for StackActiveEvent { #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct StackResetEvent { + /// Time of registering the stack reset. pub time: u64, + + /// Agent that the stack reset happened to. pub agent: AgentId, + + /// New duration to reset to. pub duration: i32, + + /// Stack id. pub stack_id: u32, } diff --git a/evtc/src/event/event_kind.rs b/evtc/src/event/event_kind.rs index a78b90b75c..176669ab4a 100644 --- a/evtc/src/event/event_kind.rs +++ b/evtc/src/event/event_kind.rs @@ -1,8 +1,8 @@ use crate::{ agent::{ AgentStatusEvent, AttackTargetEvent, BarrierUpdateEvent, BreakbarPercentEvent, - BreakbarStateEvent, DownContributionEvent, EnterCombatEvent, HealthUpdateEvent, - MaxHealthEvent, TargetableEvent, TeamChangeEvent, + BreakbarStateEvent, DownContributionEvent, EnterCombatEvent, GliderEvent, + HealthUpdateEvent, MaxHealthEvent, TargetableEvent, TeamChangeEvent, }, buff::{ BuffApplyEvent, BuffDamageEvent, BuffFormula, BuffInfo, BuffInitialEvent, BuffRemoveEvent, @@ -10,7 +10,7 @@ use crate::{ }, effect::{Effect, EffectOld}, guid::ContentGUID, - log::{ErrorEvent, LogEvent}, + log::{ArcBuildEvent, ErrorEvent, LogEvent}, marker::{AgentMarkerEvent, SquadMarkerEvent}, player::{GuildEvent, RewardEvent}, position::PositionEvent, @@ -54,10 +54,10 @@ pub enum EventKind { HealthUpdate(HealthUpdateEvent), /// Log started. - LogStart(LogEvent), + SquadCombatStart(LogEvent), /// Log ended. - LogEnd(LogEvent), + SquadCombatEnd(LogEvent), /// Agent swapped weapon set. WeaponSwap(WeaponSwapEvent), @@ -135,7 +135,7 @@ pub enum EventKind { BreakbarPercent(BreakbarPercentEvent), /// Error. - Error(ErrorEvent), + Integrity(ErrorEvent), /// Agent has marker. AgentMarker(AgentMarkerEvent), @@ -156,7 +156,7 @@ pub enum EventKind { InstanceStart { time: u64, start: u64 }, /// Tick rate. - Tickrate { time: u64, rate: u64 }, + RateHealth { time: u64, rate: u64 }, /// Last 90% before down for downs contribution. Last90BeforeDown(DownContributionEvent), @@ -164,7 +164,7 @@ pub enum EventKind { /// Effect created or ended. EffectOld(EffectOld), - /// Id to GUID. + /// Content id to GUID. /// /// This maps a volatile content id to a stable GUID. IdToGUID(ContentGUID), @@ -202,6 +202,12 @@ pub enum EventKind { /// Squad marker placed or removed. SquadMarker(SquadMarkerEvent), + /// ArcDPS build information. + ArcBuild(ArcBuildEvent), + + /// Agent gliding state changed. + Glider(GliderEvent), + /// Unknown event. Unknown(Event), } @@ -224,8 +230,8 @@ impl From for EventKind { StateChange::Spawn => Self::Spawn(event.extract()), StateChange::Despawn => Self::Despawn(event.extract()), StateChange::HealthUpdate => Self::HealthUpdate(event.extract()), - StateChange::LogStart => Self::LogStart(event.extract()), - StateChange::LogEnd => Self::LogEnd(event.extract()), + StateChange::SquadCombatStart => Self::SquadCombatStart(event.extract()), + StateChange::SquadCombatEnd => Self::SquadCombatEnd(event.extract()), StateChange::WeaponSwap => Self::WeaponSwap(event.extract()), StateChange::MaxHealthUpdate => Self::MaxHealthUpdate(event.extract()), StateChange::PointOfView => Self::PointOfView(event.extract()), @@ -263,7 +269,7 @@ impl From for EventKind { StateChange::SkillTiming => Self::SkillTiming(event.extract()), StateChange::BreakbarState => Self::BreakbarState(event.extract()), StateChange::BreakbarPercent => Self::BreakbarPercent(event.extract()), - StateChange::Error => Self::Error(event.extract()), + StateChange::Integrity => Self::Integrity(event.extract()), StateChange::Marker => Self::AgentMarker(event.extract()), StateChange::BarrierUpdate => Self::BarrierUpdate(event.extract()), StateChange::StatReset => Self::StatReset { @@ -284,7 +290,7 @@ impl From for EventKind { time: event.time, start: event.src_agent, }, - StateChange::Tickrate => Self::Tickrate { + StateChange::RateHealth => Self::RateHealth { time: event.time, rate: event.src_agent, }, @@ -305,6 +311,8 @@ impl From for EventKind { Self::Ruleset(Ruleset::from_bits_retain(event.src_agent)) } StateChange::SquadMarker => Self::SquadMarker(event.extract()), + StateChange::ArcBuild => Self::ArcBuild(event.extract()), + StateChange::Glider => Self::Glider(event.extract()), StateChange::Unknown(_) => Self::Unknown(event), }, EventCategory::Activation => Self::Activation(event.extract()), diff --git a/evtc/src/event/mod.rs b/evtc/src/event/mod.rs index 01955a2221..6a9c5ec346 100644 --- a/evtc/src/event/mod.rs +++ b/evtc/src/event/mod.rs @@ -5,24 +5,21 @@ mod common; mod event_kind; mod old; -pub use self::category::*; -pub use self::common::*; -pub use self::event_kind::*; -pub use self::old::*; +pub use self::{category::*, common::*, event_kind::*, old::*}; #[allow(deprecated)] pub use crate::{ agent::{ AgentStatusEvent, AttackTargetEvent, BarrierUpdateEvent, BreakbarPercentEvent, - BreakbarStateEvent, DownContributionEvent, EnterCombatEvent, HealthUpdateEvent, - MaxHealthEvent, TargetableEvent, TeamChangeEvent, + BreakbarStateEvent, DownContributionEvent, EnterCombatEvent, GliderEvent, + HealthUpdateEvent, MaxHealthEvent, TargetableEvent, TeamChangeEvent, }, buff::{ BuffApplyEvent, BuffDamageEvent, BuffFormula, BuffInfo, BuffInitialEvent, BuffRemoveEvent, StackActiveEvent, StackResetEvent, }, effect::{Effect, EffectGUID, EffectOld}, - log::{ErrorEvent, LogEvent}, + log::{ArcBuildEvent, ErrorEvent, LogEvent}, marker::{AgentMarkerEvent, SquadMarkerEvent}, player::{GuildEvent, RewardEvent, TagEvent}, position::PositionEvent, diff --git a/evtc/src/log/arc_build.rs b/evtc/src/log/arc_build.rs new file mode 100644 index 0000000000..782f492d88 --- /dev/null +++ b/evtc/src/log/arc_build.rs @@ -0,0 +1,39 @@ +use crate::{ + extract::{transmute_field, Extract}, + Event, StateChange, TryExtract, +}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// ArcDPS log error. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ArcBuildEvent { + /// ArcDPS build string. + pub build: String, +} + +impl ArcBuildEvent { + pub const MAX_LEN: usize = 32; +} + +impl Extract for ArcBuildEvent { + #[inline] + unsafe fn extract(event: &Event) -> Self { + let bytes = transmute_field!(event.time as [u8; ArcBuildEvent::MAX_LEN]); + + Self { + build: String::from_utf8_lossy(&bytes) + .trim_end_matches('\0') + .into(), + } + } +} + +impl TryExtract for ArcBuildEvent { + #[inline] + fn can_extract(event: &Event) -> bool { + event.get_statechange() == StateChange::Integrity + } +} diff --git a/evtc/src/log/error.rs b/evtc/src/log/error.rs index 1d05dfcb82..47712ada20 100644 --- a/evtc/src/log/error.rs +++ b/evtc/src/log/error.rs @@ -34,6 +34,6 @@ impl Extract for ErrorEvent { impl TryExtract for ErrorEvent { #[inline] fn can_extract(event: &Event) -> bool { - event.get_statechange() == StateChange::Error + event.get_statechange() == StateChange::Integrity } } diff --git a/evtc/src/log/mod.rs b/evtc/src/log/mod.rs index 85a0286687..e0ae2b066a 100644 --- a/evtc/src/log/mod.rs +++ b/evtc/src/log/mod.rs @@ -1,6 +1,7 @@ +mod arc_build; mod error; -pub use self::error::*; +pub use self::{arc_build::*, error::*}; use crate::{extract::Extract, Event, StateChange, TryExtract}; use std::mem::transmute; @@ -30,8 +31,8 @@ impl Extract for LogEvent { unsafe fn extract(event: &Event) -> Self { Self { time: event.time, - server_time: transmute(event.value), - local_time: transmute(event.buff_dmg), + server_time: transmute::(event.value), + local_time: transmute::(event.buff_dmg), id: event.src_agent, } } @@ -42,7 +43,7 @@ impl TryExtract for LogEvent { fn can_extract(event: &Event) -> bool { matches!( event.get_statechange(), - StateChange::LogStart | StateChange::LogEnd | StateChange::LogNPCUpdate + StateChange::SquadCombatStart | StateChange::SquadCombatEnd | StateChange::LogNPCUpdate ) } } diff --git a/evtc/src/skill/activation.rs b/evtc/src/skill/activation.rs index ff9ee36a74..4de99f35f0 100644 --- a/evtc/src/skill/activation.rs +++ b/evtc/src/skill/activation.rs @@ -95,3 +95,38 @@ pub enum Activation { #[num_enum(catch_all)] Unknown(u8), } + +/// Skill animation stop (UNOFFICIAL). +/// +/// Present in `result` for activation cancels. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, IntoPrimitive, FromPrimitive, +)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "strum", + derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames) +)] +#[repr(u8)] +pub enum AnimationStop { + None = 0, + Instant = 1, + SecondUse = 2, + Transition = 3, + Partial = 4, + Ended = 5, + Cancel = 6, + StowedChange = 7, + Interrupt = 8, + Death = 9, + Downed = 10, + CrowdControl = 11, + MoveBehind = 12, + MoveSkill = 13, + MoveDodge = 14, + MoveStop = 15, + + /// Unknown or invalid. + #[num_enum(catch_all)] + Unknown(u8), +} diff --git a/evtc/src/state_change.rs b/evtc/src/state_change.rs index f6846edeb0..7a4211c7a2 100644 --- a/evtc/src/state_change.rs +++ b/evtc/src/state_change.rs @@ -20,190 +20,322 @@ pub enum StateChange { /// Not used, different kind of event. None = 0, - /// Source agent entered combat. + /// Agent entered combat. /// + /// `src_agent` entered combat. /// `dst_agent` contains the subgroup. + /// `value` contains the Profession id. + /// `buff_dmg` contains the Elite Specialization id. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: yes, limited to squad. EnterCombat = 1, - /// Source agent left combat. + /// Agent left combat. + /// + /// `src_agent` left combat. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: yes, limited to squad. ExitCombat = 2, - /// Source agent is now alive. + /// Agent is now alive. + /// + /// `src_agent` is alive. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: yes, limited to squad. ChangeUp = 3, - /// Source agent is now dead. + /// Agent is now dead. + /// + /// `src_agent` is dead. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: yes, limited to squad. ChangeDead = 4, - /// Source agent is now downed. + /// Agent is now downed. + /// + /// `src_agent` is down. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: yes, limited to squad. ChangeDown = 5, - /// Source agent is now in game tracking range. + /// Agent is now in game tracking range. /// - /// *Not used in realtime API.* + /// `src_agent` is now tracked. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no Spawn = 6, /// Source agent is no longer being tracked or out of game tracking range. /// - /// *Not used in realtime API.* + /// `src_agent` is no longer tracked. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no Despawn = 7, - /// Source agent health change. + /// Agent health change. /// + /// `src_agent` health changed. /// `dst_agent` contains percentage as `percent * 10000`. /// For example 99.5% will be `9950`. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no HealthUpdate = 8, - /// Logging has started. + /// Squad combat start, first player entered combat. Logging has started. /// /// `value` contains the server Unix timestamp as `u32`. /// `buff_dmg` contains the local Unix timestamp. /// /// `src_agent` is `0x637261` (ArcDPS id) if log EVTC and species id if realtime API. - LogStart = 9, + /// + /// EVTC: yes + /// + /// Realtime: yes + SquadCombatStart = 9, - /// Logging has ended. + /// Squad combat end, last player has left combat. Logging has ended. /// /// `value` contains the server Unix timestamp as `u32`. /// `buff_dmg` contains the local Unix timestamp. /// /// `src_agent` is `0x637261` (ArcDPS id) if log EVTC and species id if realtime API. - LogEnd = 10, + /// + /// EVTC: yes + /// + /// Realtime: yes + SquadCombatEnd = 10, - /// Source agent swapped weapon set. + /// Agent swapped weapon set. + /// + /// `src_agent` swapped weapons. + /// `dst_agent` contains the new weapon set id. + /// `value` contains the previous weapon set id. /// - /// `dst_agent` contains the current set id. /// `0`/`1` for underwater weapon sets and `4`/`5` for land weapon sets. /// `2` is bundle/kit weapon set and `3` transform weapon set. + /// + /// EVTC: yes + /// + /// Realtime: yes WeaponSwap = 11, - /// Source agent maximum health change. + /// Agent maximum health change. /// + /// `src_agent` changed max health. /// `dst_agent` contains the new maximum health. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to non-players. + /// + /// Realtime: no MaxHealthUpdate = 12, - /// Source agent is "recording" player. + /// Player recording the log. /// - /// *Not used in realtime API.* + /// `src_agent` is point of view + /// + /// EVTC: yes + /// + /// Realtime: no PointOfView = 13, - /// Source agent contains the game text language. + /// Game text language. /// - /// *Not used in realtime API.* + /// `src_agent` contains the text language id. + /// + /// EVTC: yes + /// + /// Realtime: no Language = 14, - /// Source agent contains the game build. + /// Game build. /// - /// *Not used in realtime API.* + /// `src_agent` contains the game build. + /// + /// EVTC: yes + /// + /// Realtime: no GWBuild = 15, - /// Source agent contains the sever shard id. + /// Sever shard id. /// - /// *Not used in realtime API.* + /// `src_agent` contains the shard id. + /// + /// EVTC: yes + /// + /// Realtime: no ShardId = 16, /// Source agent got a reward chest. /// - /// Source is always self. + /// `src_agent` is always self. /// `dst_agent` contains the reward id. - /// Value contains the reward type. + /// `value` contains the reward type. + /// + /// EVTC: yes + /// + /// Realtime: yes Reward = 17, + /// Initially present buffs. + /// + /// Identical to buff application event. /// Appears once per buff per agent on logging start. /// - /// *(`statechange == 18` and `buff == 18`, normal combat event otherwise)* + /// EVTC: yes, limited to squad outside instances. + /// + /// Realtime: yes, limited to squad. BuffInitial = 18, - /// Source agent position change. + /// Agent position change. /// - /// `dst_agent` contains x/y/z as array of 3 floats. + /// `src_agent` changed position. + /// `dst_agent` contains XYZ coordinates as `[f32; 3]`. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no Position = 19, - /// Source agent velocity change. + /// Agent velocity change. /// - /// `dst_agent` contains x/y/z as array of 3 floats. + /// `src_agent` changed position. + /// `dst_agent` contains XYZ velocity as `[f32; 3]`. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no Velocity = 20, - /// Source agent facing change. + /// Agent facing change. /// - /// `dst_agent` contains x/y as array of 2 floats. + /// `src_agent` changed position. + /// `dst_agent` contains XY direction as `[f32; 2]`. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no Facing = 21, - /// Source agent team change. + /// Agent team change. /// + /// `src_agent` changed team. /// `dst_agent` contains the new team id. + /// `value` contains the previous team id. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: yes, limited to squad. TeamChange = 22, - /// Source agent is now an attack target. + /// Agent is an attack target of parent gadget. /// - /// `dst_agent` is the parent agent (gadget type). + /// `src_agent` is the attack target. + /// `dst_agent` is the parent gadget. /// `value` contains the current targetable state. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no AttackTarget = 23, - /// Source agent targetability change. + /// Agent changed targetable state. /// + /// `src_agent` changed targetable state. /// `dst_agent` contains the new targetable state. /// `0` for no, `1` for yes. Default is yes. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no Targetable = 24, - /// Source agent contains the map id. + /// Map id. /// - /// *Not used in realtime API.* + /// `src_agent` contains the map id. + /// + /// EVTC: yes + /// + /// Realtime: no MapId = 25, /// Used internally by ArcDPS. /// Should not appear anywhere. ReplInfo = 26, - /// Source agent with active buff. + /// Buff stack is now active. + /// + /// `src_agent` has the buff. + /// `dst_agent` contains the buff stack id marked active. + /// `value` contains the current buff duration. /// - /// `dst_agent` contains the stack id marked active. + /// EVTC: yes, limited to squad outside instances. + /// + /// Realtime: yes, limited to squad. StackActive = 27, - /// Source agent with reset buff. + /// Buff stack duration changed. + /// + /// `src_agent` has the buff. + /// `value` contains the new duration to reset to (also marks inactive). + /// `pad61-64` contains the stack id. /// - /// `value` is the duration to reset to (also marks inactive). - /// `pad61` contains the stack id. + /// EVTC: yes, limited to squad outside instances. + /// + /// Realtime: yes, limited to squad. StackReset = 28, - /// Source agent is in guild. + /// Agent is in guild. /// - /// `dst_agent` until `buff_dmg` is [`u128`] (16 byte) guid. + /// `src_agent` is in guild. + /// `dst_agent` contains the guild guid as [u8; 16]. /// /// Given in client form, needs minor rearrange for API form. + /// + /// EVTC: yes, limited to squad outside instances. + /// + /// Realtime: yes, limited to squad. Guild = 29, /// Buff information. /// + /// `skill_id` is skilldef id of buff. /// `is_offcycle` contains the category. /// `pad61` contains the stacking type. /// `src_master_instance_id` contains the max stacks. /// `overstack_value` contains the duration cap. /// - /// If `is_flanking` probably invulnerable. - /// If `is_shields` probably invert. - /// If `pad62` probably resistance. + /// If `is_flanking` probably invulnerability-like. + /// If `is_shields` probably invert-like. + /// If `pad62` probably resistance-like. /// - /// *Not used in realtime API.* + /// One event per buff. + /// + /// EVTC: yes + /// + /// Realtime: no BuffInfo = 30, /// Buff formula. /// - /// `time` contains `type`, `attr1`, `attr2`, `param1`, `param2`, `param3`, `trait_src` and `trait_self` as `[f32; 8]`. - /// `src_instance_id` contains `buff_src` and `buff_self` as `[f32; 2]`. + /// `skill_id` is skilldef id of buff. + /// `time` contains `type`, `attr1`, `attr2`, `param1`, `param2`, `param3`, `trait_condition_src` and `trait_condition_self`, `content_reference` as `[f32; 9]`. + /// `src_instance_id` contains `buff_condition_src` and `buff_condition_self` as `[f32; 2]`. /// /// If `is_flanking` not NPC. /// If `is_shields` not player. @@ -211,123 +343,171 @@ pub enum StateChange { /// /// `overstack_value` is value of type determined by `pad61`. /// - /// Once per formula. + /// One event per buff formula. /// - /// *Not used in realtime API.* + /// EVTC: yes + /// + /// Realtime: no BuffFormula = 31, /// Skill information. /// + /// `skill_id` is skilldef id of ability. /// `time` contains `recharge`, `range0`, `range1` and `tooltiptime` as `[f32; 4]`. /// - /// *Not used in realtime API.* + /// One event per ability. + /// + /// EVTC: yes + /// + /// Realtime: no SkillInfo = 32, /// Skill action. /// - /// `src_agent` contains the action. - /// `dst_agent` contains at which millisecond. + /// `skill_id` is skilldef id of ability. + /// `src_agent` contains the action type. + /// `dst_agent` contains the time since activation in milliseconds. /// - /// One per timing. + /// One event per ability timing. /// - /// *Not used in realtime API.* + /// EVTC: yes + /// + /// Realtime: no SkillTiming = 33, - /// Source agent breakbar state change. + /// Agent breakbar state change. /// - /// Value is [`u16`] game enum (active, recover, immune, none). + /// `src_agent` changed breakbar state. + /// `value` contains the new breakbar state as [`u16`] (game enum: active, recover, immune, none). /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no BreakbarState = 34, /// Breakbar percentage. /// - /// `value` contains percentage as float. + /// `src_agent` has breakbar percentage. + /// `value` contains percentage as [`f32`]. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no BreakbarPercent = 35, - /// Error. + /// Message with log integrity information. /// - /// `time` contains the error message as an array of up to 32 characters. + /// `time` contains the message as a null-terminated C string. /// - /// *Not used in realtime API.* - Error = 36, + /// EVTC: yes + /// + /// Realtime: no + Integrity = 36, - /// Source agent has marker. + /// Agent has a marker. /// - /// `src_agent` is agent. - /// `value` is the id of the marker (volatile, depends on game build). - /// `buff` will be non-zero if commander. + /// `src_agent` has the marker. + /// `value` contains the markerdef id (volatile, depends on game build). + /// If `buff`, marker is a commander tag. /// /// A marker id of `0` indicates a remove. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no Marker = 37, - /// Source agent barrier change. + /// Agent barrier change. /// + /// `src_agent` has barrier percentage. /// `dst_agent` contains percentage as `percent * 10000`. /// For example 99.5% will be `9950`. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no BarrierUpdate = 38, /// Arc UI stats reset. /// - /// `src_agent` contains the NPC id of the active log. + /// `src_agent` contains the species id of the agent triggering the reset, for example boss species id. /// - /// *Not used in log EVTC.* + /// EVTC: yes + /// + /// Realtime: yes StatReset = 39, /// A custom event created by an extension (addon/plugin). Extension = 40, /// Delayed combat event. + /// + /// Event deemed "unsafe" for realtime that was held back until after squad left combat. + /// + /// EVTC: no + /// + /// Realtime: yes ApiDelayed = 41, - /// Instance started. + /// Map instance start timestamp. /// - /// `src_agent` contains the time in ms at which the instance was likely started. + /// `src_agent` contains the time in milliseconds since the instance was started. + /// + /// EVTC: yes + /// + /// Realtime: yes InstanceStart = 42, - /// Tick rate. + /// Tick rate health. /// + /// `src_agent` is `25 - tickrate` when `tickrate < 21`. /// Every 500ms. - /// `src_agent` is `25 - tickrate` (when `tickrate < 21`). - Tickrate = 43, + /// + /// EVTC: yes + /// + /// Realtime: no + RateHealth = 43, - /// Last 90% before down. + /// Retired since 230716. /// - /// `src_agent` is enemy agent that went down, `dst_agent` is time in ms since last 90%. - /// For downs contribution. + /// Previously: *Last 90% before down.* + /// + /// *`src_agent` is enemy agent that went down, `dst_agent` is time in ms since last 90%. + /// For downs contribution.* Last90BeforeDown = 44, - /// Effect created or ended. + /// Retired since 230716. /// - /// `skill_id` contains the effect id. - /// `src_agent` is the effect owner. - /// `dst_agent` if effect located at agent. - /// Otherwise `value` contains XYZ position as `[f32; 3]`, `affinity` contains XY orientation as `[f32; 2]`, `pad61` contains Z orientation as [`f32`]. - /// `is_shields` contains duration as [`u16`]. - /// If `is_flanking`, duration is a tracking id. - /// If effect id is `0`, effect ended and `is_shields` contains tracking id. + /// Previously: *Effect created or ended.* /// - /// *Not used in realtime API.* + /// *`skill_id` contains the effect id.* + /// *`src_agent` is the effect owner.* + /// *`dst_agent` if effect located at agent.* + /// *Otherwise `value` contains XYZ position as `[f32; 3]`, `affinity` contains XY orientation as `[f32; 2]`, `pad61` contains Z orientation as [`f32`].* + /// *`is_shields` contains duration as [`u16`].* + /// *If `is_flanking`, duration is a tracking id.* + /// *If effect id is `0`, effect ended and `is_shields` contains tracking id.* EffectOld = 45, - /// Id to GUID. + /// Content id to GUID. /// - /// `src_agent` contains [`u128`] (16 byte) persistent content guid. - /// `overstack_value` is a variant of [`ContentLocal`](crate::guid::ContentLocal), `skill_id` is content id. + /// `skill_id` is the content id. + /// `src_agent` contains the persistent content guid as `[u8; 16]`. + /// `overstack_value` contains a variant of [`ContentLocal`](crate::guid::ContentLocal). /// /// *Not used in realtime API.* IdToGUID = 46, - /// Log NPC changed. + /// Log boss agent changed. /// + /// `src_agent` contains the species id of the agent. + /// `dst_agent` is the boss agent. /// `value` contains the server Unix timestamp as `u32`. /// `buff_dmg` contains the local Unix timestamp. /// - /// `src_agent` is species id. + /// EVTC: yes + /// + /// Realtime: yes LogNPCUpdate = 47, /// Used internally by ArcDPS. @@ -342,33 +522,66 @@ pub enum StateChange { /// Fractal scale. /// /// `src_agent` contains the scale. + /// + /// EVTC: yes + /// + /// Realtime: no FractalScale = 50, - /// Effect created or ended. + /// Visual effect created or ended. /// /// `skill_id` contains the effect id. - /// `src_agent` is the effect owner. + /// `src_agent` is the effect owner (if any). /// `dst_agent` if effect located at agent. - /// Otherwise `value` contains XYZ position as `[f32; 3]`. + /// Otherwise `value` contains XYZ location as `[f32; 3]`. /// `affinity` contains duration as [`u32`]. /// `is_buffremove` contains trackable id as [`u32`]. /// `is_shields` contains orientation as `[i16; 3]`. - /// Orientation values are original multiplied by `1000` or [`i16::MIN`]/[`i16::MAX`] if out of bounds. + /// Orientation values are `original * 1000` or [`i16::MIN`]/[`i16::MAX`] if out of bounds. /// - /// *Not used in realtime API.* + /// EVTC: yes, limited to agent table outside instances + /// + /// Realtime: no Effect = 51, /// Combat ruleset. /// - /// `src_agent` has bit 0 set if PvE rules buff, bit 1 if WvW rules and bit 2 if PvP rules. + /// `src_agent` has bit 0 set if PvE rules, bit 1 if WvW rules and bit 2 if PvP rules. + /// + /// EVTC: yes + /// + /// Realtime: no Ruleset = 52, - /// Squad marker placed or removed. + /// Squad ground marker placed or removed. /// - /// `src_agent` contains the XYZ location as `[f32; 3]` or [`f32::INFINITY`] if removed. - /// `skill_id` contains the index of the squad marker. + /// `src_agent` contains XYZ location as `[f32; 3]` or [`f32::INFINITY`] if removed. + /// `skill_id` contains the index of the squad marker, for example `0` for Arrow marker. + /// + /// EVTC: yes + /// + /// Realtime: no SquadMarker = 53, + /// ArcDPS build information. + /// + /// `src_agent` contains ArcDPS build as null-terminated C string. + /// + /// EVTC: yes + /// + /// Realtime: no + ArcBuild = 54, + + /// Agent gliding state change. + /// + /// `src_agent` changed gliding state. + /// `value` contains `1` if deployed and `0` if stowed. + /// + /// EVTC: yes, limited to agent table outside instances. + /// + /// Realtime: no + Glider = 55, + /// Unknown or invalid. #[num_enum(catch_all)] Unknown(u8), @@ -389,8 +602,8 @@ impl StateChange { | Self::Spawn | Self::Despawn | Self::HealthUpdate - | Self::LogStart - | Self::LogEnd + | Self::SquadCombatStart + | Self::SquadCombatEnd | Self::WeaponSwap | Self::MaxHealthUpdate | Self::Reward @@ -414,6 +627,8 @@ impl StateChange { | Self::LogNPCUpdate | Self::ExtensionCombat | Self::Effect + | Self::SquadMarker + | Self::Glider ) } } diff --git a/evtc/src/strike.rs b/evtc/src/strike.rs index a0dcc07cd6..3487818d8e 100644 --- a/evtc/src/strike.rs +++ b/evtc/src/strike.rs @@ -118,13 +118,16 @@ pub enum Strike { /// Not a damage strike. Breakbar = 10, - /// On-activation event. + /// On-skill-activation event. /// /// Not a damage strike. - /// - /// *Arc: Source hit target if damaging buff.* Activation = 11, + /// Skill crowd controlled the target. + /// + /// Not a damage strike. + CrowdControl = 12, + /// Unknown. #[num_enum(catch_all)] Unknown(u8), diff --git a/evtc/src/weapon.rs b/evtc/src/weapon.rs index 131710c02d..0191f431d2 100644 --- a/evtc/src/weapon.rs +++ b/evtc/src/weapon.rs @@ -2,6 +2,7 @@ use crate::{extract::Extract, AgentId, Event, StateChange, TryExtract}; use num_enum::{FromPrimitive, IntoPrimitive}; +use std::mem; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -21,6 +22,9 @@ pub struct WeaponSwapEvent { /// New weapon set. pub weapon_set: WeaponSet, + + /// Previous weapon set. + pub prev_weapon_set: WeaponSet, } impl Extract for WeaponSwapEvent { @@ -30,6 +34,7 @@ impl Extract for WeaponSwapEvent { time: event.time, agent: AgentId::from_src(event), weapon_set: event.dst_agent.into(), + prev_weapon_set: u64::from(unsafe { mem::transmute::(event.value) }).into(), } } }