144 Commits

Author SHA1 Message Date
Kelsi
be33fbe2cc fix(gameobjects): drive animation freezing from game object type
Freezing was decided by matching model paths against a list of portal
names, so every game object with a looping idle sat in its bind pose
unless someone had thought to name it — fishing pools were the reported
case, but braziers, banners and waterwheels were frozen the same way.

Freeze only the types whose pose is server state (door, button, chest,
trap, goober, destructible building, trapdoor) and let everything else
play its idle, which is what retail does. A game object's model spawns
from its display id before its type is known, so an object that spawns
with the query still in flight is frozen conservatively and revisited
when GAMEOBJECT_QUERY_RESPONSE arrives, via a new info callback.
2026-07-31 06:32:26 -07:00
Kelsi
3139896401 docs: changelog entries for v2.0.30-preview and v2.0.31-preview
The open v2.0.29-preview section had been collecting entries as work
landed, but stopped at the terrain-seam fix; everything after it went
unrecorded. Adds the two missing sections: rendering (brightness multiply
over water, FSR3 16-bit storage, validation-layer env var), bank and guild
bank, crafting, quests, GM tools, and the two changes since.

Also narrows a comment in macos_platform.hpp -- W takes no diacritics.
2026-07-24 18:25:51 -07:00
Kelsi
af2c6bb280 fix: disable macOS press-and-hold accent popup during play
SDL2 leaves text input enabled for the whole session, so AppKit routes key
events through NSTextInputContext even in normal gameplay. Holding a key
with diacritics -- W, A, S, E -- opened the accent chooser over the game
instead of repeating the key.

Register ApplePressAndHoldEnabled=NO before SDL_Init brings up
NSApplication. The registration domain is process-scoped, so the user's
saved preferences are untouched; if a global setting explicitly enables
press-and-hold, fall back to this application's own domain, which outranks
NSGlobalDomain.

Needs an Objective-C++ shim, wired into both wowee and wowee_editor since
both compile window.cpp.
2026-07-24 18:10:58 -07:00
Kelsi
83ce09a87d fix: apply barber shop appearance changes without a restart
Barber cuts updated the character data and inventory preview but never
rebuilt the in-world 3D model, so the new hair/facial hair only showed
after a restart or teleport. Add a player-model-rebuild callback fired on
PLAYER_BYTES / PLAYER_BYTES_2 changes; Application re-spawns the model in
place via the same path teleport uses. Guard on an actual appearance diff
(PLAYER_BYTES_2 also carries rest state), preserve the current position,
and reset equipment dirty tracking so armor and weapons re-composite onto
the fresh model.
2026-07-22 15:33:37 -07:00
Kelsi
462defc76d refactor: consolidate transport hull orientation into single bow-offset rule
Funnel every ship-facing path through TransportManager::transportModelBowOffset
(facing = direction of travel + fixed per-model bow offset), replacing the
scattered per-entry 180-degree hull corrections. berthRunsParallel() names the
side-on dock routes, and buildTaxiSegmentSpline is extracted static/testable so
the cyclic-wrap behavior is unit-covered without a DBC.
2026-07-20 19:36:21 -07:00
Kelsi
fe0ad4dfdb fix: stabilize Kraken cross-map boarding 2026-07-20 03:35:32 -07:00
Kelsi
955c04e385 fix: stabilize transports and world integration 2026-07-20 03:15:37 -07:00
Kelsi
0123173e90 fix mount sync and enchant timing 2026-07-19 17:29:21 -07:00
Kelsi
efe56faf55 feat(core): add portable config mode with one-time migration
Route the four config call sites (login.cfg, settings.cfg, last_character.cfg,
characters/) through a shared core::getConfigRoot(). A portable.txt marker or an
existing config/ folder next to the executable redirects config to <exe_dir>/config;
otherwise the per-user location (%APPDATA%\wowee or ~/.wowee) is used as before.

