Commit graph

362 commits

Author SHA1 Message Date
Kelsi
d2a396df11 feat: log GPU vendor/name at init, add PLAY_SOUND diagnostics
Log GPU name and vendor ID during VkContext initialization for easier
debugging of GPU-specific issues (FSR3, driver compat, etc.). Add
isAmdGpu()/isNvidiaGpu() accessors.

Temporarily log SMSG_PLAY_SOUND and SMSG_PLAY_OBJECT_SOUND at WARN
level (sound ID, name, file path) to diagnose unidentified ambient
NPC sounds reported by the user.
2026-03-24 09:56:54 -07:00
Kelsi
503f9ed650 fix: auto-detect CharSections.dbc layout and add Blood Elf/Draenei NPC voices
CharSections.dbc has different field layouts between stock WotLK (textures
at field 4-6) and Classic/TBC/Turtle/HD-textured WotLK (VariationIndex at
field 4). Add detectCharSectionsFields() that probes field-4 values at
runtime to determine the correct layout, so both stock and modded clients
work without JSON changes.

Also add BLOODELF_MALE/FEMALE and DRAENEI_MALE/FEMALE voice types to the
NPC voice system — previously all Blood Elf and Draenei NPCs fell through
to GENERIC (random dwarf/gnome/night elf/orc mix).
2026-03-23 11:00:49 -07:00
Kelsi
aa164478e1 feat: fire DISPLAY_SIZE_CHANGED and UNIT_QUEST_LOG_CHANGED events
DISPLAY_SIZE_CHANGED fires when the window is resized via
SDL_WINDOWEVENT_RESIZED, allowing UI addons to adapt their layout
to the new screen dimensions (5 FrameXML registrations).

UNIT_QUEST_LOG_CHANGED("player") fires alongside QUEST_LOG_UPDATE
at all 6 quest log modification points, for addons that register
for this variant instead (4 FrameXML registrations).
2026-03-22 19:29:06 -07:00
Kelsi
e9ce062112 fix: restore correct CharSections.dbc field indices for character textures
PR #19 (572bb4ef) swapped CharSections.dbc field indices, placing
Texture1-3 at fields 4-6 and VariationIndex/ColorIndex at 8-9. Binary
analysis of the actual DBC files (Classic, TBC, Turtle — all identical
layout, no WotLK-specific override) confirms the correct order is:

  Field 4 = VariationIndex
  Field 5 = ColorIndex
  Field 6 = Texture1 (string)
  Field 7 = Texture2 (string)
  Field 8 = Texture3 (string)
  Field 9 = Flags

With the wrong indices, VariationIndex/ColorIndex reads returned string
offsets (garbage values that never matched), so all CharSections lookups
failed silently — producing white untextured character models at the
login screen and in-world.

Fixes all 4 expansion JSON layouts, hardcoded fallbacks in
character_preview.cpp, application.cpp, and character_create_screen.cpp.
Also handles the single-layer edge case (body skin only, no face/underwear)
by loading the texture directly instead of skipping compositing.
2026-03-22 15:22:25 -07:00
Paul
027640189a make start on ubuntu intel video cards 2026-03-22 21:47:12 +03:00
Kelsi
a4c8fd621d fix: use geoset 503 for bare shins to reduce knee width discontinuity
Change the bare shin (no boots) default from geoset 502 to 503 across
all four code paths (character creation, character preview, equipment
update, NPC rendering).

Geoset 503 has Y width ~0.44 which better matches the thigh mesh
width (~0.42) than 502's width (~0.39), reducing the visible gap at
the knee joint where lower and upper leg meshes meet.
2026-03-21 13:53:02 -07:00
Kelsi
19b8d31da2 feat: display Lua addon errors as in-game UI errors
Previously Lua addon errors only logged to the log file. Now they
display as red UI error text to the player (same as spell errors and
game warnings), helping addon developers debug issues in real-time.

