Commit graph

392 commits

Author SHA1 Message Date
Kelsi
604d29d375 fix(editor): NPC + object position/rotation NaN guards on JSON load
Mirrors the recent scale guards. NaN/inf positions or rotations make
the M2 renderer's matrix math produce chaos-shaped instances or crash
during normal computation. Now both fields are reset to zero on load
when any component is non-finite.
2026-05-06 05:04:25 -07:00
Kelsi
d525318e9c fix(editor): normalise NPC orientation to [0,360) and guard scale against NaN
Orientation values from edited JSON could be negative or wrap multiple
revolutions; now normalised once at load. Scale was already clamped
on small-positive but didn't reject NaN/inf — now rejects both with the
same defensive check object_placer just got.
2026-05-06 05:02:24 -07:00
Kelsi
1979d921a7 fix(editor): clamp PlacedObject scale to 1.0 when JSON value is invalid
Same defensive check the WoB doodad load just got — guards against
corrupted/partial-write JSON where scale ends up 0/NaN/inf. Without
this an invisible (scale=0) or crashed (NaN matrix) placement could
silently bork a loaded zone.
2026-05-06 04:59:28 -07:00
Kelsi
70e48640d8 feat(editor): surface quest-chain validation issues to user via toast
validateChains warnings were only going to the log file. Most users
don't tail the log while editing, so a broken chain (quest pointing at
a deleted nextQuestId, circular dependency) would only be discovered
when testing in-game. Now also shows a 5s toast with the issue count.
2026-05-06 04:54:51 -07:00
Kelsi
df1eed1c42 fix(editor): preserve CreatureSpawn.id across JSON save/load
Quest links (questGiverNpcId, turnInNpcId, KillCreature targetName) all
key off CreatureSpawn.id, but loadFromFile always assigned new ids via
nextId(). So saving and reloading would silently break every quest hook
in the zone. Now JSON stores id, the loader reads it back when present
(legacy files fall back to nextId()), and idCounter_ is bumped past
loaded values to prevent future collisions. Same fix as the recent
PlacedObject.uniqueId one.
2026-05-06 04:53:09 -07:00
Kelsi
00717543a8 feat(editor): preset selection auto-fills sensible AzerothCore faction
When a user picks a creature preset, faction stays 0 (server treats this
as 'use template') unless the user already typed a value. Now defaults
based on the preset category:
  Critter        -> 250 (critter, indifferent to all)
  hostile preset -> 14  (monster, hostile to all)
  friendly preset -> 35  (friendly to all)
