wowlab_engine_content/hooks/arcane_mage/
handler.rs1use wowlab_engine_combat::GuardianOps as _;
2use wowlab_engine_combat::{CombatHandler, GuardianAbility, GuardianSpec};
3use wowlab_engine_ports::{HandlerParams, SimContext};
4
5use wowlab_types::constants::MS_PER_SECOND;
6
7use crate::generated::specs::arcane_mage::REPORTED_SPELL;
8
9use super::{
10 config::{apply_arcane_max_mana, register_arcane, ArcaneConfig},
11 hooks::{arcane_familiar_action, ARCANE_FAMILIAR_PERIOD_MS},
12};
13
14#[derive(Debug)]
15pub(crate) struct ArcaneHandler {
16 inner: CombatHandler,
17 fight_duration_ms: u32,
18}
19
20impl ArcaneHandler {
21 pub(crate) fn new(
22 parts: crate::composition::ContentHandlerParts,
23 params: &HandlerParams<'_>,
24 ) -> Result<Self, wowlab_engine_ports::EngineError> {
25 let mut parts = parts;
26 let built = parts.draft_mut();
27
28 apply_arcane_max_mana(built, params);
29 register_arcane(built, params);
30
31 let built = parts.finalize()?;
32
33 Ok(Self {
34 inner: CombatHandler::from_built(built),
35 fight_duration_ms: wowlab_types::numeric::f64_to_u32_saturating_trunc(params.fight_duration_secs * MS_PER_SECOND),
36 })
37 }
38}
39
40crate::hooks::define_spec_handler! {
41impl ArcaneHandler(self, inner) {
42 fn on_sim_start(&mut self, ctx: &mut SimContext) {
43 self.inner.on_sim_start(ctx);
44 if self
45 .inner
46 .program()
47 .spec_config::<ArcaneConfig>()
48 .is_some_and(|cfg| cfg.arcane_familiar)
49 {
50 self.inner
51 .with_hook_ctx(&mut *ctx.telemetry, wowlab_engine_combat::HookCtxRequest::targetless(wowlab_types::sim::SimTime::ZERO), |precombat| {
52 let _ = precombat.summon_guardian(
53 GuardianSpec::new_inheriting_owner(
54 REPORTED_SPELL::ARCANE_FAMILIAR,
55 self.fight_duration_ms.saturating_add(1),
56 )
57 .ability(
58 GuardianAbility::new(
59 ARCANE_FAMILIAR_PERIOD_MS,
60 arcane_familiar_action,
61 )
62 .first_action_delay(ARCANE_FAMILIAR_PERIOD_MS)
63 .hasted_actions(),
64 ),
65 );
66 });
67 }
68 }
69
70}
71}