Add LuaErrorCallback to LuaEngine, fire it from event handler and
frame OnEvent pcall error paths. Wire the callback to GameHandler's
addUIError in application.cpp.
2026-03-21 06:00:06 -07:00
Kelsi
494175e2a7 feat: add remaining CHAT_MSG_* event mappings
Map 5 previously unmapped chat types to addon events:
- CHAT_MSG_MONSTER_PARTY (NPC party chat in dungeons/scripted events)
- CHAT_MSG_AFK (player AFK auto-reply messages)
- CHAT_MSG_DND (player DND auto-reply messages)
- CHAT_MSG_LOOT (loot roll/distribution messages)
- CHAT_MSG_SKILL (skill-up messages)

All WoW chat types in the ChatType enum are now mapped to addon events.
2026-03-21 04:57:19 -07:00
Kelsi
70a5d3240c feat: add ACHIEVEMENT_EARNED event and 15 missing CHAT_MSG_* events
Fire ACHIEVEMENT_EARNED event when a player earns an achievement,
enabling achievement tracking addons.

Add 15 previously unmapped chat type → addon event mappings:
- CHAT_MSG_ACHIEVEMENT, CHAT_MSG_GUILD_ACHIEVEMENT
- CHAT_MSG_WHISPER_INFORM (echo of sent whispers)
- CHAT_MSG_RAID_LEADER, CHAT_MSG_BATTLEGROUND_LEADER
- CHAT_MSG_MONSTER_SAY/YELL/EMOTE/WHISPER
- CHAT_MSG_RAID_BOSS_EMOTE/WHISPER
- CHAT_MSG_BG_SYSTEM_NEUTRAL/ALLIANCE/HORDE

These events are needed by boss mod addons (DBM, BigWigs) to detect
boss emotes, by achievement trackers, and by chat filter addons that
process all message types.
2026-03-21 04:28:15 -07:00
Kelsi
91794f421e feat: add spell power cost to SpellDataResolver; fix IsUsableSpell mana check
Extend SpellDataInfo with manaCost and powerType fields, extracted from
Spell.dbc ManaCost and PowerType columns. This enables IsUsableSpell()
to properly check if the player has enough mana/rage/energy to cast.

Previously IsUsableSpell always returned notEnoughMana=false since cost
data wasn't available. Now it compares the spell's DBC mana cost against
the player's current power, returning accurate usability and mana state.

This fixes action bar addons showing abilities as usable when the player
lacks sufficient power, and enables OmniCC-style cooldown text to
properly dim insufficient-power abilities.
2026-03-21 04:20:58 -07:00
Kelsi
c7e16646fc feat: resolve spell cast time and range from DBC for GetSpellInfo
Add SpellDataResolver that lazily loads Spell.dbc, SpellCastTimes.dbc,
and SpellRange.dbc to provide cast time and range data. GetSpellInfo()
now returns real castTime (ms), minRange, and maxRange instead of
hardcoded 0 values.

This enables spell tooltip addons, cast bar addons (Quartz), and range
check addons to display accurate spell information. The DBC chain is:
  Spell.dbc[CastingTimeIndex] → SpellCastTimes.dbc[Base ms]
  Spell.dbc[RangeIndex] → SpellRange.dbc[MinRange, MaxRange]

Follows the same lazy-loading pattern as SpellIconPathResolver and
ItemIconPathResolver.
2026-03-21 04:16:12 -07:00
Kelsi
7105672f06 feat: resolve item icon paths from ItemDisplayInfo.dbc for Lua API
Add ItemIconPathResolver that lazily loads ItemDisplayInfo.dbc to map
displayInfoId → icon texture path. This fixes three Lua API functions
that previously returned nil for item icons:

- GetItemInfo() field 10 (texture) now returns the icon path
- GetActionTexture() for item-type action bar slots now returns icons
- GetLootSlotInfo() field 1 (texture) now returns proper item icons
  instead of incorrectly using the spell icon resolver