Means picking 'Wolf' from the preset list immediately produces a hostile
NPC that actually attacks players in-game without further configuration.
2026-05-06 04:48:30 -07:00
Kelsi
70366dc5f6 fix(sql): wander_distance is 0 for non-Wander NPCs
Stationary and Patrol creatures should have wander_distance=0; only
Wander behaviour uses it. Previously the editor's wanderRadius template
default of 10.0 was being written for every spawn, making stationary
guards drift around in-game.
2026-05-06 04:46:57 -07:00
Kelsi
2d41e560f2 docs(format-spec): add quick-reference table mapping formats to replaced Blizzard formats 2026-05-06 04:44:24 -07:00
Kelsi
b1c89823d4 docs(format-spec): tighten WHM + WOM layouts with exact field sizes
WHM: clarify chunkCount=256, vertsPerChunk=145 are fixed, baseHeight is
a float, heights are 145 floats. WOM: spell out boundRadius+min+max as
separate fields (was 'bounds(28)'), nameLen prefix, indices uint32, and
texPath length-prefixing. WOM2 trailing block reorganized into a single
list with bone+anim sequence shown as one block. Reading the spec now
yields a working parser without source-diving.
2026-05-06 04:42:04 -07:00
Kelsi
15630f7723 docs(format-spec): tighten WOB layout description with exact field sizes/types
The previous WOB section was loose ('bounds(4)' was actually boundRadius;
group size annotations missed the prefixed string lengths). Updated to
show every byte: 4-byte field sizes, 12-byte vec3s, 2-byte length
prefixes, 48-byte interleaved vertices. Reverse-engineering a WOB from
the spec is now possible without reading the source.
2026-05-06 04:38:15 -07:00
Kelsi
079ff5bfb5 feat(editor): --info-wcp CLI prints content pack metadata
Reports name/author/description/version/format/mapId, total file count,
per-category breakdown (terrain/model/building/texture/data), and total
on-disk bytes. Useful for inspecting third-party WCPs before importing
or for sanity-checking your own exports.
2026-05-06 04:36:40 -07:00
Kelsi
8d78b5f8c6 fix(content-pack): unpackZone now creates the zone subdirectory
packZone stores files relative to the zone subdirectory (e.g. just
'MyZone_32_32.adt'), so unpacking to 'custom_zones/' produced files at
'custom_zones/MyZone_32_32.adt' — without the zone subdir the loader
expects. Now reads the info JSON to extract the zone name and unpacks
to 'custom_zones/<zoneName>/' so imported zones load correctly.
2026-05-06 04:32:28 -07:00
Kelsi
a0e363f706 feat(editor): WCP export toast reports file size in MB 2026-05-06 04:30:13 -07:00
Kelsi
c0ae924fc7 fix(editor): NPC default scale 1.0 (was 3.0) to match AzerothCore defaults
The CreatureSpawn struct default of 3.0 made every newly placed NPC
appear as an oversized 3x-scale creature, very obviously not what users
wanted. Existing JSON spawn files load their stored scale unchanged
(only impacts newly placed templates).
2026-05-06 04:27:22 -07:00
Kelsi
4d11949048 fix(editor): preserve PlacedObject uniqueId across JSON save/load
uniqueId was always regenerated on load, so re-saving the same zone
produced a different uniqueId per placement each time. Since
syncToTerrain copies obj.uniqueId into MDDF/MODF, the ADT also rotated
uniqueIds across cycles. Now JSON stores uniqueId, the loader reads it
back when present (falling back to nextUniqueId() for legacy files),
and uniqueIdCounter_ is bumped past any loaded value so future
placements never collide.
2026-05-06 04:23:39 -07:00
Kelsi
882321863a feat(editor): NPC template + selected-NPC editor expose Respawn (s) input
The respawnTimeMs field was loaded/saved/exported but never editable
through the UI. Added a DragFloat showing seconds (range 5-86400) in
both the template and the selected-NPC editors. SQL export already
divides by 1000 for AzerothCore's spawntimesecs column.
2026-05-06 04:21:42 -07:00
Kelsi
8d006b6b86 feat(editor): selected-NPC editor gains Mana / Min Dmg / Max Dmg / Armor inputs
Last batch of stat fields missing from the selected-NPC editor. Now any
property a user could set on the template can also be edited on an
already-placed NPC, without removing and re-placing.
2026-05-06 04:18:25 -07:00
Kelsi
a7ab2756d6 feat(editor): selected-NPC editor gains role flag checkboxes
Adds Hostile/Quest/Vendor/Inn/Train/Bank/Auc/Repair/Flight checkboxes
to the selected-NPC editor matching the template editor's set. Lets
users toggle these on already-placed NPCs without removing and
re-placing them.
2026-05-06 04:17:31 -07:00
Kelsi
9625201952 feat(editor): selected-NPC editor gains Faction input (parity with template) 2026-05-06 04:15:39 -07:00
Kelsi
4e01dd5553 feat(editor): NPC template gains Faction ID input with common-value tooltip
The CreatureSpawn struct has a faction field that was already exported
to creature_template.faction but wasn't editable. Added an InputInt with
a tooltip listing the common AzerothCore FactionTemplate IDs (Stormwind,
Monster, Beast, Friendly, Critter, etc.) so users can pick the right
hostility/disposition without referencing the DBC manually.
2026-05-06 04:14:33 -07:00
Kelsi
da2e7a4133 feat(editor): viewport WOM/WOB lookups also probe per-zone roots
EditorViewport gains setActiveMapName() so rebuildObjects can pass
per-zone prefixes (output/<map>/models|buildings/, custom_zones/<map>/...)
to tryLoadByGamePath. EditorApp wires it from loadADT, loadWMOInstance,
and createNewTerrain. Now the editor's preview mirrors the main game's
priority: per-zone WOM/WOB beats global custom_zones/, beats game data.
2026-05-06 04:13:03 -07:00
Kelsi
db068d480b feat(wob): tryLoadByGamePath helper, used by editor + terrain_manager
Mirrors the WOM tryLoadByGamePath API: probes custom_zones/buildings/ +
output/buildings/ by default, with optional extraPrefixes (e.g. per-zone
output/<map>/buildings/) checked first. Both the editor and the main
game's terrain_manager now use the helper, removing duplicate inline
lookup loops in two more places.
2026-05-06 04:10:12 -07:00
Kelsi
99aaab3aa8 feat(editor): add Trainer/Banker/Auctioneer/Repair NPC flags + SQL export
CreatureSpawn struct gains four AzerothCore-standard NPC flag bits:
  trainer    -> npcflag 0x10
  repair     -> npcflag 0x1000
  banker     -> npcflag 0x20000
  auctioneer -> npcflag 0x200000