On first portable launch, migratePortableConfigIfNeeded() seeds the portable
folder from the existing per-user config so saved server profiles, settings, and
characters carry over.
2026-07-17 11:37:31 -07:00
Kelsi
b87471f29d refactor(core): retire temporary runtime tuning hooks 2026-07-17 02:33:53 -07:00
Kelsi
67edf01a57 refactor(audio): make music loading cancellable 2026-07-17 02:10:41 -07:00
Kelsi
d00f20b3e1 fix(gameplay): polish worker and consumable presentation
Parse per-animation M2 color alpha tracks across vanilla and WotLK layouts so hidden lumberjack bundle meshes remain culled until their authored death sequence.

Keep scripted worker emotes and display swaps coherent across movement and async model loads. Rework food and drink presentation around the seated aura lifecycle, repeat consumption audio, preserve real cast animations on auto-stand, and retain potion swigs.

Add server-driven intoxication movement, camera, and post-process effects plus focused regression coverage for M2 alpha tracks and consumable classification.
2026-07-17 00:48:21 -07:00
Kelsi
f9d1c70397 fix(gameplay): repair fishing interactions and worker emotes 2026-07-16 22:26:27 -07:00
Kelsi
799ab3c72d fix(gameplay): repair barber, unstuck mounts and terrain seams 2026-07-16 21:28:12 -07:00
Kelsi
90fee4ba6f fix(world): solid rendered WMO walls; retry transport registration
Wall collision only blocked triangles carrying the explicit collision
flag (0x08), so rendered geometry without it — the Deeprun Tram gates —
was walk-through, letting players escape onto the exterior shell.
Blocking is now the union of collision hulls (0x08, tunnels rely on
invisible ones) and rendered non-detail surfaces (0x20 without 0x04);
detail geometry still never blocks.

Transport registrations were erased on the first frame the GameObject
render instance didn't exist yet, silently racing the budgeted GO spawn
queue — tram cars never registered as transports. Retry until the
instance spawns and only drop (with a warning) after ~30s.
2026-07-16 14:47:51 -07:00
Kelsi
f0dccf7f17 fix(character): move weapon model ids out of the NPC displayId keyspace
Weapon models share CharacterRenderer's models map with NPC composites
keyed by creature displayId, and the weapon id counter started at 1000
inside that range. Once enough weapon reloads advanced the counter onto
a loaded NPC's displayId, attachWeapon reused the NPC model as the
weapon mesh and stomped its textures. Weapon ids now start at
0x40000000, above every other id scheme in the map (displayIds,
player models at 60000+, projectiles capped below 999000).
2026-07-16 13:59:16 -07:00
Kelsi
8921c40ae7 fix(character): bake in live-tuned 2H back-sheath placement
Defaults from in-game /sheathtune iteration: tx=-0.03 ty=-0.10 tz=0
cant=33, roll unchanged. Clears the torso and left arm at rest.
2026-07-16 13:55:31 -07:00
Kelsi
cca37fb74f feat(ui): add /sheathtune for live 2H back-sheath placement tuning
The back-sheath offsets and angles are now runtime-tunable and the
/sheathtune chat command updates them and reloads equipped weapons
immediately, so weapon placement can be iterated in game instead of
rebuilding per guess. Current defaults unchanged; settled values get
baked into SheathTuning.
2026-07-16 13:43:02 -07:00
Kelsi
cc170bf61c feat(character): sheathe weapons automatically when mounting
Mounting stows drawn weapons like the original client: the mount
callback sets the sheathed state and reloads equipped weapons onto
their sheath attachment points.
2026-07-16 13:33:07 -07:00
Kelsi
5eb8cee324 perf(spawn): throttle and de-lock the creature stealth visual sync
syncCreatureStealthVisuals() ran every frame and paid a mutex-locked
getEntity() plus a dynamic_pointer_cast for every spawned creature,
adding ~100k lock acquisitions and RTTI casts per second in crowded
zones. Scan every 15th frame instead, read the unlocked main-thread
entity map, and replace the dynamic cast with a type check and
static_cast. Stealth flags flip rarely, so the ~150ms worst-case
latency on the translucency fade is imperceptible.
2026-07-14 21:18:33 -07:00
Kelsi
e544122dea fix(rendering): honor creature stealth visibility 2026-07-14 15:34:57 -07:00
Kelsi
dc3e447e93 perf(spawn): cache NPC equipment attachment models
Helm and shoulder M2s were re-read, re-parsed and re-uploaded once per NPC spawn,
with a fresh model id each time — so a crowd wearing the same gear paid for the
same mesh repeatedly. Cache the parsed geometry by path (with negative caching so
a missing file is not re-probed) and the renderer model id by (path, texture).