Follows the same lazy-loading pattern as SpellIconPathResolver. The DBC
is loaded once on first query and cached for all subsequent lookups.
2026-03-21 02:53:07 -07:00
Kelsi
e21f808714 feat: support SavedVariablesPerCharacter for per-character addon data
Implement the SavedVariablesPerCharacter TOC directive that many addons
use to store different settings per character (Bartender, Dominos,
MoveAnything, WeakAuras, etc.). Without this, all characters share the
same addon data file.

Per-character files are stored as <AddonName>.<CharacterName>.lua.saved
alongside the existing account-wide <AddonName>.lua.saved files. The
character name is resolved from the player GUID at world entry time.

Changes:
- TocFile::getSavedVariablesPerCharacter() parses the TOC directive
- AddonManager loads/saves per-character vars alongside account-wide vars
- Character name set from game handler before addon loading
2026-03-21 02:46:21 -07:00
Kelsi
4b3e377add feat: resolve random property/suffix names for item display
Load ItemRandomProperties.dbc and ItemRandomSuffix.dbc lazily to resolve
suffix names like "of the Eagle", "of the Monkey" etc. Add
getRandomPropertyName(id) callback on GameHandler wired through Application.
Append suffix to item names in SMSG_ITEM_PUSH_RESULT loot notifications
so items display as "Leggings of the Eagle" instead of just "Leggings".
2026-03-20 19:18:30 -07:00
Kelsi
2b99011cd8 fix: cap gossipPois_ vector growth and add soft frame rate limiter
Cap gossipPois_ at 200 entries (both gossip POI and quest POI paths) to
prevent unbounded memory growth from rapid gossip/quest queries. Add soft
240 FPS frame rate limiter when vsync is off to prevent 100% CPU usage —
sleeps for remaining frame budget when frame completes in under 4ms.
2026-03-20 18:51:05 -07:00
Kelsi
71837ade19 feat: show zone name on loading screen during world transitions
Add setZoneName() to LoadingScreen and display the map name from Map.dbc
as large gold text with drop shadow above the progress bar. Shown in both
render() and renderOverlay() paths. Zone name is resolved from gameHandler's
getMapName(mapId) during world load. Improves feedback during zone transitions.
2026-03-20 18:12:23 -07:00
Kelsi
50ca4f71f9 fix: correct NPC equipment geoset group assignments for gloves and boots
NPC character models used wrong geoset groups: gloves were group 3 (300s)
instead of group 4 (400s), boots were group 4 (400s) instead of group 5
(500s), matching the character preview code. Also remove spurious "torso"
geoset from group 5 (conflicted with boots) — chest armor controls only
group 8 (sleeves), not a separate torso visibility group. Fixes NPC
equipment rendering with incorrect body part meshes.
2026-03-20 15:15:15 -07:00
Kelsi
66f779c186 feat: add zone change and login sequence events for Lua addons
Fire ZONE_CHANGED_NEW_AREA and ZONE_CHANGED when worldStateZoneId changes
in SMSG_INIT_WORLD_STATES. Add VARIABLES_LOADED and PLAYER_LOGIN events in
the addon loading sequence (before PLAYER_ENTERING_WORLD), and fire
PLAYER_ENTERING_WORLD on subsequent world entries (teleport, instance).
Enables zone-aware addons like DBM and quest trackers.
2026-03-20 15:05:29 -07:00
Kelsi
22b0cc8a3c feat: add GetSpellInfo, GetSpellTexture, GetItemInfo and more Lua API functions
Add spell icon path resolution via SpellIcon.dbc + Spell.dbc lazy loading,
wired through GameHandler callback. Fix UnitBuff/UnitDebuff to return icon
texture paths instead of nil. Add GetLocale, GetBuildInfo, GetCurrentMapAreaID.
2026-03-20 13:58:54 -07:00
Kelsi
062cfd1e4a feat: add SavedVariables persistence for Lua addons
Addons can now persist data across sessions using the standard WoW
SavedVariables pattern:

1. Declare in .toc: ## SavedVariables: MyAddonDB
2. Use the global in Lua: MyAddonDB = MyAddonDB or {default = true}
3. Data is automatically saved on logout and restored on next login