Saved/loaded via the JSON spawn file, exported to creature_template.npcflag,
exposed as checkboxes in the NPC template panel. Lets users build full
city NPCs (city auctioneer, weapon trainer, etc.) without dropping to SQL.
2026-05-06 04:03:23 -07:00
Kelsi
bc6e60c6e9 polish(editor): placement scale slider matches selected-object range (0.1-50) 2026-05-06 04:00:07 -07:00
Kelsi
a156b6246e polish(editor): NPC selected-editor Facing slider shows 'deg' unit (matches template) 2026-05-06 03:59:10 -07:00
Kelsi
597c6547ac feat(editor): WMO objects also try WOB open format first like M2->WOM does
The editor's M2 placement path tries WOM (custom_zones/models/, output/
models/) before falling back to game M2 files. WMO placement just went
straight to game files. Now mirrors the M2 path: probes
custom_zones/buildings/ + output/buildings/ for a .wob, converts via
toWMOModel, falls back to MPQ-extracted WMO only on miss. Lets exported
zones render their custom buildings without needing the original WMO.
2026-05-06 03:56:52 -07:00
Kelsi
cbf1d4638f fix(editor): WMO instance scale actually applied to renderer
WMORenderer::createInstance accepts a scale parameter (default 1.0),
but the editor's call site ignored obj.scale. So a WMO sized to 2.0 in
the panel still rendered at 1.0. Now passes obj.scale through, so the
loaded MODF scale + any user gizmo scaling work end-to-end.
2026-05-06 03:52:40 -07:00
Kelsi
32ff80f177 feat(editor): texture panel shows total pool count + dir count 2026-05-06 03:48:37 -07:00
Kelsi
f4805b8e69 feat(editor): object panel shows total pool count for M2/WMO assets
Helps users understand the search base — 'Pool: 1234 M2  56 WMO' tells
them why their filter might be matching too many results, and gives a
quick view into how much asset variety the loaded data has.
2026-05-06 03:47:54 -07:00
Kelsi
ebf90eba9f fix(editor): zone manifest reset on load + auto-load existing zone.json
Two related fixes:
1. loadADT now resets zoneManifest_ at the top so the previous zone's
   mapId/displayName/flags/audio don't bleed into the new export.
2. When loading a zone that has a previously-exported zone.json on disk,
   call manifest.load() to restore the user-customized metadata. Without
   this every reload would reset Map ID back to 9000 etc.
