wowlab_engine_domain/rotation/context/
spell_usable.rs1use wowlab_types::sim::{AuraProjectionKey, FastMap, IntMap, SpellIdx};
2
3use super::schema::SpellUsableOffsets;
4use crate::rotation::{
5 buffer::{BufferOffsets, ByteOffset},
6 resolver::{SpecResolver, SpellAuraPolicyKind},
7};
8
9pub(super) fn build_cooldown_bypass_offsets(
10 buffer_offsets: &BufferOffsets,
11 resolver: Option<&SpecResolver>,
12) -> FastMap<usize, usize> {
13 resolver
14 .into_iter()
15 .flat_map(SpecResolver::spell_aura_policies)
16 .filter_map(|policy| {
17 if policy.kind != SpellAuraPolicyKind::CooldownBypass {
18 return None;
19 }
20
21 let cooldown_offset = buffer_offsets
22 .slots
23 .cooldowns
24 .get(&policy.spell)
25 .map(ByteOffset::as_usize)?;
26 let aura_offset = buffer_offsets
27 .slots
28 .aura_projections
29 .get(&AuraProjectionKey::new(policy.aura, policy.on))
30 .map(ByteOffset::as_usize)?;
31
32 Some((cooldown_offset, aura_offset))
33 })
34 .collect()
35}
36
37pub(super) fn build_spell_usable_offsets(
38 buffer_offsets: &BufferOffsets,
39 resolver: Option<&SpecResolver>,
40 pending: &[(SpellIdx, usize)],
41) -> FastMap<usize, SpellUsableOffsets> {
42 let primary_resource_base = resolver
43 .and_then(SpecResolver::primary_resource_type)
44 .and_then(|resource| {
45 buffer_offsets
46 .slots
47 .resources
48 .get(&resource)
49 .map(ByteOffset::as_usize)
50 });
51 let secondary_resource_base = resolver
52 .and_then(SpecResolver::secondary_resource_type)
53 .and_then(|resource| {
54 buffer_offsets
55 .slots
56 .resources
57 .get(&resource)
58 .map(ByteOffset::as_usize)
59 });
60
61 let mut gating_by_spell: IntMap<SpellIdx, (usize, u8)> = IntMap::default();
62 let mut cost_bypass_by_spell: IntMap<SpellIdx, usize> = IntMap::default();
63 let mut cooldown_bypass_by_spell: IntMap<SpellIdx, usize> = IntMap::default();
64 let mut override_by_spell: IntMap<SpellIdx, (SpellIdx, usize, bool)> = IntMap::default();
65
66 for policy in resolver
67 .into_iter()
68 .flat_map(SpecResolver::spell_aura_policies)
69 {
70 let Some(aura_offset) = buffer_offsets
71 .slots
72 .aura_projections
73 .get(&AuraProjectionKey::new(policy.aura, policy.on))
74 .map(ByteOffset::as_usize)
75 else {
76 continue;
77 };
78
79 match policy.kind {
80 SpellAuraPolicyKind::Gate { min_stacks } => {
81 gating_by_spell.insert(policy.spell, (aura_offset, min_stacks));
82 }
83 SpellAuraPolicyKind::CostBypass => {
84 cost_bypass_by_spell.insert(policy.spell, aura_offset);
85 }
86 SpellAuraPolicyKind::CooldownBypass => {
87 cooldown_bypass_by_spell.insert(policy.spell, aura_offset);
88 }
89 SpellAuraPolicyKind::Override {
90 replacement,
91 shares_base_cooldown,
92 } => {
93 override_by_spell.insert(
94 policy.spell,
95 (replacement, aura_offset, shares_base_cooldown),
96 );
97 }
98 }
99 }
100
101 let mut offsets = FastMap::default();
102
103 for (spell_idx, field_offset) in pending {
104 let (gating_aura_offset, gating_aura_min_stacks) = gating_by_spell
105 .get(spell_idx)
106 .map_or((None, 0), |&(offset, stacks)| (Some(offset), stacks));
107 let (override_spell_offset, override_cooldown_base, override_aura_offset) =
108 match override_by_spell.get(spell_idx) {
109 Some((replacement, aura_offset, shares_base_cooldown)) => (
110 buffer_offsets
111 .slots
112 .spells
113 .get(replacement)
114 .map(ByteOffset::as_usize),
115 buffer_offsets
116 .slots
117 .cooldowns
118 .get(if *shares_base_cooldown {
119 spell_idx
120 } else {
121 replacement
122 })
123 .map(ByteOffset::as_usize),
124 Some(*aura_offset),
125 ),
126 None => (None, None, None),
127 };
128
129 offsets.insert(
130 *field_offset,
131 SpellUsableOffsets {
132 resource_current: primary_resource_base,
133 secondary_resource_current: secondary_resource_base,
134 cooldown_base: buffer_offsets
135 .slots
136 .cooldowns
137 .get(spell_idx)
138 .map(ByteOffset::as_usize),
139 gating_aura_offset,
140 gating_aura_min_stacks,
141 cost_bypass_aura_offset: cost_bypass_by_spell.get(spell_idx).copied(),
142 cooldown_bypass_aura_offset: cooldown_bypass_by_spell.get(spell_idx).copied(),
143 override_spell_offset,
144 override_cooldown_base,
145 override_aura_offset,
146 },
147 );
148 }
149
150 offsets
151}