Implementation:
- TocFile::getSavedVariables() parses comma-separated variable names
- LuaEngine::loadSavedVariables() executes saved .lua file to restore globals
- LuaEngine::saveSavedVariables() serializes Lua tables/values to valid Lua
- Serializer handles tables (nested), strings, numbers, booleans, nil
- Save triggered on PLAYER_LEAVING_WORLD and AddonManager::shutdown()
- Files stored as <AddonDir>/<AddonName>.lua.saved

Updated HelloWorld addon to track login count across sessions.
2026-03-20 12:22:50 -07:00
Kelsi
1f8660f329 feat: add OnUpdate frame script for per-frame addon callbacks
Frames can now set an OnUpdate script that fires every frame with
the elapsed time as an argument. This enables addon timers, polling,
and animations.

  local f = CreateFrame("Frame")
  f:SetScript("OnUpdate", function(self, elapsed)
      -- called every frame with deltaTime
  end)

OnUpdate only fires for visible frames (frame:Hide() pauses it).
Tracked in __WoweeOnUpdateFrames table, dispatched via
LuaEngine::dispatchOnUpdate() called from the Application main loop.
2026-03-20 12:07:22 -07:00
Kelsi
269d9e2d40 feat: fire PLAYER_TARGET_CHANGED and PLAYER_LEVEL_UP addon events
Add a generic AddonEventCallback to GameHandler for firing named events
with string arguments directly from game logic. Wire it to the addon
system in Application.

New events fired:
- PLAYER_TARGET_CHANGED — when target is set or cleared
- PLAYER_LEVEL_UP(newLevel) — on level up

The generic callback pattern makes it easy to add more events from
game_handler.cpp without touching Application/AddonManager code.
Total addon events: 16 (2 world + 12 chat + 2 gameplay).
2026-03-20 11:51:46 -07:00
Kelsi
0a0ddbfd9f feat: fire CHAT_MSG_* events to Lua addons for all chat types
Wire chat messages to the addon event system via AddonChatCallback.
Every chat message now fires the corresponding WoW event:

- CHAT_MSG_SAY, CHAT_MSG_YELL, CHAT_MSG_WHISPER
- CHAT_MSG_PARTY, CHAT_MSG_GUILD, CHAT_MSG_OFFICER
- CHAT_MSG_RAID, CHAT_MSG_RAID_WARNING, CHAT_MSG_BATTLEGROUND
- CHAT_MSG_SYSTEM, CHAT_MSG_CHANNEL, CHAT_MSG_EMOTE

Event handlers receive (eventName, message, senderName) arguments.
Addons can now filter, react to, or log chat messages in real-time.
2026-03-20 11:29:53 -07:00
Kelsi
510f03fa32 feat: add WoW event system for Lua addons
Implement the WoW-compatible event system that lets addons react to
gameplay events in real-time:

- RegisterEvent(eventName, handler) — register a Lua function for an event
- UnregisterEvent(eventName, handler) — remove a handler
- fireEvent() dispatches events to all registered handlers with args

Currently fired events:
- PLAYER_ENTERING_WORLD — after addons load and world entry completes
- PLAYER_LEAVING_WORLD — before logout/disconnect

Events are stored in a __WoweeEvents Lua table, dispatched via
LuaEngine::fireEvent() which is called from AddonManager::fireEvent().
Error handling logs Lua errors without crashing.

Updated HelloWorld addon to use RegisterEvent for world entry/exit.
2026-03-20 11:23:38 -07:00
Kelsi
290e9bfbd8 feat: add Lua 5.1 addon system with .toc loader and /run command
Foundation for WoW-compatible addon support:

