evtc\content/
mod.rs

1//! Bindings & utilities for GUIDs appearing in events.
2
3mod guid;
4
5#[cfg(feature = "serde")]
6pub mod serde_guid;
7
8use crate::{
9    extract::{transmute_field, Extract},
10    Event, StateChange, TryExtract,
11};
12use num_enum::{IntoPrimitive, TryFromPrimitive};
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17#[cfg(feature = "strum")]
18use strum::{Display, EnumCount, EnumIter, IntoStaticStr, VariantNames};
19
20pub use self::guid::*;
21
22/// Content information.
23///
24/// The contained GUID is interpreted as a Windows [`GUID`].
25/// See https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid for more information.
26///
27/// Some GW2 community projects misinterpret the memory layout of the GUID as bytes rather than a Windows [`GUID`].
28/// When comparing or interfacing with such projects, you can use [`GuidExt::misinterpret`] on the [`GUID`].
29#[derive(Debug, Clone)]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31pub struct ContentInfo {
32    /// Id of the content (volatile, depends on game build).
33    pub content_id: u32,
34
35    /// Persistent content GUID.
36    #[cfg_attr(feature = "serde", serde(with = "serde_guid"))]
37    pub guid: GUID,
38
39    /// Content type.
40    pub content_type: ContentType,
41}
42
43impl ContentInfo {
44    /// Formats the contained GUID as [`String`].
45    #[inline]
46    pub fn guid_string(&self) -> String {
47        self.guid.format_simple()
48    }
49
50    /// Whether the content is an effect.
51    #[inline]
52    pub fn is_effect(&self) -> bool {
53        matches!(self.content_type, ContentType::Effect { .. })
54    }
55
56    /// Whether the content is a marker.
57    #[inline]
58    pub fn is_marker(&self) -> bool {
59        matches!(self.content_type, ContentType::Marker)
60    }
61
62    /// Whether the content is a skill.
63    #[inline]
64    pub fn is_skill(&self) -> bool {
65        matches!(self.content_type, ContentType::Skill)
66    }
67
68    /// Whether the content is a species.
69    #[inline]
70    pub fn is_species(&self) -> bool {
71        matches!(self.content_type, ContentType::Species)
72    }
73}
74
75impl Extract for ContentInfo {
76    #[inline]
77    unsafe fn extract(event: &Event) -> Self {
78        Self {
79            content_id: event.skill_id,
80            guid: transmute_field!(event.src_agent as GUID),
81            content_type: event.extract(),
82        }
83    }
84}
85
86impl TryExtract for ContentInfo {
87    #[inline]
88    fn can_extract(event: &Event) -> bool {
89        event.get_statechange() == StateChange::IdToGUID
90    }
91}
92
93/// Content type for [`ContentInfo`].
94#[derive(Debug, Clone)]
95#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
96pub enum ContentType {
97    /// Effect.
98    Effect {
99        /// Effect type.
100        effect_type: u16,
101
102        /// Default effect duration, if available.
103        default_duration: f32,
104    },
105
106    /// Marker.
107    Marker,
108
109    /// Skill.
110    ///
111    /// See skill & buff info events for extra information.
112    Skill,
113
114    /// Species (only characters, not gadgets).
115    Species,
116
117    /// Unknown content type.
118    Unknown(u32),
119}
120
121impl Extract for ContentType {
122    #[inline]
123    unsafe fn extract(event: &Event) -> Self {
124        match event.overstack_value.try_into() {
125            Ok(ContentLocal::Effect) => Self::Effect {
126                effect_type: event.src_instance_id,
127                default_duration: transmute_field!(event.buff_dmg as f32),
128            },
129            Ok(ContentLocal::Marker) => Self::Marker,
130            Ok(ContentLocal::Skill) => Self::Skill,
131            Ok(ContentLocal::Species) => Self::Species,
132            Err(err) => Self::Unknown(err.number),
133        }
134    }
135}
136
137impl TryExtract for ContentType {
138    #[inline]
139    fn can_extract(event: &Event) -> bool {
140        event.get_statechange() == StateChange::IdToGUID
141    }
142}
143
144/// Content local for [`StateChange::IdToGUID`] events.
145#[derive(
146    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, IntoPrimitive, TryFromPrimitive,
147)]
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
149#[cfg_attr(
150    feature = "strum",
151    derive(Display, EnumCount, EnumIter, IntoStaticStr, VariantNames)
152)]
153#[repr(u32)]
154pub enum ContentLocal {
155    /// Content is an effect.
156    ///
157    /// `src_instance_id` contains the effect type.
158    /// `buff_dmg` contains the default duration as [`f32`], if available.
159    Effect = 0,
160
161    /// Content is a marker.
162    Marker = 1,
163
164    /// Content is a skill.
165    ///
166    /// See skill & buff info events for extra information.
167    Skill = 2,
168
169    /// Content is a species (only characters, not gadgets).
170    Species = 3,
171}