Commit graph

128 commits

Author SHA1 Message Date
Kelsi
08500384e2 refactor: migrate all remaining JSON to nlohmann/json
- npc_spawner: save/load with proper JSON (25+ fields + patrol paths)
- zone_manifest: save/load with nlohmann (was naive string concat/parse)
  - load now parses all fields: mapId, baseHeight, tiles, hasCreatures
- custom_zone_discovery: parse zone.json with nlohmann, extract mapId
  and tile coordinates (was only reading name/author/description)
- object_placer: save/load with nlohmann (was substring parsing)
- editor_app: stats.json export uses nlohmann, score display now /6

Zero naive JSON string concatenation remains in the editor codebase.
2026-05-05 13:10:07 -07:00
Kelsi
815787933b feat: WHM alpha maps + nlohmann/json for WOT format
- WHM binary now includes per-chunk alpha map data (alphaSize + data)
  so custom zones render with proper texture blending in the client
- WOT exporter rewritten with nlohmann/json (was manual string concat)
- WOT loader rewritten with nlohmann/json (was naive substring parsing)
- Backward compatible: old WHM files without alpha data still load fine
2026-05-05 13:04:51 -07:00
Kelsi
4fc0361f7a feat: complete client integration for all 6 open formats
- Wire WOB buildings into WMO render pipeline (loads→converts→renders)
- Implement JSON DBC loading in DBCFile::loadJSON() with nlohmann/json
- Wire JSON DBC override into AssetManager (custom_zones/output scan)
- Add WMO→WOB conversion with full geometry (fromWMO)
- Replace placeholder WOB export with real WMO→WOB conversion in editor
- Add --convert-wmo CLI flag for batch WMO→WOB conversion
- Store discovered custom zones on Renderer with getCustomZones() accessor
- Add isCustomZone_ member to TerrainManager

All 6 Blizzard format replacements now fully load in the client:
  ADT→WOT/WHM, WDT→zone.json, BLP→PNG, DBC→JSON, M2→WOM, WMO→WOB
2026-05-05 12:41:19 -07:00
Kelsi
01d3638835 feat: WOB→WMO conversion and loading in client terrain manager
- WoweeBuildingLoader::toWMOModel() converts WOB groups to WMOModel
  with vertices, indices, normals, texCoords, and vertex colors
- TerrainManager now loads WOB files from custom_zones/buildings/
  and converts to WMOModel for the WMO renderer pipeline
- WMOGroup indices converted from uint32 to uint16 for renderer compat

Client open format support — 4 of 6 now loading:
- FULL: WOT/WHM terrain, PNG textures, WOM models
- LOAD: WOB buildings (converts to WMOModel, render pipeline TODO)
- DETECT: zone.json (scanned), JSON DBC (scanned)
2026-05-05 12:12:26 -07:00
Kelsi
2d417aa125 feat: client detects WOB buildings and JSON DBCs from custom zones
- TerrainManager now checks for .wob files before loading WMO buildings
  (searches custom_zones/buildings/ and output/MapName/buildings/)
