Skip to main content

wowlab_engine_combat/handler/spec_handler/
encounter.rs

1use super::{
2    ActorId, CombatHandler, EncounterDespawnKind, EncounterTelemetryEventKind,
3    EncounterTerminationReason, EnemyDespawnOutcome, EnemyIdx, Event, FIRST_ENEMY_SWING_DIVISOR,
4    GroupId, HookCtxRequest, PositionedActorRef, PullScope, SimContext, SimTime, SpatialTransform,
5    WaveId, project_encounter,
6};
7
8impl CombatHandler {
9    pub(super) fn on_death_impl(
10        &mut self,
11        target: EnemyIdx,
12        scope: wowlab_engine_ports::DeathEventScope,
13        ctx: &mut SimContext,
14    ) {
15        let now = ctx.state.current_time;
16
17        self.handle_encounter_death(target, scope, now, &mut *ctx.telemetry);
18    }
19
20    pub(super) fn on_despawn_impl(
21        &mut self,
22        target: EnemyIdx,
23        ctx: &mut SimContext,
24    ) -> EnemyDespawnOutcome {
25        let now = ctx.state.current_time;
26        let outcome = self.state.transition_enemy_despawn(target, now);
27
28        if outcome == EnemyDespawnOutcome::Despawned {
29            self.emit_enemy_lifecycle(
30                &mut *ctx.telemetry,
31                EncounterTelemetryEventKind::Despawn,
32                target,
33                now,
34                self.state
35                    .enemy(target)
36                    .map(wowlab_engine_domain::encounter::EnemyState::current_transform),
37            );
38            self.cleanup_inactive_enemy(target, now, &mut *ctx.telemetry);
39        }
40
41        outcome
42    }
43
44    pub(super) fn on_encounter_despawn_impl(
45        &mut self,
46        scope: PullScope,
47        target: EnemyIdx,
48        kind: EncounterDespawnKind,
49        ctx: &mut SimContext,
50    ) {
51        let PullScope { pull, epoch } = scope;
52
53        self.handle_encounter_despawn(pull, epoch, target, kind, ctx);
54    }
55
56    pub(super) fn on_group_complete_impl(
57        &mut self,
58        scope: PullScope,
59        group: GroupId,
60        ctx: &mut SimContext,
61    ) {
62        let PullScope { pull, epoch } = scope;
63
64        self.handle_group_completion(pull, epoch, group, ctx.state.current_time);
65    }
66
67    pub(super) fn on_pull_complete_impl(
68        &mut self,
69        scope: PullScope,
70        finalize: bool,
71        ctx: &mut SimContext,
72    ) {
73        let PullScope { pull, epoch } = scope;
74
75        self.handle_pull_completion(pull, epoch, finalize, ctx);
76    }
77
78    pub(super) fn on_encounter_terminate_impl(
79        &mut self,
80        _reason: EncounterTerminationReason,
81        ctx: &mut SimContext,
82    ) {
83        self.mark_encounter_terminated(ctx.state.current_time);
84    }
85
86    pub(super) fn on_wave_activate_impl(
87        &mut self,
88        scope: PullScope,
89        wave: WaveId,
90        ctx: &mut SimContext,
91    ) {
92        let PullScope { pull, epoch } = scope;
93
94        self.handle_wave_activation(pull, epoch, wave, ctx.state.current_time);
95    }
96
97    pub(super) fn on_enemy_activate_impl(
98        &mut self,
99        scope: PullScope,
100        target: EnemyIdx,
101        ctx: &mut SimContext,
102    ) {
103        let PullScope { pull, epoch } = scope;
104
105        self.handle_enemy_activation(pull, epoch, target, ctx);
106
107        if self.state.is_valid_target(target)
108            && let Some(swing_ms) = self.state.enemy_definition(target).and_then(
109                wowlab_engine_domain::encounter::EnemyActorDefinition::auto_attack_swing_ms,
110            )
111        {
112            self.state.schedule_enemy_auto_attack(
113                target,
114                ActorId::Player,
115                ctx.state
116                    .current_time
117                    .saturating_add(SimTime::from_millis(swing_ms / FIRST_ENEMY_SWING_DIVISOR)),
118            );
119        }
120    }
121
122    pub(super) fn on_retarget_impl(&mut self, scope: PullScope, ctx: &mut SimContext) {
123        let PullScope { pull, epoch } = scope;
124
125        self.handle_retarget(pull, epoch, ctx);
126    }
127
128    pub(super) fn on_encounter_move_impl(
129        &mut self,
130        scope: PullScope,
131        actor: PositionedActorRef,
132        transform: SpatialTransform,
133        ctx: &mut SimContext,
134    ) {
135        let PullScope { pull, epoch } = scope;
136
137        self.handle_movement(pull, epoch, actor, transform, ctx);
138    }
139
140    pub(super) fn on_raid_event_impl(&mut self, index: usize, ctx: &mut SimContext) {
141        self.handle_raid_event(index, ctx);
142    }
143
144    pub(super) fn on_raid_add_despawn_impl(
145        &mut self,
146        target: EnemyIdx,
147        activation_generation: u64,
148        encounter_generation: wowlab_engine_ports::PullLifecycleGeneration,
149        ctx: &mut SimContext,
150    ) {
151        self.handle_raid_add_despawn(target, activation_generation, encounter_generation, ctx);
152    }
153    pub(crate) fn cleanup_inactive_enemy_effects(
154        &mut self,
155        target: EnemyIdx,
156        now: SimTime,
157        telemetry: &mut wowlab_engine_telemetry::TelemetrySink,
158    ) {
159        self.terminate_channel_targeting(target, now);
160        self.with_hook_ctx(telemetry, HookCtxRequest::for_target(now, target), |ctx| {
161            crate::systems::expire_auras_on_actor(ctx, ActorId::Enemy(target));
162        });
163    }
164
165    pub(super) fn cleanup_inactive_enemy(
166        &mut self,
167        target: EnemyIdx,
168        now: SimTime,
169        telemetry: &mut wowlab_engine_telemetry::TelemetrySink,
170    ) {
171        self.cleanup_inactive_enemy_effects(target, now, telemetry);
172        self.state.retarget();
173        project_encounter(&self.state, &mut self.buf, now);
174        self.state.schedule(Event::PlayerReady { t: now });
175    }
176
177    fn terminate_channel_targeting(&mut self, target: EnemyIdx, now: SimTime) {
178        if self.state.runtime.casting.active_channel_target != Some(target) {
179            return;
180        }
181
182        crate::systems::end_channel(&mut self.state, &mut self.buf, now);
183    }
184}