Skip to main content

wowlab_engine_combat/
lib.rs

1#![expect(
2    clippy::multiple_crate_versions,
3    reason = "the workspace dependency graph currently includes independently versioned transitive dependencies"
4)]
5#![expect(
6    clippy::redundant_feature_names,
7    // #t(rust_duplicate_strings) Rust attributes require a literal reason at the declaration they govern.
8    reason = "the cross-crate test-support capability name is mandated by the shared fixture contract"
9)]
10
11//! Combat system for the `WoW` Lab simulation engine.
12//!
13
14#[cfg(test)]
15extern crate self as wowlab_engine_combat;
16
17mod builder;
18mod context;
19mod handler;
20#[cfg(test)]
21mod integration_tests;
22mod keys;
23mod pipeline;
24mod state;
25mod systems;
26mod targeting;
27#[cfg(any(test, feature = "test-support"))]
28#[doc(hidden)]
29pub mod test_alloc;
30#[cfg(any(test, feature = "test-support"))]
31pub mod test_support;
32mod travel;
33
34/// Canonical codegen-owned local identities.
35///
36/// Values are generated into the content artifact tree.
37/// This inclusion restricts private local-index construction to this crate.
38#[doc(hidden)]
39#[expect(
40    non_snake_case,
41    reason = "generated identity namespaces mirror the established constant-module convention"
42)]
43pub mod generated_identities {
44    include!(concat!(
45        env!("CARGO_MANIFEST_DIR"),
46        "/../engine-content/src/generated/identity_values.rs"
47    ));
48}
49
50#[cfg(test)]
51#[global_allocator]
52static TEST_ALLOCATOR: test_alloc::TestAllocator = test_alloc::TestAllocator;
53
54#[rustfmt::skip]
55pub use builder::{
56    BuilderError, validated_channel_tick_count, validated_max_stacks,
57    validated_milliseconds, validated_nonnegative,
58};
59pub use builder::{
60    aura_builder::{AuraDefinitionDraft, PeriodicDamageDataInput},
61    auto_attack_builder::AutoAttackDefinitionDraft,
62    built::BuiltCombatSystem,
63    combat_builder::{CombatSystemBuilder, RppmScalingOptions},
64    draft::CombatSystemDraft,
65    impact_effect_proc_builder::ImpactEffectProcDefinition,
66    spell_builder::draft::SpellDefinitionDraft,
67};
68pub use wowlab_types::combat::PeriodicDamageMode;
69#[rustfmt::skip]
70pub use context::{
71    FreeActionSource, HookCtx, HookCtxRequest, HookCtxServices,
72};
73pub use context::{
74    combat_ctx::{CombatCtx, CombatView, HookProgramView, HookView},
75    damage::DamageOutcome,
76    hook::{AuraOps, CooldownOps, DamageOps, GuardianOps, ProcOps, ResourceOps, SchedulingOps},
77};
78#[rustfmt::skip]
79pub use handler::CombatHandler;
80#[rustfmt::skip]
81pub use state::buff_effect::{
82    BuffEffect, BuffTotals, MAX_AURA_BUFF_EFFECTS, try_push_effect,
83};
84pub use state::{
85    config::BaseStats,
86    damage::{
87        DamageEffectRef, DamageReplication, DamageSource, HealthThresholdDirection,
88        RuntimeDamageDef, WeaponApType,
89    },
90    defs::{
91        aura::{AuraData, AuraTickBehavior, DynamicTickAction},
92        drivers::{
93            DriverSpellModifier, DriverSpellModifierResult, PassiveDriverEffect, PeriodicDriver,
94            WeaponEnchantBinding,
95        },
96        guardians::{
97            GuardianAbility, GuardianActionFn, GuardianEvent, GuardianHandle, GuardianSpec,
98            GuardianSummonError,
99        },
100        periodic::{PeriodicData, PeriodicKind},
101    },
102    program::{DamageActionId, DamageEffectId, config::AttackPosition},
103};
104#[rustfmt::skip]
105pub use state::defs::procs::{
106    AccumulatingImpactProc, ImpactActorFilter, ImpactChanceScale, ImpactEvent, ImpactEventData,
107    ImpactProc, ImpactProcFn, ImpactSource, LandedImpactEvent, LandedImpactProc, LandedImpactProcFn,
108    ProcDriver, ProcEventKind, ProcHitMask, ProcPhaseMask, ProcPolicy, ProcProvenance,
109    ProcTriggerSide, ResourceGainEvent, ResourceGainProc, SwingEvent, SwingHand, SwingReplacement,
110};
111pub use state::{
112    AuraBinding, CombatInstance, CombatProgram, LocalAuraIdx, LocalRppmIdx, LocalSpellIdx,
113    LocalThresholdIdx, PeriodicDriverBinding, PetResourceBinding, ProcCategoryBinding,
114    ProcCounterBinding, RppmBinding, SpecAuraBinding, SpecIdentityProof, SpecSpellBinding,
115    SpellBinding, SystemRppmBinding,
116    defs::{
117        pets::{PetAbility, PetActionBar, PetResourceModel, PetStatInheritance},
118        spell::{
119            CastHookFn, EmpowerReleaseAdjustment, MAX_AURA_APPLICATION_EXCLUSIONS, ResourcePool,
120            SpellEffectData, SpellEffectRange,
121        },
122    },
123    ground_effect::{GroundEffectDefinition, GroundEffectId},
124    resource::ResourceGainSource,
125};
126pub use wowlab_engine_rng::{RppmTracker, ThresholdTracker};
127
128#[rustfmt::skip]
129pub use systems::auras::apply::{apply_aura, apply_aura_with_duration};
130#[cfg(test)]
131pub use systems::damage_pipeline::deal_damage_ap;
132pub use systems::{
133    auras::{
134        expire::expire_aura,
135        stacks::{extend_aura, is_aura_active},
136    },
137    buffs::buff_totals,
138    channel::{clip_active_channel, process_channel_tick_for},
139    damage_pipeline::api::{DamageFlags, DamagePayload},
140    mastery::{MasteryCtx, MasteryEvent, MasteryFn, no_crit_damage, no_damage_bonus},
141    periodic::tick::process_single_aura_tick_for,
142    resources::set_primary_resource_base_regen,
143    spell_gates::set_spell_gate,
144};
145/// Generates a smoke test that verifies draft construction does not panic with default inputs.
146#[macro_export]
147macro_rules! smoke_test {
148    ($build_fn:path) => {
149        #[cfg(test)]
150        mod tests {
151            use super::*;
152
153            #[googletest::gtest]
154            fn build_combat_system_draft_does_not_panic() -> googletest::Result<()> {
155                let stats = wowlab_engine_ports::CombatStats::default();
156                let encounter = wowlab_engine_ports::test_support::introspection_fixture(300.0)
157                    .expect("valid smoke-test encounter proof");
158                let game_data =
159                    wowlab_engine_ports::test_support::introspection_game_data(&encounter);
160                let rotation = wowlab_types::sim::Rotation::empty();
161                let params = wowlab_engine_ports::HandlerParams {
162                    game_data,
163                    rotation: &rotation,
164                    stats: &stats,
165                    talent_selections: &[],
166                    encounter: &encounter,
167                    fight_duration_secs: encounter.fight_duration_secs(),
168                    equipped_items: &[],
169                    set_bonus_auras: &[],
170                    bloodlust: false,
171                    pre_pot_tempered: false,
172                    flask: false,
173                    augment_rune: false,
174                    race: wowlab_types::game::RaceId::Human,
175                    weapon_enchant_procs: &[],
176                    bugs: wowlab_engine_ports::BugSettings::default(),
177                    cast_latency: wowlab_engine_ports::CastLatency::default(),
178                    raid_events: &[],
179                    external_buffs: &[],
180                }
181                .validate()
182                .expect("valid handler params");
183                let _built = $build_fn(&params);
184                Ok(())
185            }
186        }
187    };
188}