Skip to main content

wowlab_engine_combat/handler/can_cast/
reject.rs

1//! Typed cast-rejection reasons.
2
3use wowlab_types::sim::SimTime;
4
5/// Why a spell cannot be cast this instant.
6#[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    // #t(fn: rust_cyclomatic_complexity) flat 1:1 reject-reason formatting arms
79    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}