The id is keyed by texture as well as mesh because attachWeapon() binds the
texture to the model id: sharing one id across recolours of the same mesh would
let the last NPC spawned retexture every other NPC wearing it.
2026-07-14 13:40:08 -07:00
Kelsi
78310de36a fix(spawn): remove blocking creature M2 load from the main-thread spawn path
spawnOnlineCreature carried a fallback that read and parsed the creature M2 from
disk inline. The async loader already publishes every creature model before a
spawn is attempted, so the fallback was unreachable — but it left file I/O one
missed invariant away from running mid-frame. The spawn now treats a cache miss
as 'not ready' and re-queues through the async path, and the sync loader is gone.
2026-07-14 13:29:40 -07:00
Kelsi
62738956a7 fix(core): stop the watchdog mistaking world load for a hang
Entering the world blocks the main loop for ~1.6s while the world loader presents
its own frames, so the loop never beat the watchdog and it force-released mouse
capture mid-load. The loader now beats the watchdog at each progress step.

Also time the render phases, mirroring the existing IN_GAME update stages — the
stall was invisible because render() had no instrumentation.
2026-07-14 12:53:56 -07:00
Kelsi
1e95504c43 fix(character): show mining pick during gather casts
Show the actual Mining Pick model while the local character performs a
mining cast instead of leaving the equipped main-hand weapon visible.

Mining already used the correct gather animation, but the attachment path
kept rendering the normal equipped weapon. Add a scoped mining-pick override
that attaches ItemDisplayInfo 6568 to the right hand for the cast duration
and restores the equipped weapons when the cast ends.

Keep the temporary tool authoritative across equipment refreshes while the
cast is active, and track the character instance that owns the override so
interrupted casts, respawns, or logout transitions cannot leave a later
character stuck with the temporary attachment.

Wire the appearance composer into animation callbacks so mining start and
finish events can drive the held-tool override directly.

Build passes. All 33 tests pass.
2026-07-13 16:43:10 -07:00
Kelsi
4fa0dd250a Add isolated asset profiles and Turtle 1.18.1 support 2026-07-13 15:47:34 -07:00
Josh Anderson
a7e3e2e992 fix(taxi): select landing-clamp floor by proximity, not by source
The third merge-blocking issue Kelsidavis flagged in review of PR #96:
unconditionally preferring any WMO/M2 floor over terrain fixed
underground landings (terrain has no notion of being underground - a
WMO tunnel beneath a mountain, like Ironforge's flight point, reports
the outdoor mountain surface far above the correct tunnel floor), but
could just as easily snap an outdoor landing down onto an unrelated
structure sitting underneath it.

Now captures the character's render-position Z the moment the landing
clamp arms (WorldEntryCallbackHandler::taxiLandingReferenceZ_) -
wherever the taxi flight simulation itself left the player, before
terrain/WMO streaming has a chance to move things around - and picks
whichever candidate (terrain/WMO/M2) is numerically closest to that
reference, rather than preferring non-terrain sources outright. Still
correctly picks the WMO tunnel floor for Ironforge (the flight lands
right at it, ~0 distance, vs. ~267 units to the terrain surface above)
while no longer able to prefer a distant WMO/M2 floor under an outdoor
landing over the much-closer terrain surface.
2026-07-13 05:27:59 -05:00
Kelsi
7e77d90f91 feat(ui): show build version and date on the login screen and in settings
Generate core/version.hpp from `git describe --tags --abbrev=0` so the client
always reports the last tagged release, refreshed on every build rather than only
when cmake happens to reconfigure. The date carries no clock time, so the header
changes at most once a day instead of forcing a recompile every build.