- AssetManager::loadDBC() scans custom_zones/*/data/ for JSON DBC
  overrides exported by the editor
- WOB detection logs when found (full WOB→WMOModel conversion pending)
- JSON DBC detection logs when found (full JSON→DBCFile loading pending)

Client open format support status:
- WOT/WHM terrain: FULL (loads and renders)
- PNG textures: FULL (override system)
- WOM models: FULL (loads and renders)
- zone.json: DETECTION (CustomZoneDiscovery scans)
- WOB buildings: DETECTION (found, conversion pending)
- JSON DBC: DETECTION (found, loading pending)
2026-05-05 12:00:31 -07:00
Kelsi
71c3eb0fe6 feat: Wowee Open Building format (.wob) — novel WMO replacement
ALL 6 BLIZZARD FORMATS NOW HAVE OPEN REPLACEMENTS.

WOB format: binary building file with groups, portals, and doodads.
- WOB1 magic header
- Groups: vertices (pos+normal+uv+color), indices, texture paths,
  bounding box, indoor/outdoor flag
- Portals: connect groups with polygon boundaries
- Doodad placements: reference .wom models with transform

WoweeBuildingLoader: load/save/exists for .wob files.

Complete format replacement table:
- ADT → WOT/WHM (terrain heightmaps + metadata)
- WDT → zone.json (map definition)
- BLP → PNG (textures)
- DBC → JSON (data tables)
- M2 → WOM (static models)
- WMO → WOB (buildings with groups/portals/doodads)

The wowee project now has a complete suite of novel, open file
formats for creating and distributing custom WoW content without
any dependency on Blizzard proprietary file formats.
2026-05-05 10:28:24 -07:00
Kelsi
b4cb833108 feat: Wowee Open Model format (.wom) — novel M2 replacement
WOM format: binary model file with no Blizzard structures.
- WOM1 magic header + vertex/index counts + bounding box
- Vertices: position(vec3) + normal(vec3) + texCoord(vec2) = 32 bytes
- Indices: uint32 triangle list
- Texture paths: PNG references (not BLP)

WoweeModelLoader:
- load(): reads .wom binary back to WoweeModel struct
- save(): writes WoweeModel to .wom binary
- fromM2(): converts existing M2 models to WOM (static geometry,
  strips bone/animation data, converts BLP paths to PNG)
- exists(): checks for .wom file

Format replacement progress — 5 out of 6 done:
- DONE: ADT → WOT/WHM (terrain)
- DONE: WDT → zone.json (map definition)
- DONE: BLP → PNG (textures)
- DONE: DBC → JSON (data tables)
- DONE: M2 → WOM (static models)
- TODO: WMO → open building format
2026-05-05 10:24:46 -07:00
Kelsi
d10d962e31 feat: custom zone discovery system for client auto-detection
- CustomZoneDiscovery scans directories for zone.json manifest files
- Discovers custom zones in custom_zones/ and output/ directories
- Reports: name, author, description, creature/quest availability
- Client can list all available custom expansions at startup
- Foundation for a zone selection menu in the client UI
2026-05-05 10:01:05 -07:00
Kelsi
954894460e feat: integrate Wowee Open Terrain loader into client terrain pipeline
The wowee client can now load custom zones exported from the editor
using the novel WOT/WHM format — no Blizzard files needed.

Loading priority in TerrainManager::prepareTile():
1. Check custom_zones/{mapName}/{mapName}_{x}_{y}.wot/.whm
2. Check output/{mapName}/{mapName}_{x}_{y}.wot/.whm (editor output)
3. Fall back to World\Maps\...\*.adt (standard extracted data)

Pipeline:
- WoweeTerrainLoader in src/pipeline/ (shared between client + editor)
- Loads .whm binary heightmap (WHM1 magic, 256 chunks × 145 floats)
- Loads .wot JSON metadata (textures, layers, holes, water)
- Populates the same ADTTerrain struct the mesh generator uses
- obj0 merge only runs for ADT-loaded tiles (custom zones have no obj0)

To use: export zone from editor → files appear in output/ → client
loads them automatically on next terrain request for that map name.
2026-05-05 09:56:24 -07:00
Pavel Okhlopkov
b79d9b8fea feat(rendering): implement spell visual effects with bone-tracked ribbons and particles
Add complete spell visual pipeline resolving the DBC chain
(Spell → SpellVisual → SpellVisualKit → SpellVisualEffectName → M2)
with precast/cast/impact phases, bone-attached positioning, and
automatic dual-hand mirroring.

Ribbon rendering fixes:
- Parse visibility track as uint8 (was read as float, suppressing
  all ribbon edges due to ~1.4e-45 failing the >0.5 check)
- Filter garbage emitters with bone=UINT_MAX unconditionally
- Guard against NaN spine positions from corrupt bone data
- Resolve ribbon textures via direct index, not textureLookup table
- Fall back to bone 0 when ribbon bone index is out of range

Particle rendering fixes:
- Reduce spell particle scale from 5x to 1.5x (was oversized)
- Exempt spell effect instances from position-based deduplication

Spell handler integration:
- Trigger precast visuals on SMSG_SPELL_START with server castTimeMs
- Trigger cast/impact visuals on SMSG_SPELL_GO
- Cancel precast visuals on cast interrupt/failure/movement

M2 classifier expansion:
- Add AmbientEmitterType enum for sound system integration
- Add 20+ foliage tokens, 4 spell effect tokens, isSmallFoliage flag
- Add markModelAsSpellEffect() to override disableAnimation

DBC layouts:
- Add SpellVisualID field to Spell.dbc for all expansion configs

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
2026-04-07 11:27:59 +03:00
Kelsi
069dd36698 fix(parsing): bail on suspicious maskBlockCount in CREATE_OBJECT blocks
When spline parsing consumes the wrong number of bytes, the subsequent
blockCount read lands on garbage data (e.g. 71 instead of ~5 for UNIT).
Previously the parser logged a warning but continued, reading garbage
mask/field data until hitting truncation. Now it returns false for
CREATE_OBJECT blocks with suspicious counts, letting the block loop
skip cleanly to the next entity.

Also downgrade ~44 diagnostic LOG_WARNING messages to LOG_DEBUG across
17 files (equipment, transport, DBC, heartbeat, chat, GO raypick, etc.)
to reduce log noise and make real warnings visible.
2026-04-05 20:12:17 -07: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
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
Kelsi
1e06ea86d7 chore: remove dead code, document water shader wave parameters
- Delete 4 legacy GLSL 330 shaders (basic.vert/frag, terrain.vert/frag)
  left over from OpenGL→Vulkan migration — Vulkan equivalents exist as
  *.glsl files compiled to SPIR-V by the build system
- Delete orphaned mpq_manager.hpp/cpp (694 lines) — not in CMakeLists,
  not included by any file, unreferenced StormLib integration attempt
- Add comments to water.frag.glsl wave constants explaining the
  multi-octave noise design: non-axis-aligned directions prevent tiling,
  frequency increases and amplitude decreases per octave for natural
  water appearance
2026-03-30 18:50:14 -07:00
Kelsi
548828f2ee refactor: extract color write mask, name frustum epsilon, add comments
- vk_pipeline: extract kColorWriteAll constant from 4 duplicated RGBA
  bitmask expressions across blend mode functions, with why-comment
- frustum: name kMinNormalLenSq epsilon (1e-8) with why-comment —
  prevents division by zero on degenerate planes
- dbc_loader: add why-comment on DBC field width validation — all
  fields are fixed 4-byte uint32 per format spec
- pin_auth: replace 0x30 hex literal with '0' char constant, add
  why-comment on ASCII encoding for server HMAC compatibility
2026-03-30 15:02:47 -07:00
Kelsi
ef787624fe refactor: name M2 sequence flag, replace empty loop with std::advance
- m2_loader: define kM2SeqFlagEmbeddedData (0x20) with why-comment —
  when clear, keyframe data lives in external .anim files and M2 offsets
  are file-relative (reading them from M2 produces garbage). Replaces
  3 bare hex literals across parseAnimTrack and ribbon emitter parsing
- audio_engine: replace empty for-loop iterator advance with
  std::advance() for clarity
2026-03-30 14:59:03 -07:00
Kelsi
086f32174f fix: guard fsPath underflow, name WMO doodad mask, add why-comments
- asset_manager: add size guard before fsPath.substr(size-4) in
  tryLoadPngOverride — resolveFile could theoretically return a
  path shorter than the extension
- wmo_loader: name kDoodadNameIndexMask (0x00FFFFFF) with why-comment
  explaining the 24-bit name index / 8-bit flags packing and MODN
  string table reference
- window: add why-comment on LOG_WARNING usage during shutdown —
  intentionally elevated so teardown progress is visible at default
  log levels for crash diagnosis
2026-03-30 14:33:08 -07:00
Kelsi
1151785381 refactor: name ADT vertex constants, add BLP decompression comments
- adt_loader: replace magic 145 with kMCVTVertexCount and 17 with
  kMCVTRowStride — MCVT height grid is 9 outer + 8 inner vertices
  per row across 9 rows
- adt_loader: replace 999999.0f sentinels with numeric_limits
- blp_loader: add why-comments on RGB565→RGB888 bit layout
  (R=bits[15:11], G=[10:5], B=[4:0])
- blp_loader: explain DXT3 4-bit alpha scaling (n * 255 / 15)
- blp_loader: explain palette 4-bit alpha multiply-by-17 trick
  (equivalent to n * 255 / 15, exact for all 16 values)
2026-03-30 14:28:22 -07:00
Kelsi
16aaf58198 fix: M2 readString uint32 overflow in bounds check
offset + length was computed in uint32_t before comparing to size_t.
A crafted M2 with offset=0xFFFFFFFF, length=2 wraps to 1 in uint32,
passing the check and reading out of bounds. Now uses size_t arithmetic,
matching the readArray fix from an earlier round.
2026-03-29 20:41:56 -07:00
Kelsi
fa1643dc90 fix: WMO readArray integer overflow in bounds check
count * sizeof(T) was computed in uint32_t — a large count value from a
crafted WMO file could wrap to a small number, pass the bounds check,
then attempt a multi-GB allocation causing OOM/crash. Now uses 64-bit
arithmetic with a 64MB sanity cap, matching the M2 loader pattern.
2026-03-29 20:32:47 -07:00
Kelsi
f02be1ffac fix: tolower/toupper UB on signed char at 10 remaining call sites
Final sweep across mpq_manager, application, auth_screen, wmo_renderer,
character_renderer, and terrain_manager. All now use the unsigned char
cast pattern. No remaining bare ::tolower/::toupper or std::tolower(c)
calls on signed char in the codebase.
2026-03-29 20:27:16 -07:00
Kelsi
a1575ec678 fix: WDT MWMO parser used unbounded strlen on chunk data
std::strlen on raw MWMO chunk data has no upper bound if the chunk
lacks a null terminator (truncated/corrupt WDT file). Replaced with
strnlen bounded by chunkSize, matching the ADT parser fix in d776226f.
2026-03-29 20:26:58 -07:00
Kelsi
7f5cad63cd fix: WMO group debug log throttle was per-process, not per-model
static int logCount/batchLogCount inside the per-group parse loop
accumulated globally, so after the first WMO with many sub-chunks
loaded, no subsequent WMO group would ever log. Changed to function-
local / loop-index-based throttle so each group gets its own window.
2026-03-29 20:14:53 -07:00
Kelsi
568a14852d fix: WMO MODS parser raw memcpy without bounds check
The doodad set name read used raw memcpy(20 bytes) bypassing the safe
read<T> template that returns {} on OOB. A truncated WMO file would
read past the vector's storage. Added bounds check before the memcpy.
2026-03-29 20:05:37 -07:00
Kelsi
b5fba65277 fix: BLP loader OOB read on ARGB8888 and signed overflow on dimensions
ARGB8888 decompression read pixelCount*4 bytes from mipData without
checking that mipSize was large enough — a truncated BLP caused heap
OOB reads. Also, 'int pixelCount = width * height' overflowed for
large dimensions (signed int UB). Now validates dimensions <= 4096,
uses uint32_t arithmetic, and checks mipSize >= required for ARGB8888.
2026-03-29 20:05:29 -07:00
Kelsi
d776226fd1 fix: ADT parser OOB reads on sub-chunk headers and unterminated strings
1. MCNK sub-chunk bounds checks didn't account for the 8-byte header
   skip, so parseMCVT/parseMCNR could read up to 8 bytes past the
   validated buffer when sub-chunk headers are present (the common case).

2. parseMTEX/parseMMDX/parseMWMO used unbounded strlen on raw chunk
   data. A truncated file without a null terminator would read past the
   chunk boundary. Replaced with strnlen bounded by remaining size.

Also removes dead debug code: empty magic buffer copy, cathedral WMO
search, and Stormwind placement dump (which also had ::toupper UB).
2026-03-29 19:58:28 -07:00
Kelsi
3b7ac068d2 perf: hoist key array read out of per-sequence loop in parseAnimTrackVanilla
readArray was called inside the loop on every iteration, re-parsing the
entire flat key array via memcpy. For a model with 200 sequences and
10k keys this produced ~24MB of redundant copying. Now reads once before
the loop (matching how allTimestamps was already handled).
2026-03-29 19:51:17 -07:00
Kelsi
0ee57f4257 fix: FBlock comment said 'CImVector 4 bytes RGBA' but code reads C3Vector
The comment would lead a maintainer to "fix" the working code to read
4-byte RGBA instead of 3-float C3Vector. Updated to match the actual
M2 particle FBlock color format (3 floats, values 0-255, per WoWDev).
2026-03-29 19:43:57 -07:00
Kelsi
c58537e2b8 fix: load binary DBCs from Data/db/ fallback path
CreatureDisplayInfo.dbc (691KB, 24K+ entries) exists at Data/db/ but
the loader only checked DBFilesClient\ (MPQ manifest) and expansion CSV.
The CSV had only 13248 entries (malformed export), so TBC+ creatures
(Mana Wyrms, Blood Elf area) had no display data and were invisible.

Now checks Data/db/ as a fallback for binary DBCs. This path contains
pre-extracted DBCs shared across expansions. Binary DBCs have complete
record data including proper IDs.
2026-03-28 16:17:59 -07:00
Kelsi
e2383725f0 security: path traversal rejection, packet length validation; code quality
Security:
- Asset loader rejects paths containing ".." sequences (path traversal)
- Chat message parser validates length against remaining packet bytes
  before resize(), preventing memory exhaustion from malformed packets

Code quality:
- Extract 11 named geoset constants (kGeosetBareForearms, kGeosetWithCape,
  etc.) replacing ~40 magic number sites across 4 code paths
- Add build-debug/ and .claude/ to .gitignore
- Remove .claude/scheduled_tasks.lock from tracking
2026-03-27 18:42:48 -07:00
Kelsi
b0466e9029 perf: eliminate ~70 unnecessary sqrt ops per frame, optimize caches and threading
Squared distance optimizations across 30 files:
- Convert glm::length() comparisons to glm::dot() (no sqrt)
- Use glm::inversesqrt() for check-then-normalize patterns (1 rsqrt vs 2 sqrt)
- Defer sqrt to after early-out checks in collision/movement code
- Hottest paths: camera_controller (21), weather particles, WMO collision,
  transport movement, creature interpolation, nameplate culling

Container and algorithm improvements:
- std::map<string> → std::unordered_map for asset/DBC/MPQ/warden caches
- std::mutex → std::shared_mutex for asset_manager and mpq_manager caches
- std::sort → std::partial_sort in lighting_manager (top-2 of N volumes)
- Double-lookup find()+operator[] → insert_or_assign in game_handler
- Add reserve() for per-frame vectors: weather, swim_effects, WMO/M2 collision

Threading and synchronization:
- Replace 1ms busy-wait polling with condition_variable in character_renderer
- Move timestamp capture before mutex in logger
- Use memory_order_acquire/release for normal map completion signaling

API additions:
- DBC getStringView()/getStringViewByOffset() for zero-copy string access
- Parse creature display IDs from SMSG_CREATURE_QUERY_SINGLE_RESPONSE
2026-03-27 16:33:16 -07:00
Kelsi
b5b84fbc19 fix: guard texture log dedup sets with mutex for thread safety
loadTexture() is called from terrain worker threads, but the static
unordered_set dedup caches for missing-texture and decode-failure
warnings had no synchronization. Add std::mutex guards around both
log-dedup blocks to prevent data races.
2026-03-27 15:12:36 -07:00
Kelsi
ba99d505dd refactor: remaining C-style casts, color constants, and header guard cleanup
Replace ~37 remaining C-style casts with static_cast across 16 files.
Extract named color constants (kColorRed/Green/Yellow/Gray) and dialog
window flags (kDialogFlags) in game_screen.cpp, replacing 72 inline
literals. Normalize keybinding_manager.hpp to #pragma once.
2026-03-25 11:57:22 -07:00
Kelsi
05f2bedf88 refactor: replace C-style casts with static_cast and extract toLowerInPlace
Replace ~300 C-style casts ((int), (float), (uint32_t), etc.) with
static_cast across 15 source files. Extract toLowerInPlace() helper in
lua_engine.cpp to replace 72 identical tolower loop patterns.
2026-03-25 11:40:49 -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
1108aa9ae6 feat: implement M2 ribbon emitter rendering for spell trail effects
Parse M2RibbonEmitter data (WotLK format) from M2 files — bone index,
position, color/alpha/height tracks, edgesPerSecond, edgeLifetime,
gravity. Add CPU-side trail simulation per instance (edge birth at bone
world position, lifetime expiry, gravity droop). New m2_ribbon.vert/frag
shaders render a triangle-strip quad per emitter using the existing
particleTexLayout_ descriptor set. Supports both alpha-blend and additive
pipeline variants based on material blend mode. Fixes invisible spell
trail effects (~5-10%% of spell visuals) that were silently skipped.
2026-03-13 01:17:30 -07:00
Kelsi
19eb7a1fb7 fix: animation stutter, resolution crash, memory cap, spell tooltip hints, GO collision
- Animation stutter: skip playAnimation(Run) for the local player in the
  server movement callback — the player renderer state machine already manages
  it; resetting animTime on every movement packet caused visible stutter
- Resolution crash: reorder swapchain recreation so old swapchain is only
  destroyed after confirming the new build succeeded; add null-swapchain
  guard in beginFrame to survive the retry window
- Memory cap: reduce cache budget from 80% uncapped to 50% hard-capped at
  16 GB to prevent excessive RAM use on high-memory systems
- Spell tooltip: suppress "Drag to action bar / Double-click to cast" hints
  when the tooltip is shown from the action bar (showUsageHints=false)
- M2 collision: add watermelon/melon/squash/gourd to foliage (no-collision);
  exclude chair/bench/stool/seat/throne from smallSolidProp so invisible chair
  bounding boxes no longer trap the player
2026-03-10 22:26:50 -07:00
Kelsi
0ea8e55ad4 ui,game,pipeline: player nameplates always-on, level-up ring effect, vanilla tile fallback, warden null guard
- Nameplates: player names always rendered regardless of V-key toggle;
  separate cull distance 40u (players/target) vs 20u (NPCs); cyan name
  color for other players; fade alpha scales with cull distance
- Level-up: add expanding golden ring burst (3 staggered waves, 420u
  max radius) + full-screen flash to renderDingEffect(); M2 LevelUp.m2
  is still attempted as a bonus on top
- Vanilla tile loading: add AssetManager::setBaseFallbackPath() so that
  when the primary manifest is an expansion-specific DBC-only subset
  (e.g. Data/expansions/vanilla/), world terrain files fall back to
  the base Data/ extraction; wired in Application::initialize()
- Warden: map a null guard page at address 0x0 in the Unicorn emulator
  so NULL-pointer reads in the module don't crash with UC_ERR_MAP;
  execution continues past the NULL read for better diagnostics
2026-03-10 07:25:04 -07:00
Kelsi
3cdaf78369 game,warden,assets: fix unknown player names, warden heap overlap, and Vanilla Item.dbc
- game: clear pendingNameQueries on player out-of-range and DESTROY_OBJECT so
  re-entering players get a fresh name query instead of being silently skipped
- game: add 5s periodic name resync scan that re-queries players with empty names
  and no pending query, recovering from dropped CMSG_NAME_QUERY responses
- warden: fix UC_ERR_MAP by moving HEAP_BASE from 0x200000 to 0x20000000; the old
  heap [0x200000, 0x1200000) overlapped the module at 0x400000, causing Unicorn to
  reject the heap mapping and abort emulator initialisation
- warden: add early overlap check between module and heap regions to catch future
  layout bugs at init time
- assets: add loadDBCOptional() which logs at DEBUG level when a DBC is absent,
  for files that are not distributed on all expansions
- assets: use loadDBCOptional for Item.dbc (absent on Vanilla 1.12 clients) and
  fall back to server-sent itemInfoCache displayInfoId for NPC weapon resolution
2026-03-10 07:00:43 -07:00
Kelsi
4d1be18c18 wmo: apply MOHD ambient color to interior group lighting
Read the ambient color from the MOHD chunk (BGRA uint32) and store it
on WMOModel as a normalized RGB vec3.  Pass it through ModelData into
the per-batch WMOMaterialUBO (replacing the unused pad[3] bytes, keeping
the struct at 64 bytes).  The GLSL interior branch now floors vertex
colors against the WMO ambient instead of a hardcoded 0.5, so dungeon
interiors respect the artist-specified ambient tint from the WMO root
rather than always clamping to grey.
2026-03-09 21:27:01 -07:00
Kelsi
0a528935e2 Auto-detect WoW locale from data directory; override with WOWEE_LOCALE env 2026-03-09 15:36:26 -07:00
Kelsi
ee4e6a31ce Filter WMO decorative geometry from collision, fix tram portal trigger IDs
Parse MOPY per-triangle flags in WMO groups and exclude detail/decorative
triangles (flag 0x04) from collision detection. This prevents invisible
walls from objects like gears and railings in WMO interiors.

Add WotLK area trigger IDs 2173/2175 to extended-range tram triggers.
2026-03-06 10:37:32 -08:00
Kelsi
08d40583c9 Fix MLIQ water parsing, skip interior water, clear movement on teleport
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
- Remove bogus 2-byte skip after materialId in MLIQ parser that shifted
  all vertex heights and tile flags by 2 bytes (garbage data)
- Skip liquid loading for interior WMO groups (flag 0x2000) to prevent
  indoor water from rendering as outdoor canal water
- Clear movement inputs on teleport/portal to prevent auto-running after
  zone transfer (held keys persist through loading screen)
2026-03-05 15:12:51 -08:00
Kelsi
19652ae521 Add purgeExtractedAssets() to clear all extracted asset data from disk
Some checks failed
Build / Build (arm64) (push) Has been cancelled
Build / Build (x86-64) (push) Has been cancelled
Build / Build (macOS arm64) (push) Has been cancelled
Build / Build (windows-arm64) (push) Has been cancelled
Build / Build (windows-x86-64) (push) Has been cancelled
Security / CodeQL (C/C++) (push) Has been cancelled
Security / Semgrep (push) Has been cancelled
Security / Sanitizer Build (ASan/UBSan) (push) Has been cancelled
2026-02-28 09:07:47 -08:00
Kelsi
543535a892 Add clean.sh reset utility, exclude CSVs from repo, prefer binary DBCs
- Add clean.sh with granular options (--build, --assets, --cache, --csvs, --user, --all, --dry-run)
- Remove 119 CSV files from git tracking and add to .gitignore
- Flip DBC loading order: try binary from extracted MPQs first, CSV as fallback
- Remove forceBinaryDbc workaround and garbled-CSV validation (no longer needed)
2026-02-28 09:04:43 -08:00
Kelsi
16d88f19fc Fix instance portals: WDT byte order, box trigger sizing, suppress ping-pong, WMO cache cleanup
- Fix WDT chunk magic constants to big-endian ASCII (matching ADTLoader)
- Add minimum effective size for box area triggers (90 units, like sphere 45-unit radius)
- Add areaTriggerSuppressFirst_ flag to prevent portal ping-pong on map transfer
- Add WMORenderer::clearAll() to clear models/textures on map change (prevents GPU crash)
- Increase WMO texture cache default to 8GB
- Fix setMapName called after loadTestTerrain so WMO renderer exists
- Save/restore player position around CMSG_AREATRIGGER to prevent bad DB persistence
2026-02-27 04:59:12 -08:00
Kelsi
d0e8b44866 Add instance support: WDT parser, WMO-only map loading, area triggers, BG queue accept
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
- WDT parser detects WMO-only maps (dungeons/raids/BGs) via MPHD flag 0x01
- WMO-only loading branch in loadOnlineWorldTerrain loads root WMO directly
- Area trigger system: loads AreaTrigger.dbc, checks player proximity, sends CMSG_AREATRIGGER
- BG queue acceptance via /join command sending CMSG_BATTLEFIELD_PORT
- SMSG_INSTANCE_DIFFICULTY handler stub
- Map change cleanup (clear old WMO/M2/terrain on map transfer)
- 5-second area trigger cooldown after map transfer to prevent ping-pong loops
2026-02-26 17:56:11 -08:00
Kelsi
25412ff5cc Fix DBC loading and bank drag-and-drop reliability
- Force binary loading for SkillLine.dbc (CSV exports are garbled)
- Handle quoted numeric values in DBC CSV parser
- Fix bank slot drag-and-drop to use mouse-release detection instead
  of click detection, preventing item drops on wrong slots
- Fix action bar item drop to use hoveredOnRelease for consistency
2026-02-26 02:33:10 -08:00
Kelsi
94e4a0bdb3 Fix letter-named patch MPQs not loading on case-sensitive filesystems
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
Two bugs in loadPatchArchives():
1. isLetterPatch detection was inverted (rfind != 0 is false for all
   "patch-*" entries), making the disable flags non-functional
2. Patch file lookup used exact std::filesystem::exists() which is
   case-sensitive on Linux — Patch-A.MPQ wouldn't match patch-a.mpq

Now scans the data directory once and builds a case-insensitive lookup
map, so any case variant (Patch-A.MPQ, patch-a.mpq, PATCH-A.MPQ) is
found correctly.
2026-02-25 12:18:36 -08:00
Kelsi
d045c1215a Fix Warden module parsing and DBC loading for Classic expansion
Warden: copy/skip pair order was reversed — format is [copy][data][skip]
per MaNGOS/TrinityCore, not [skip][copy][data]. All copy sizes read as 0,
causing module load failure and server disconnect.

DBC: when binary DBCs aren't available (no MPQ extraction), fall back to
expansion CSV files even for visual DBCs (CreatureDisplayInfo, CharSections,
ItemDisplayInfo, etc.) instead of failing with "DBC not found".
2026-02-24 04:42:36 -08:00