wowlab_engine_combat/context/
combat_ctx.rs1#[cfg(test)]
4use std::cell::Cell;
5use std::cell::OnceCell;
6
7use wowlab_engine_domain::rotation::DenseBuffer;
8use wowlab_engine_telemetry::TelemetrySink;
9use wowlab_types::sim::{ActorId, EnemyIdx, SimTime};
10
11use super::{HookCtx, HookCtxRequest, HookCtxServices};
12use crate::{
13 state::{BuffTotals, CombatState},
14 systems,
15};
16
17#[derive(Debug)]
19#[must_use]
20pub struct CombatView<'a> {
21 pub state: &'a CombatState,
22 pub buf: &'a DenseBuffer,
23 buff_totals: OnceCell<BuffTotals>,
24 #[cfg(test)]
25 buff_traversals: Cell<u32>,
26}
27
28impl<'a> CombatView<'a> {
29 pub fn new(state: &'a CombatState, buf: &'a DenseBuffer) -> Self {
30 Self {
31 state,
32 buf,
33 buff_totals: OnceCell::new(),
34 #[cfg(test)]
35 buff_traversals: Cell::new(0),
36 }
37 }
38
39 pub const fn for_actor(&self, actor: ActorId) -> ActorView<'a> {
40 ActorView {
41 state: self.state,
42 buf: self.buf,
43 actor,
44 }
45 }
46
47 pub const fn for_hook(&self, now: SimTime, target: Option<EnemyIdx>) -> HookView<'a> {
48 HookView {
49 state: self.state,
50 buf: self.buf,
51 target,
52 now,
53 }
54 }
55
56 pub(crate) fn buff_totals(&self) -> &BuffTotals {
57 self.buff_totals.get_or_init(|| {
58 #[cfg(test)]
59 self.buff_traversals
60 .set(self.buff_traversals.get().saturating_add(1));
61
62 systems::accumulate_buffs(self.state, self.buf, None)
63 })
64 }
65
66 #[cfg(test)]
67 pub(crate) fn buff_traversals(&self) -> u32 {
68 self.buff_traversals.get()
69 }
70}
71
72#[derive(Clone, Copy, Debug)]
74#[must_use]
75pub struct ActorView<'a> {
76 pub state: &'a CombatState,
77 pub buf: &'a DenseBuffer,
78 pub actor: ActorId,
79}
80
81impl<'a> ActorView<'a> {
82 pub const fn new(state: &'a CombatState, buf: &'a DenseBuffer, actor: ActorId) -> Self {
83 Self { state, buf, actor }
84 }
85
86 pub fn combat(self) -> CombatView<'a> {
87 CombatView::new(self.state, self.buf)
88 }
89}
90
91#[derive(Clone, Copy, Debug)]
93#[must_use]
94pub struct HookView<'a> {
95 pub state: &'a CombatState,
96 pub buf: &'a DenseBuffer,
97 pub target: Option<EnemyIdx>,
98 pub now: SimTime,
99}
100
101impl<'a> HookView<'a> {
102 pub fn combat(self) -> CombatView<'a> {
103 CombatView::new(self.state, self.buf)
104 }
105
106 pub fn for_actor(self, actor: ActorId) -> ActorView<'a> {
107 self.combat().for_actor(actor)
108 }
109
110 #[must_use]
111 pub fn last_secondary_spent(self) -> f64 {
112 self.state.runtime.resources.last_secondary_spent
113 }
114}
115
116#[must_use]
118pub struct CombatCtx<'a> {
119 pub state: &'a mut CombatState,
120 pub buf: &'a mut DenseBuffer,
121 pub sink: &'a mut TelemetrySink,
122 pub now: SimTime,
123 pub rng: &'a mut dyn FnMut() -> f64,
124 pub source: ActorId,
125 pub target: EnemyIdx,
126}
127
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
130#[must_use]
131pub struct CombatCtxRequest {
132 pub now: SimTime,
133 pub source: ActorId,
134 pub target: EnemyIdx,
135}
136
137impl CombatCtxRequest {
138 pub const fn new(now: SimTime, source: ActorId, target: EnemyIdx) -> Self {
139 Self {
140 now,
141 source,
142 target,
143 }
144 }
145}
146
147impl CombatCtx<'_> {
148 pub fn for_optional_target<'ctx, R>(
152 hook: &'ctx mut HookCtx<'_>,
153 target: Option<EnemyIdx>,
154 operation: impl FnOnce(&mut CombatCtx<'ctx>) -> R,
155 ) -> Option<R> {
156 let target = target?;
157 let mut ctx = CombatCtx {
158 state: &mut *hook.state,
159 buf: &mut *hook.buf,
160 sink: &mut *hook.sink,
161 now: hook.now,
162 rng: &mut *hook.rng,
163 source: hook.source,
164 target,
165 };
166
167 Some(operation(&mut ctx))
168 }
169
170 pub fn view(&self) -> CombatView<'_> {
171 CombatView::new(self.state, self.buf)
172 }
173
174 pub(crate) fn hook_ctx(&mut self) -> HookCtx<'_> {
175 self.hook_ctx_with_source(self.source)
176 }
177
178 pub(crate) fn hook_ctx_with_source(&mut self, source: ActorId) -> HookCtx<'_> {
179 HookCtx::new(
180 HookCtxServices {
181 state: self.state,
182 buf: self.buf,
183 sink: self.sink,
184 rng: self.rng,
185 },
186 HookCtxRequest::for_target(self.now, self.target).with_source(source),
187 )
188 }
189}
190
191impl std::fmt::Debug for CombatCtx<'_> {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 f.debug_struct("CombatCtx")
194 .field("now", &self.now)
195 .field("source", &self.source)
196 .field("target", &self.target)
197 .finish_non_exhaustive()
198 }
199}