Also un-ignore cmake/*.cmake: the repo's *.cmake rule is aimed at build output,
and would have silently dropped the new module from the tree.
2026-07-12 09:33:00 -07:00
Kelsi
63f0866ad1 feat(render): show enchant visuals on enchanted weapons
A sharpened blade should glint. Resolve the enchant's visual through
SpellItemEnchantment -> ItemVisuals -> ItemVisualEffects and attach the effect M2
to the weapon model's item-visual attachment points, riding the weapon transform.

Such a model is nothing but the additive FX batches that attached weapons
otherwise drop, so effect instances are exempted from that cull, keep their
animation advanced, and are forced additive and unlit — their materials declare
Mod/alpha blending, which would otherwise composite the glow card's black
background as an opaque quad.

Applying an enchant leaves the displayInfoId untouched, so the equipment-dirty
check now also tracks per-slot enchant ids; the visual appears without re-equipping.
2026-07-12 09:24:15 -07:00
Kelsi
45f7baac5c perf: replace per-frame std::async with a persistent thread pool
Every frame the render loop spawned and destroyed OS threads via
std::async: the M2 doodad animation update, three secondary command
buffer recording tasks, up to N bone-matrix chunks in the M2 and
character renderers, and two collision floor queries while moving —
hundreds of thread create/destroy cycles per second. Route all of them
through a shared persistent core::ThreadPool sized to the machine.

Bone-matrix chunk dispatch now runs its last chunk on the calling
thread so the nested case (M2 update running on a pool worker while
submitting sub-work) always makes progress and cannot deadlock.
2026-07-12 08:06:27 -07:00
Kelsi Rae Davis
86927ced2b Merge pull request #89 from paleophyte/fix/tbc-deeprun-tram-boarding
Fix Deeprun Tram: boarding, positioning, orientation, and polish
2026-07-10 17:31:26 -07:00
Kelsi
11d45e32b3 fix(geosets): correct boot/shin geoset selection and DBC loading
The DBC loader skipped binary DBCs in Data/db/ on Linux because
filenames are lowercase but code requested mixed-case names.  This
caused the corrupted CSV fallback to be used, where nearly all
integer fields (including GeosetGroup) were misclassified as strings
and exported as string-block lookups instead of numeric values.

Add case-insensitive filename matching for the Data/db/ directory
scan and prioritize expansion overlay DBCs.  Fix kGeosetBareShins
from 503 to 501, add per-race lowestInGroup fallback (Gnome, Tauren),
and use DBC layout JSON for field indices in game_screen_hud.  Exclude
field 0 (record ID) from string detection in both dbc_to_csv and the
asset extractor to prevent future CSV corruption.
2026-07-10 17:17:24 -07:00
Josh Anderson
f47bfe306e feat: allow walking on the deck while riding an M2 transport
The previous fix pinned the ride offset completely fixed to stop
riding from being a no-op identity, which correctly carries the
player along but also means WASD input while riding did nothing -
"once I was attached I couldn't move at all."

Compare this frame's actual render position (after the movement
system processes WASD, which runs earlier in the frame) against
where the ride-lock code placed the player last frame
(lastM2RideLockedCanonical_). That difference is exactly how far the
player tried to walk this frame, independent of how far the
transport itself moved underneath them; add it onto the persisted
offset. Standing still holds a fixed deck position (delta is zero),
active input still moves the player around on the deck, and the
delta baseline resets cleanly (hasM2RideLock_) whenever a fresh
boarding sets a new starting offset.
2026-07-09 22:20:00 -05:00
Josh Anderson
b83f323790 fix(ui): stabilize logout mouse capture 2026-07-06 00:24:47 -05:00
Josh Anderson
39836d3109 fix(tbc): support MaNGOS realm character enumeration 2026-07-05 11:44:12 -05:00
Pavel Okhlopkov
fff06fc932 refactor: decompose world map into modular component architecture
Break the monolithic 1360-line world_map.cpp into 16 focused modules
under src/rendering/world_map/:

Architecture:
- world_map_facade: public API composing all components (PIMPL)
- world_map_types: Vulkan-free domain types (Zone, ViewLevel, etc.)
- data_repository: DBC zone loading, ZMP pixel map, POI/overlay storage
- coordinate_projection: UV projection, zone/continent lookups
- composite_renderer: Vulkan tile pipeline + off-screen compositing
- exploration_state: server mask + local exploration tracking
- view_state_machine: COSMIC→WORLD→CONTINENT→ZONE navigation
- input_handler: keyboard/mouse input → InputAction mapping
- overlay_renderer: layer-based ImGui overlay system (OCP)
- map_resolver: cross-map navigation (Outland, Northrend, etc.)
- zone_metadata: level ranges and faction data

Overlay layers (each an IOverlayLayer):
- player_marker, party_dot, taxi_node, poi_marker, quest_poi,
  corpse_marker, zone_highlight, coordinate_display, subzone_tooltip

Fixes:
- Player marker no longer bleeds across continents (only shown when
  player is in a zone belonging to the displayed continent)
- Zone hover uses DBC-projected AABB rectangles (restored from
  original working behavior)
- Exploration overlay rendering for zone view subzones

Tests:
- 6 new test files covering coordinate projection, exploration state,
  map resolver, view state machine, zone metadata, and integration

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
2026-04-12 09:52:51 +03:00
Pavel Okhlopkov
de0383aa6b refactor: extract spline math, consolidate packet parsing, decompose TransportManager
Extract CatmullRomSpline (include/math/spline.hpp, src/math/spline.cpp) as a
standalone, immutable, thread-safe spline module with O(log n) binary segment
search and fused position+tangent evaluation — replacing the duplicated O(n)
evalTimedCatmullRom/orientationFromTangent pair in TransportManager.

Consolidate 7 copies of spline packet parsing into shared functions in
game/spline_packet.{hpp,cpp}: parseMonsterMoveSplineBody (WotLK/TBC),
parseMonsterMoveSplineBodyVanilla, parseClassicMoveUpdateSpline,
parseWotlkMoveUpdateSpline, and decodePackedDelta. Named SplineFlag constants
replace magic hex literals throughout.

Extract TransportPathRepository (game/transport_path_repository.{hpp,cpp}) from
TransportManager — owns path data, DBC loading, and path inference. Paths stored
as PathEntry wrapping CatmullRomSpline + metadata (zOnly, fromDBC, worldCoords).
TransportManager reduced from ~1200 to ~500 lines, focused on transport lifecycle
and server sync.

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
2026-04-11 08:30:28 +03:00
Pavel Okhlopkov
6ba0edc2fb change weapon for ranged skills
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
2026-04-10 23:01:16 +03:00
Pavel Okhlopkov
312994be83 world loading memory pressure detector
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
2026-04-06 21:05:20 +03:00
Paul
6dcc06697b refactor(core): decompose Application::setupUICallbacks() into 7 domain handlers
Extract ~1,700 lines / 60+ inline [this]-capturing lambdas from the monolithic
Application::setupUICallbacks() into 7 focused callback handler classes following
the ToastManager/ChatPanel::setupCallbacks() pattern already in the codebase.

New handlers (include/core/ + src/core/):
  - NPCInteractionCallbackHandler  NPC greeting/farewell/vendor/aggro voice
  - AudioCallbackHandler           Music, positional sound, level-up, achievement, LFG
  - EntitySpawnCallbackHandler     Creature/player/GO spawn, despawn, move, state
  - AnimationCallbackHandler       Death, respawn, combat, emotes, charge, sprint, vehicle
  - TransportCallbackHandler       Mount, taxi, transport spawn/move
  - WorldEntryCallbackHandler      World entry, unstuck, hearthstone, bind point
  - UIScreenCallbackHandler        Auth, realm selection, char selection/creation/deletion

application.cpp:  4,462 → 2,791 lines  (−1,671)
setupUICallbacks: ~1,700 → ~50 lines (thin orchestrator)

Deduplication:
  resolveSoundEntryPath()   — was 3× copy-paste of SoundEntries.dbc lookup
  resolveNpcVoiceType()     — was 4× copy-paste of display-ID→voice detection
  precacheNearbyTiles()     — was 3× copy-paste of 17×17 tile loop
  4 helper lambdas          — promoted to private methods on WorldEntryCallbackHandler

State migration out of Application:
  charge* (6 vars)          → AnimationCallbackHandler
  hearth*/worldEntry*/taxi* → WorldEntryCallbackHandler
  pendingCreatedCharacterName_ → UIScreenCallbackHandler