2026-05-06 03:46:40 -07:00
Kelsi
f9187ef58a fix(editor): exportZone preserves user displayName + dedupes tile list
Two related zone-manifest bugs:
1. displayName was always overwritten with the .adt prefix on every
   export, throwing away whatever the user typed in the Zone Metadata
   panel.
2. tiles vector was push_back'd to without clearing, so re-exporting the
   same zone would accumulate duplicate tile entries.

Both fixed by checking displayName.empty() before assignment and calling
tiles.clear() before the rebuild loop.
2026-05-06 03:42:54 -07:00
Kelsi
28c63cb6d9 fix(editor): exportContentPack uses zoneManifest.mapId instead of hardcoded 9000
Users who set a custom Map ID via the Zone Metadata panel saw it ignored
when exporting the WCP — the pack info would always say mapId=9000.
Now reads from zoneManifest_, falling back to 9000 only when the field
is unset (0).
2026-05-06 03:41:29 -07:00
Kelsi
f856a90281 feat(editor): preserve WMO instance scale across ADT load/save
The MODF scale field (u16 / 1024 = float) is now propagated in both
directions: load reads wp.scale -> obj.scale, syncToTerrain converts
obj.scale * 1024 -> wp.scale (clamped to u16). Combined with the prior
loader/writer changes this means non-1.0 WMO scales (used by some
WotLK content) survive a save/reload cycle.
2026-05-06 03:40:03 -07:00
Kelsi
db1968f2cc feat(adt): preserve MODF nameSet + scale fields across load/save round-trip
WMOPlacement struct gains nameSet and scale fields (defaulting to 0 and
1024 = 1.0). The loader now reads them when the entry is the full 64
bytes (WotLK+); the writer emits the actual values rather than always
hard-coding (0, 1024). Older expansions still round-trip cleanly because
defaults match the previous behaviour.
2026-05-06 03:37:13 -07:00
Kelsi
446b0970dc fix(sql): translate spawn.id to creature SQL entry for quest links
The editor stores quest hooks (questGiverNpcId, turnInNpcId, KillCreature
targetName) as the spawner's per-spawn .id sequence. The SQL exporter
writes creature_template entries as 'creatureStartEntry + index'. The
two number spaces are different, so quest links pointed at non-existent
creature entries. Added a spawn.id -> SQL entry map built from the
spawns vector and used it in:
  - RequiredNpcOrGo[1..4] for KillCreature objectives
  - creature_queststarter / creature_questender
