1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Bindings & utilities for any form of strikes (direct damage).

use crate::{
    event::{impl_common, CommonEvent},
    extract::Extract,
    Event, EventCategory, TryExtract,
};
use num_enum::{FromPrimitive, IntoPrimitive};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "strum")]
use strum::{Display, EnumCount, EnumIter, IntoStaticStr, VariantNames};

/// Strike (direct damage) event.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StrikeEvent {
    /// Common combat event information.
    #[cfg_attr(feature = "serde", serde(flatten))]
    pub common: CommonEvent,

    /// Kind of strike.
    pub strike: Strike,

    /// Total damage inflicted.
    pub total_damage: i32,

    /// Damage inflicted to shields (barrier).
    pub shield_damage: u32,

    /// Whether target is currently downed.
    pub target_downed: bool,
}

impl_common!(StrikeEvent);

impl Extract for StrikeEvent {
    #[inline]
    unsafe fn extract(event: &Event) -> Self {
        Self {
            common: event.into(),
            strike: event.result.into(),
            total_damage: event.value,
            shield_damage: event.overstack_value,
            target_downed: event.is_offcycle == 1,
        }
    }
}

impl TryExtract for StrikeEvent {
    #[inline]
    fn can_extract(event: &Event) -> bool {
        event.categorize() == EventCategory::Strike
    }
}

/// Strike types.
///
/// *Arc calls this "combat result".*
#[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 Strike {
    /// Normal damage strike.
    ///
    /// No crit, no glance.
    Normal = 0,

    /// Strike was critical.
    Crit = 1,

    /// Strike was glancing.
    Glance = 2,

    /// Strike was blocked.
    ///
    /// Due to Aegis, Chrono Shield 4 etc.
    Block = 3,

    /// Strike was evaded.
    ///
    /// Due to dodge, Mesmer Sword 2 etc.
    Evade = 4,

    /// Strike interrupted something.
    Interrupt = 5,

    /// Strike was absorbed.
    ///
    /// Usually due to an invulnerability like Guardian Renewed Focus.
    Absorb = 6,

    /// Strike missed.
    ///
    /// Due to blind etc.
    Blind = 7,

    /// Skill killed the target.
    ///
    /// Not a damage strike.
    KillingBlow = 8,

    /// Skill downed the target.
    ///
    /// Not a damage strike.
    Downed = 9,

    /// Skill dealt breakbar damage.
    ///
    /// Not a damage strike.
    Breakbar = 10,

    /// On-activation event.
    ///
    /// Not a damage strike.
    ///
    /// *Arc: Source hit target if damaging buff.*
    Activation = 11,

    /// Unknown.
    #[num_enum(catch_all)]
    Unknown(u8),
}

impl Strike {
    /// Whether the strike dealt health damage to the target.
    #[inline]
    pub const fn dealt_damage(&self) -> bool {
        matches!(self, Self::Normal | Self::Crit | Self::Glance)
    }
}