Bug fixes:
  - Duplicate `namespace core {` in application.hpp caused wowee::std pollution
  - AppState forward decl in ui_screen_callback_handler.hpp was at wrong scope
  - world_loader.cpp accessed moved member vars directly via friend; now uses handler API
2026-04-05 16:48:17 +03:00
Paul
e58f9b4b40 feat(animation): 452 named constants, 30-phase character animation state machine
Add animation_ids.hpp/cpp with all 452 WoW animation ID constants (anim::STAND,
anim::RUN, anim::FIRE_BOW, ... anim::FLY_BACKWARDS, etc.), nameFromId() O(1)
lookup, and flyVariant() compact 218-element ground→FLY_* resolver.

Expand AnimationController into a full state machine with 20+ named states:
spell cast (directed→omni→cast fallback chain, instant one-shot release),
hit reactions (WOUND/CRIT/DODGE/BLOCK/SHIELD_BLOCK), stun, wounded idle,
stealth animation substitution, loot, fishing channel, sit/sleep/kneel
down→loop→up transitions, sheathe/unsheathe combat enter/exit, ranged weapons
(BOW/GUN/CROSSBOW/THROWN with reload states), game object OPEN/CLOSE/DESTROY,
vehicle enter/exit, mount flight directionals (FLY_LEFT/RIGHT/UP/DOWN/BACKWARDS),
emote state variants, off-hand/pierce/dual-wield alternation, NPC
birth/spawn/drown/rise, sprint aura override, totem idle, NPC greeting/farewell.