2026-05-06 03:33:36 -07:00
Kelsi
d258144df4 docs(format-spec): document WOB->WMO restoration details under WOB section 2026-05-06 03:31:04 -07:00
Kelsi
f022459971 fix(editor): NPC template scale slider matches selected-NPC editor range (0.1-50)
Template was a SliderFloat 0.5-10 while the selected-NPC editor uses
DragFloat 0.1-50. Inconsistent ceilings made it surprising that an NPC
could be scaled higher after placement than during placement. Now both
use DragFloat with the same range.
2026-05-06 03:30:30 -07:00
Kelsi
3abe47adc6 perf(editor): periodic M2 model GPU cache cleanup every 30s
The new persistent path->modelId map keeps models alive across rebuilds,
which is great for the common case of moving an instance, but means
models that lost all references stay in GPU memory forever. Added a
30s timer that calls m2Renderer->cleanupUnusedModels(), which has its
own 60s grace period before actual eviction — so models stick around
~60-90s after their last instance is removed and then get freed.
2026-05-06 03:26:39 -07:00
Kelsi
c1b6c9f621 fix(editor): exportZone clears autoSavePendingChanges_ flag
quickSave was the only path that cleared the flag, but exportZone is
also reachable through 'Export Open Format' and exportContentPack
without going through quickSave. Now any successful zone export clears
the dirty state so the asterisk and quit-confirm dialog reset properly.
2026-05-06 03:26:01 -07:00
Kelsi
5241bbd669 perf(editor): cache M2/WMO models across rebuilds, only clear instances
The editor's rebuildObjects path was destroying every cached model and
re-uploading it on every (debounced) change. Added M2Renderer::clearInstances
that drops only the instance list while keeping models loaded. Editor's
clearObjects switches to clearInstances (M2) + clearInstances (WMO),
and persistent path->modelId maps survive across rebuilds. clearTerrain
fully evicts when loading a new zone.
2026-05-06 03:23:06 -07:00
Kelsi
f18976ced9 feat(editor): --info-woc CLI prints collision mesh metadata
Completes the --info* CLI family. Reports tile coords, triangle count,
walkable/steep classification breakdown, and world-space bounds — useful
for verifying that collision exports cover the expected area.
2026-05-06 03:17:10 -07:00
Kelsi
8787b13dc1 feat(editor): --info-wob CLI prints WOB building metadata
Companion to --info for WOM. Reports name, group/portal/doodad counts,
total vertex/triangle/material counts. Useful for verifying converted
WMOs and debugging building rendering issues without launching the GUI.
2026-05-06 03:15:43 -07:00
Kelsi
683d703fbc feat(editor): --info <wom> CLI prints model metadata for inspection
Useful for verifying WOM exports and debugging conversion issues without
loading the GUI. Accepts either /path/to/file.wom or /path/to/file
(loader expects no extension). Reports version, name, geometry counts,
texture/bone/animation/batch counts, and bound radius.
2026-05-06 03:14:12 -07:00
Kelsi
248dcd4eb4 feat(editor): quest objective limit raised to 10 (matches SQL slot capacity)
UI was capped at 4 but the SQL exporter writes RequiredNpcOrGo[1..4] +
RequiredItemId[1..6] = 10 total slots. Allowing 10 lets users define
mixed kill+collect quests fully.
2026-05-06 03:13:26 -07:00
Kelsi
ce778ed674 feat(editor): patrol waypoint reorder (up/dn) + insert-after-cursor (+after)
Previously waypoints could only be appended or removed; reordering meant
clearing and re-adding the whole path. Now each waypoint row has up/dn
swap buttons and a +after that inserts a new waypoint at the current
brush cursor right after this index — slicing long segments doesn't
require redoing the rest of the path.
2026-05-06 03:12:45 -07:00
Kelsi
0be537e73d feat(editor): --validate <zoneDir> CLI scores zone open-format completeness
New CLI option that runs ContentPacker::validateZone on a zone directory
and prints the open-format score (0-7) with per-file breakdown including
magic-byte validity. Exits 0 if 7/7, 1 otherwise — useful for CI checks
on exported zones.
2026-05-06 03:09:56 -07:00
Kelsi
7822790c60 feat(editor): status bar asterisk also reflects unsaved object/NPC/quest changes 2026-05-06 03:01:09 -07:00
Kelsi
848947604e fix(editor): quit-confirm dialog also triggers for unsaved object/NPC/quest changes
Previously only terrain edits would trigger the 'unsaved changes' prompt
on quit, so a user who only added NPCs or quests could lose their work
by closing the window. Now checks autoSavePendingChanges_ alongside
terrain dirty state.
2026-05-06 03:00:10 -07:00
Kelsi
11f0580ccb docs(format-spec): bump to v1.2, document WOC mesh-append + SQL export
- WOC: add note that addMesh() also appends placed WMO group geometry
- New section on SQL server export covering coord/orientation conversion
  rules, table list, and how quest objectives map to RequiredNpcOrGo/
  RequiredItem slots.
2026-05-06 02:57:07 -07:00
Kelsi
7b2cbcfc92 feat(editor): status bar shows cursor world position alongside camera
Cam in dim yellow, cursor (when brush is on terrain) in cyan. Useful for
quickly noting positions where to drop spawns/objects without flying over
to read the brush coords manually.
2026-05-06 02:56:10 -07:00