- Vendor Lua 5.1.5 source as a static library (extern/lua-5.1.5)
- TocParser: parses .toc files (## directives + file lists)
- LuaEngine: Lua 5.1 VM with sandboxed stdlib (no io/os/debug),
  WoW-compatible print() that outputs to chat, GetTime() stub
- AddonManager: scans Data/interface/AddOns/ for .toc files,
  loads .lua files on world entry, skips LoadOnDemand addons
- /run <code> slash command for inline Lua execution
- HelloWorld test addon that prints to chat on load

Integration: AddonManager initialized after asset manager, addons
loaded once on first world entry, reset on logout. XML frame
parsing is deferred to a future step.
2026-03-20 11:12:07 -07:00
Kelsi
6d9adc547a fix: extend world-load animation callbacks to handle online players
loadOnlineWorldTerrain re-registers the death/respawn/swing callbacks,
overriding the ones from setupUICallbacks. The world-load versions only
checked creatureInstances_, so the player lookup fix from the previous
commit was silently reverted whenever the world loaded. Now both
registration sites check playerInstances_ as a fallback.
2026-03-18 08:52:00 -07:00
Kelsi
100d66d18b fix: play death/attack animations for online players, not just NPCs
Death, respawn, and melee swing callbacks only checked
creatureInstances_, so online players never played death animation when
killed, never returned to idle on resurrect, and never showed attack
swings. Extended all three callbacks to also check playerInstances_.

Also extended the game_handler death/respawn callback triggers to fire
for PLAYER entities (not just UNIT), and added spawn-time death
detection for players that are already dead when first seen.
2026-03-18 08:43:19 -07:00
Kelsi
a619f44dfb fix: add per-frame animation sync for online players
Online players had no animation state machine — once Run started from a
movement packet, it never transitioned back to Stand/Idle. This mirrors
the creature sync loop: position, orientation, and locomotion animation
(Run/Walk/Swim/Fly ↔ Stand/SwimIdle/FlyIdle) are now driven per-frame
based on Entity::isActivelyMoving() state transitions.

Also cleans up creatureRenderPosCache_ on player despawn.
2026-03-18 08:33:45 -07:00
Kelsi
18c06d98ac fix: stop creature run animation when movement interpolation completes
Creatures were stuck in Run/Walk animation during the dead-reckoning
overrun window (up to 2x movement duration). The animation check used
isEntityMoving() which stays true through dead reckoning, causing
creatures to "run in place" after reaching their destination.

Add isActivelyMoving() which is true only during the active
interpolation phase (moveElapsed < moveDuration), and use it for
animation state transitions. Dead reckoning still works for position
extrapolation — only the animation now correctly stops at arrival.
2026-03-18 08:22:50 -07:00
Kelsi
eca570140a fix: eliminate 8-second teleport freeze on same-map teleport
Replace processAllReadyTiles() with bounded processReadyTiles() in the
same-map teleport and reconnect paths. processAllReadyTiles finalizes
every pending tile synchronously with a GPU sync wait, which caused
8+ second main-thread stalls when many tiles were queued. The bounded
version processes 1-4 tiles per call with async GPU upload — remaining
tiles finalize incrementally over subsequent frames.
2026-03-18 07:54:05 -07:00
Kelsi
586408516b fix: correct character geoset group ranges for other-player equipment rendering
setOnlinePlayerEquipment used wrong geoset ID ranges for boots (402+ instead
of 501+), gloves (301+ instead of 401+), and chest/sleeves (501+ instead of
801+), and was missing bare-shin (502), bare-wrist (801), and bare-leg (1301)
defaults. This caused other players to render with missing shin/wrist geometry
and wrong geosets when wearing equipment (the "shin mesh" gap in status.md).

Now mirrors the CharacterPreview::applyEquipment logic exactly:
- Group 4 (4xx) forearms/gloves: default 401, equipment 401+gg
- Group 5 (5xx) shins/boots:    default 502, equipment 501+gg
- Group 8 (8xx) wrists/sleeves: default 801, equipment 801+gg
- Group 13 (13xx) legs/pants:   default 1301, equipment 1301+gg
2026-03-18 04:42:21 -07:00
Kelsi
cf3fe70f1f fix: hide window on shutdown to prevent OS force-close dialog
SDL_HideWindow immediately on shutdown so the OS doesn't show a
"not responding" dialog during the slow cleanup process.
2026-03-17 09:04:47 -07:00
Kelsi
b0fafe5efa fix: stabilize turtle world entry session handling 2026-03-15 01:21:23 -07:00
Kelsi
075b4c1772 fix(gameplay): tighten TB elevator bounds and reset stale combat visuals
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
2026-03-14 09:19:16 -07:00
Kelsi
38210ec186 fix(gameplay): keep timeout animation stable on repeated presses and harden M2 elevator sync 2026-03-14 09:02:20 -07:00
Kelsi
cebca9a882 fix(gameplay): stabilize run animation and clean ghost/death visuals 2026-03-14 08:27:32 -07:00
Kelsi
1a4b21955c fix(transport): add Thunder Bluff lift-specific M2 attach bounds 2026-03-14 08:09:23 -07:00
Kelsi
c1baffadf0 fix(input): release mouse on stalls and clean quest keybind duplication 2026-03-14 07:29:39 -07:00
Kelsi
85767187b1 fix: clear gameObjectDisplayIdWmoCache_ on world transition, add stale-entry guard
gameObjectDisplayIdWmoCache_ was not cleared on world unload/transition,
causing stale WMO model IDs (e.g. 40006, 40003) to be looked up after
the renderer cleared its model list, resulting in "Cannot create instance
of unloaded WMO model" errors on zone re-entry.

Changes:
- Clear gameObjectDisplayIdWmoCache_ alongside other GO caches on world reset
- Add WMORenderer::isModelLoaded() for cache-hit validation
- Inline GO WMO path now verifies cached model is still renderer-resident
  before using it; evicts stale entries and falls back to reload
2026-03-13 03:43:55 -07:00
Kelsi
0487d2eda6 fix: check loadModel return before createInstance for WMO doodads
When the M2 model cache is full (>6000 entries), loadModel() returns
false and the model is never added to the GPU cache. The WMO instance
doodad path was calling createInstanceWithMatrix() unconditionally,
generating hundreds of "Cannot create instance: model X not loaded"
warnings on zone entry. Add the same guard already present in the
terrain doodad path.
2026-03-13 03:41:42 -07:00
Kelsi
8f3f1b21af fix: check vertices before skin load so WotLK (v264) character M2s parse correctly
TBC races like Draenei use version-264 M2 files with no embedded skin;
indices come from a separate .skin file loaded after M2::load().
The premature isValid() check (which requires non-empty indices) always
failed for WotLK-format character models, making Draenei (and Blood Elf)
players invisible.

Fix: only check vertices.empty() right after load(), then validate fully
with isValid() after the skin file is loaded.
2026-03-13 02:58:42 -07:00
Kelsi
d4bf8c871e fix: clear gameObjectDisplayIdFailedCache_ on world reset and zone change
The failed-model cache introduced in f855327 would persist across map
changes, permanently suppressing models that failed on one map but might
be valid assets on another (or after a client update). Clear it in the
world reset path alongside the existing gameObjectDisplayIdModelCache_
clear, so model loads get a fresh attempt on each zone change.
2026-03-13 01:53:59 -07:00
Kelsi
d58c55ce8d fix: allow ribbon-only M2 models to load and silence transport doodad load errors
Two follow-up fixes for the ribbon emitter implementation and the
transport-doodad stall fix:

1. loadModel() rejected any M2 with no vertices AND no particles, but
   ribbon-only spell-effect models (e.g. weapon trail or aura ribbons)
   have neither. These models were silently invisible even though the
   ribbon rendering pipeline added in 1108aa9 is fully capable of
   rendering them. Extended the guard to also accept models that have
   ribbon emitters, matching the particle-emitter precedent.

2. processPendingTransportDoodads() ignored the bool return of
   loadModel(), calling createInstance() even when the model was
   rejected, generating spurious "Cannot create instance: model X not
   loaded" warnings for every failed doodad path. Check the return
   value and continue to the next doodad on failure.
2026-03-13 01:49:22 -07:00
Kelsi
f855327054 fix: eliminate 490ms transport-doodad stall and GPU device-loss crash
Three root causes identified from wowee.log crash at frame 134368:

1. processPendingTransportDoodads() was doing N separate synchronous
   GPU uploads (vkQueueSubmit + vkWaitForFences per texture per doodad).
   With 30+ doodads × multiple textures, this caused the 489ms stall in
   the 'gameobject/transport queues' update stage. Fixed by wrapping the
   entire batch in beginUploadBatch()/endUploadBatch() so all texture
   layout transitions are submitted in a single async command buffer.

2. Game objects whose M2 model has no geometry/particles (empty or
   unsupported format) were retried every frame because loadModel()
   returns false without adding to gameObjectDisplayIdModelCache_.
   Added gameObjectDisplayIdFailedCache_ to permanently skip these
   display IDs after the first failure, stopping the per-frame spam.

3. renderM2Ribbons() only checked ribbonPipeline_ != null, not
   ribbonAdditivePipeline_. If additive pipeline creation failed, any
   ribbon with additive blending would call vkCmdBindPipeline with
   VK_NULL_HANDLE, causing VK_ERROR_DEVICE_LOST on the GPU side.
   Extended the early-return guard to cover both ribbon pipelines.
2026-03-13 01:45:31 -07:00
Kelsi
a1edddd1f0 feat: open dungeon finder UI when server sends SMSG_OPEN_LFG_DUNGEON_FINDER
Previously SMSG_OPEN_LFG_DUNGEON_FINDER was consumed silently with no UI
response. Now it fires an OpenLfgCallback wired to openDungeonFinder() on
the GameScreen, so the dungeon finder window opens as the server requests.
2026-03-12 21:24:42 -07:00
Kelsi
9aa4b223dc feat: implement SMSG_CAMERA_SHAKE with sinusoidal camera shake effect
- Add triggerShake(magnitude, frequency, duration) to CameraController
- Apply envelope-decaying sinusoidal XYZ offset to camera in update()
- Handle SMSG_CAMERA_SHAKE opcode in GameHandler dispatch
- Translate shakeId to magnitude (minor <50: 0.04, larger: 0.08 world units)
- Wire CameraShakeCallback from GameHandler through to CameraController
- Shake uses 18Hz oscillation with 30% fade-out envelope at end of duration
2026-03-12 19:37:53 -07:00
Kelsi
6426bde7ea feat: enhance NPC tabard rendering with ItemDisplayInfo.dbc variant lookup
Look up tabard display ID from CreatureDisplayInfoExtra and map to
geoset variant via ItemDisplayInfo.dbc to select correct tabard
meshes. Falls back to hardcoded 1201 if DBC lookup unavailable.
Improves NPC appearance variety with proper scope handling.
2026-03-11 12:01:45 -07:00
Kelsi
9578e123cc fix: revert tabard DBC enhancement due to scope issue
The itExtra variable is not in scope at the tabard rendering site.
Reverted to original hardcoded 1201 fallback which is working reliably.
DBC variant approach requires refactoring variable scope.
2026-03-11 10:39:35 -07:00
Kelsi
71597c9a03 feat: enhance NPC tabard rendering to use ItemDisplayInfo.dbc variants
Reads equipped tabard display ID from CreatureDisplayInfoExtra (slot 9)
and looks up the corresponding geoset group in ItemDisplayInfo.dbc to
select the correct tabard variant. Falls back to hardcoded 1201 if DBC
unavailable. Improves NPC appearance variety without risky features.
2026-03-11 10:37:41 -07:00
Kelsi
589ec3c263 refactor: consolidate duplicate NPC helmet attachment code paths
Remove redundant helmet attachment code path (lines 6490-6566) that was
disabled and inferior to the main path. The main path (enabled in Loop 25)
provides better fallback logic by trying attachment points 0 and 11,
includes proper logging, and has undergone validation.

This consolidation reduces code duplication by 78 lines, improves
maintainability, and eliminates potentially wasteful spawn-time overhead
from the disabled path.
2026-03-11 10:14:49 -07:00