Add spell_defines.hpp with SpellEffect (~45 constants) and SpellMissInfo
(12 constants) namespaces; replace all magic numbers in spell_handler.cpp.

Add GAMEOBJECT_BYTES_1 to update field table (all 4 expansion JSONs) and wire
GameObjectStateCallback. Add DBC cross-validation on world entry.

Expand tools/_ANIM_NAMES from ~35 to 452 entries in m2_viewer.py and
asset_pipeline_gui.py. Add tests/test_animation_ids.cpp.

Bug fixes included:
- Stand state 1 was animating READY_2H(27) — fixed to SITTING(97)
- Spell casts ended freeze-frame — add one-shot release animation
- NPC 2H swing probe chain missing ATTACK_2H_LOOSE (polearm/staff)
- Chair sits (states 2/4/5/6) incorrectly played floor-sit transition
- STOP(3) used for all spell casts — replaced with model-aware chain
2026-04-04 23:02:53 +03:00
Kelsi
f79395788a fix(rendering): filter player hair geosets via CharHairGeosets.dbc
buildDefaultPlayerGeosets() was inserting all submeshIds 0-99 into
activeGeosets, showing every hair variation simultaneously. Now uses
the hairGeosetMap_ (from CharHairGeosets.dbc) to select only the
correct hair scalp geoset for the player's race/sex/style, matching
the existing NPC geoset filtering logic in EntitySpawner.
2026-04-03 22:43:37 -07:00
Paul
2cb47bf126 chore(testing): add unit tests and update core render/network pipelines
- add new tests:
  - test_blp_loader.cpp
  - test_dbc_loader.cpp
  - test_entity.cpp
  - test_frustum.cpp
  - test_m2_structs.cpp
  - test_opcode_table.cpp
  - test_packet.cpp
  - test_srp.cpp
  - CMakeLists.txt
