Skip to main content

wowlab_engine_combat/handler/
spec_handler.rs

1use wowlab_engine_ports::{
2    AuraEventRef, EncounterDespawnKind, EncounterTerminationReason, EnemyDespawnOutcome, Event,
3    PullScope, SimContext, SpecAction, SpecHandler, SpecRuntimeError,
4};
5use wowlab_engine_telemetry::{EncounterTelemetryEvent, EncounterTelemetryEventKind};
6use wowlab_types::{
7    combat::DamageSchool,
8    constants::DEFAULT_GCD_MS,
9    sim::{
10        ActorId, EnemyIdx, GroupId, PositionedActorRef, SimTime, SpatialTransform, SpellIdx, WaveId,
11    },
12};
13
14use super::{
15    CombatHandler,
16    introspection::build_introspection,
17    reset::{reset_combat, set_precombat_aura_stacks},
18};
19use crate::{
20    builder::buffer_init::project_encounter,
21    context::{CombatCtx, CombatCtxRequest, HookCtxRequest},
22    state::{GuardianHandle, LocalAuraIdx, LocalSpellIdx},
23    systems::{
24        apply_aura, apply_auto_attack_action_lifecycle, check_recharge, drain_deferred_work,
25        earliest_auto_attack_time, initialize_pet_actions, phase_auto_attacks_for,
26        process_channel_tick_for, process_pet_action, process_single_aura_tick_for, set_gcd,
27        sync_resource,
28    },
29};
30
31mod auras;
32mod casting;
33mod combat_events;
34mod encounter;
35mod introspect;
36mod lifecycle;
37mod timers;
38
39const FIRST_ENEMY_SWING_DIVISOR: u32 = 2;
40
41impl SpecHandler for CombatHandler {
42    fn on_sim_start(&mut self, ctx: &mut SimContext) {
43        self.on_sim_start_impl(ctx);
44    }
45    fn on_player_ready(&mut self, ctx: &mut SimContext) -> Option<SpecAction> {
46        self.on_player_ready_impl(ctx)
47    }
48    fn on_cast_complete(&mut self, event: Event, ctx: &mut SimContext) {
49        self.on_cast_complete_impl(event, ctx);
50    }
51    fn on_triggered_spell(
52        &mut self,
53        spell_id: SpellIdx,
54        source: ActorId,
55        target: ActorId,
56        ctx: &mut SimContext,
57    ) {
58        self.on_triggered_spell_impl(spell_id, source, target, ctx);
59    }
60    fn on_spell_impact(&mut self, impact_id: u32, ctx: &mut SimContext) {
61        self.on_spell_impact_impl(impact_id, ctx);
62    }
63    fn on_spell_launch(&mut self, impact_id: u32, ctx: &mut SimContext) {
64        self.on_spell_launch_impl(impact_id, ctx);
65    }
66    fn on_guardian_action(
67        &mut self,
68        guardian_id: u32,
69        guardian_generation: u32,
70        ability_index: usize,
71        ctx: &mut SimContext,
72    ) {
73        self.on_guardian_action_impl(guardian_id, guardian_generation, ability_index, ctx);
74    }
75    fn on_guardian_expire(
76        &mut self,
77        guardian_id: u32,
78        guardian_generation: u32,
79        ctx: &mut SimContext,
80    ) {
81        self.on_guardian_expire_impl(guardian_id, guardian_generation, ctx);
82    }
83    fn on_pet_action(&mut self, auto_attack_index: usize, ctx: &mut SimContext) {
84        self.on_pet_action_impl(auto_attack_index, ctx);
85    }
86    fn on_aura_tick(&mut self, event: AuraEventRef, ctx: &mut SimContext) {
87        self.on_aura_tick_impl(event, ctx);
88    }
89    fn on_channel_tick(
90        &mut self,
91        spell_id: SpellIdx,
92        source: ActorId,
93        target: EnemyIdx,
94        generation: u64,
95        ctx: &mut SimContext,
96    ) {
97        self.on_channel_tick_impl(spell_id, source, target, generation, ctx);
98    }
99    fn on_aura_expire(&mut self, event: AuraEventRef, ctx: &mut SimContext) {
100        self.on_aura_expire_impl(event, ctx);
101    }
102    fn on_proc_heartbeat(&mut self, ctx: &mut SimContext) {
103        self.on_proc_heartbeat_impl(ctx);
104    }
105    fn on_death(
106        &mut self,
107        target: EnemyIdx,
108        scope: wowlab_engine_ports::DeathEventScope,
109        ctx: &mut SimContext,
110    ) {
111        self.on_death_impl(target, scope, ctx);
112    }
113    fn on_despawn(&mut self, target: EnemyIdx, ctx: &mut SimContext) -> EnemyDespawnOutcome {
114        self.on_despawn_impl(target, ctx)
115    }
116    fn on_encounter_despawn(
117        &mut self,
118        scope: PullScope,
119        target: EnemyIdx,
120        kind: EncounterDespawnKind,
121        ctx: &mut SimContext,
122    ) {
123        self.on_encounter_despawn_impl(scope, target, kind, ctx);
124    }
125    fn on_group_complete(&mut self, scope: PullScope, group: GroupId, ctx: &mut SimContext) {
126        self.on_group_complete_impl(scope, group, ctx);
127    }
128    fn on_pull_complete(&mut self, scope: PullScope, finalize: bool, ctx: &mut SimContext) {
129        self.on_pull_complete_impl(scope, finalize, ctx);
130    }
131    fn on_encounter_terminate(&mut self, reason: EncounterTerminationReason, ctx: &mut SimContext) {
132        self.on_encounter_terminate_impl(reason, ctx);
133    }
134    fn on_wave_activate(&mut self, scope: PullScope, wave: WaveId, ctx: &mut SimContext) {
135        self.on_wave_activate_impl(scope, wave, ctx);
136    }
137    fn on_enemy_activate(&mut self, scope: PullScope, target: EnemyIdx, ctx: &mut SimContext) {
138        self.on_enemy_activate_impl(scope, target, ctx);
139    }
140    fn on_retarget(&mut self, scope: PullScope, ctx: &mut SimContext) {
141        self.on_retarget_impl(scope, ctx);
142    }
143    fn on_encounter_move(
144        &mut self,
145        scope: PullScope,
146        actor: PositionedActorRef,
147        transform: SpatialTransform,
148        ctx: &mut SimContext,
149    ) {
150        self.on_encounter_move_impl(scope, actor, transform, ctx);
151    }
152    fn on_event_queue_empty(&mut self, ctx: &mut SimContext) {
153        self.on_event_queue_empty_impl(ctx);
154    }
155    fn encounter_termination_time(&self) -> Option<SimTime> {
156        self.encounter_termination_time_impl()
157    }
158    fn on_hook_timer(
159        &mut self,
160        timer_id: u32,
161        source: ActorId,
162        target: Option<EnemyIdx>,
163        ctx: &mut SimContext,
164    ) {
165        self.on_hook_timer_impl(timer_id, source, target, ctx);
166    }
167    fn on_auto_attack(&mut self, source: ActorId, target: EnemyIdx, ctx: &mut SimContext) {
168        self.on_auto_attack_impl(source, target, ctx);
169    }
170    fn on_enemy_auto_attack(&mut self, source: EnemyIdx, target: ActorId, ctx: &mut SimContext) {
171        self.on_enemy_auto_attack_impl(source, target, ctx);
172    }
173    fn on_actor_movement(
174        &mut self,
175        actor: ActorId,
176        moving: bool,
177        generation: u64,
178        ctx: &mut SimContext,
179    ) {
180        self.on_actor_movement_impl(actor, moving, generation, ctx);
181    }
182    fn on_raid_event(&mut self, index: usize, ctx: &mut SimContext) {
183        self.on_raid_event_impl(index, ctx);
184    }
185    fn on_raid_add_despawn(
186        &mut self,
187        target: EnemyIdx,
188        activation_generation: u64,
189        encounter_generation: wowlab_engine_ports::PullLifecycleGeneration,
190        ctx: &mut SimContext,
191    ) {
192        self.on_raid_add_despawn_impl(target, activation_generation, encounter_generation, ctx);
193    }
194    fn on_external_buff(&mut self, index: usize, ctx: &mut SimContext) {
195        self.on_external_buff_impl(index, ctx);
196    }
197    fn on_cooldown_ready(&mut self, cooldown_key: SpellIdx, ctx: &mut SimContext) {
198        self.on_cooldown_ready_impl(cooldown_key, ctx);
199    }
200    fn flush_scheduled(&mut self, push: &mut dyn FnMut(Event)) {
201        self.flush_scheduled_impl(push);
202    }
203    fn take_runtime_error(&mut self) -> Option<SpecRuntimeError> {
204        self.take_runtime_error_impl()
205    }
206    fn reset(&mut self) {
207        self.reset_impl();
208    }
209    fn total_damage(&self) -> f64 {
210        self.total_damage_impl()
211    }
212    fn cast_time_ms(&self, spell_id: SpellIdx, empower_rank: u8) -> u32 {
213        self.cast_time_ms_impl(spell_id, empower_rank)
214    }
215    fn introspect(&self) -> wowlab_types::game::SpecIntrospection {
216        self.introspect_impl()
217    }
218    fn attach_decision_trace(
219        &mut self,
220        sink: std::sync::Arc<dyn wowlab_engine_ports::DecisionTraceSink>,
221    ) {
222        self.attach_decision_trace_impl(sink);
223    }
224    fn paperdoll(&self) -> Option<wowlab_engine_ports::Paperdoll> {
225        Some(self.paperdoll_impl())
226    }
227}
228
229#[cfg(test)]
230mod tests;