wowlab_engine_combat/handler/can_cast/
reject.rs1use wowlab_types::sim::SimTime;
4
5#[derive(Clone, Copy, Debug)]
7pub(crate) enum CastReject {
8 NoTarget,
9 UnknownSpell,
10 Disabled,
11 PrimaryResource {
12 have: f64,
13 cost: f64,
14 },
15 SecondaryResource {
16 have: f64,
17 cost: f64,
18 },
19 Health {
20 have: f64,
21 cost: f64,
22 },
23 NoCharges,
24 Cooldown {
25 ready_at: f64,
26 now: f64,
27 },
28 CooldownCategory {
29 category: wowlab_types::data::CooldownCategoryId,
30 ready_at: SimTime,
31 now: SimTime,
32 },
33 ChargeCategory {
34 category: wowlab_types::data::CooldownCategoryId,
35 ready_at: SimTime,
36 now: SimTime,
37 },
38 StartRecoveryCategory {
39 category: wowlab_types::data::CooldownCategoryId,
40 ready_at: SimTime,
41 now: SimTime,
42 },
43 ExecuteRange {
44 target_health_pct: f64,
45 threshold_pct: f64,
46 },
47 GatingAuraInactive {
48 aura_id: u32,
49 min_stacks: i32,
50 },
51 RequiresStealth,
52 RequiresBehindTarget,
53 RequiresUnshifted {
54 form: i64,
55 },
56 ShapeshiftRequired {
57 form: i64,
58 required_mask: u64,
59 },
60 ShapeshiftExcluded {
61 form: i64,
62 },
63 RequiresMainHand,
64 RequiresOffHand,
65 EquippedItemRequirement,
66 Moving,
67 SchoolLocked {
68 ready_at: SimTime,
69 },
70 CrowdControlled {
71 ready_at: Option<SimTime>,
72 },
73 AuraState,
74 OutOfRange,
75}
76
77impl std::fmt::Display for CastReject {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 match *self {
81 Self::NoTarget => write!(f, "no target"),
82 Self::UnknownSpell => write!(f, "spell not in resolved set"),
83 Self::Disabled => write!(f, "disabled (masked or talent-gated off)"),
84 Self::PrimaryResource { have, cost } => {
85 write!(f, "primary resource {have:.0}/{cost:.0}")
86 }
87 Self::SecondaryResource { have, cost } => {
88 write!(f, "secondary resource {have:.0}/{cost:.0}")
89 }
90 Self::Health { have, cost } => write!(f, "health {have:.0}/{cost:.0}"),
91 Self::NoCharges => write!(f, "no charges available"),
92 Self::Cooldown { ready_at, now } => {
93 write!(f, "on cooldown ({:.2}s remaining)", ready_at - now)
94 }
95 Self::CooldownCategory {
96 category,
97 ready_at,
98 now,
99 } => write!(
100 f,
101 "cooldown category {} locked ({:.2}s remaining)",
102 category.raw(),
103 ready_at.saturating_sub(now).as_secs_f64()
104 ),
105 Self::ChargeCategory {
106 category,
107 ready_at,
108 now,
109 } => write!(
110 f,
111 "charge category {} locked ({:.2}s remaining)",
112 category.raw(),
113 ready_at.saturating_sub(now).as_secs_f64()
114 ),
115 Self::StartRecoveryCategory {
116 category,
117 ready_at,
118 now,
119 } => write!(
120 f,
121 "start-recovery category {} locked ({:.2}s remaining)",
122 category.raw(),
123 ready_at.saturating_sub(now).as_secs_f64()
124 ),
125 Self::ExecuteRange {
126 target_health_pct,
127 threshold_pct,
128 } => write!(
129 f,
130 "target at {target_health_pct:.0}%, above execute threshold {threshold_pct:.0}%"
131 ),
132 Self::GatingAuraInactive {
133 aura_id,
134 min_stacks,
135 } => write!(
136 f,
137 "gating aura {aura_id} inactive (need {min_stacks} stacks)"
138 ),
139 Self::RequiresStealth => write!(f, "requires stealth (stealthed flag = 0)"),
140 Self::RequiresBehindTarget => write!(f, "requires position behind target"),
141 Self::RequiresUnshifted { form } => {
142 write!(f, "requires no shapeshift (current form = {form})")
143 }
144 Self::ShapeshiftRequired {
145 form,
146 required_mask,
147 } => write!(
148 f,
149 "requires shapeshift form mask {required_mask:#x} (current form = {form})"
150 ),
151 Self::ShapeshiftExcluded { form } => {
152 write!(f, "excluded in shapeshift form {form}")
153 }
154 Self::RequiresMainHand => write!(f, "requires a main-hand weapon"),
155 Self::RequiresOffHand => write!(f, "requires an off-hand weapon"),
156 Self::EquippedItemRequirement => {
157 write!(f, "equipped weapon does not satisfy spell requirements")
158 }
159 Self::Moving => write!(f, "cannot cast while moving"),
160 Self::SchoolLocked { ready_at } => write!(
161 f,
162 "spell school is locked ({:.2}s remaining)",
163 ready_at.as_secs_f64()
164 ),
165 Self::CrowdControlled { ready_at } => match ready_at {
166 Some(ready_at) => write!(
167 f,
168 "actor is crowd controlled until {:.2}s",
169 ready_at.as_secs_f64()
170 ),
171 None => write!(f, "actor is permanently crowd controlled"),
172 },
173 Self::AuraState => write!(f, "aura-state requirement is not satisfied"),
174 Self::OutOfRange => write!(f, "target out of range"),
175 }
176 }
177}