- add docs and progress tracking:
  - TESTING.md
  - perf_baseline.md
- update project config/build:
  - .gitignore
  - CMakeLists.txt
  - test.sh
- core engine updates:
  - application.cpp
  - game_handler.cpp
  - world_socket.cpp
  - adt_loader.cpp
  - asset_manager.cpp
  - m2_renderer.cpp
  - post_process_pipeline.cpp
  - renderer.cpp
  - terrain_manager.cpp
  - game_screen.cpp
- add profiler header:
  - profiler.hpp
2026-04-03 09:41:34 +03:00
Paul
d43397163e refactor: decouple Application singleton by extracting core subsystems and updating interfaces
- Add `audio::AudioCoordinator` interface and implementation
- Modify `Application` to reduce singleton usage and move controller responsibilities:
  - application.hpp
  - application.cpp
- Update UI and audio headers/sources:
  - game_screen.hpp
  - game_screen.cpp
  - ui_manager.hpp
  - audio_coordinator.hpp
  - audio_coordinator.cpp
- Project config touched:
  - CMakeLists.txt
2026-04-01 20:38:37 +03:00
Paul
9b38e64f84 "Fix and refine app initialization flow
- Update core application startup paths and cleanup logic
- Adjust renderer & input subsystem integration for stability
- Address recent staging source updates with robust error handling"
2026-04-01 20:06:26 +03:00
Paul
afeaa13562 chore(application): extract entity spawner + composer, apply app and UI updates
- add include/core/appearance_composer.hpp + src/core/appearance_composer.cpp
- update include/core/application.hpp + src/core/application.cpp
- update src/ui/game_screen.cpp
- adjust CMakeLists.txt and README.md for new composer module
2026-04-01 13:31:48 +03:00
Paul
cf3ae3bbfe chore(application): refactor app lifecycle and add entity spawner module
- updated CMakeLists.txt to include new module targets
- refactored application.hpp + application.cpp
- added new `entity_spawner` headers + sources:
  - entity_spawner.hpp
  - entity_spawner.cpp
2026-03-31 22:01:55 +03:00
Paul
a86efaaa18 [refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.

Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
  pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
  taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
  `services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
  after all subsystems are created, then constructs `GameHandler`
  (fixes latent init-order bug: `GameHandler` was previously created
  before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
  `isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
  included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
  `CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
  `Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
Kelsi
f2237c5531 perf: switch 3 spawn queues from vector to deque
pendingPlayerSpawns_, deferredEquipmentQueue_, and
pendingGameObjectSpawns_ are consumed from the front via erase(begin()),
which is O(n) on vector (shifts all elements). With many spawns queued
(entering a city), this made the processing loop O(n²). deque supports
O(1) front erasure. pendingCreatureSpawns_ already used deque.
2026-03-29 19:51:26 -07:00
Kelsi
56f8f5c592 refactor: extract loadWeaponM2() to deduplicate weapon model loading
Extract shared M2+skin loading logic into Application::loadWeaponM2(),
replacing duplicate 15-line blocks in loadEquippedWeapons() and
tryAttachCreatureVirtualWeapons(). Future weapon loading changes only
need to update one place.
2026-03-25 14:17:19 -07:00