Skip to content

Address Discord feedback: Reforged skin data, custom model rendering, shortcuts, terrain palette - #32

Merged
StephenSHorton merged 6 commits into
mainfrom
fix/discord-feedback-jul06
Jul 7, 2026
Merged

StephenSHorton merged 6 commits into
mainfrom
fix/discord-feedback-jul06

Conversation

@StephenSHorton

Copy link
Copy Markdown
Owner

Addresses four issues from a Discord user testing wc3-forge against a heavily-customized Reforged map. All four were reproduced/root-caused in the source; two were confirmed data bugs (not just World-Editor-familiarity gaps).

1. Custom units all showed as base "Peasant" (name/model/icon) — confirmed bug

Reforged splits per-map object overrides across two tables per kind: war3map.w3* (gameplay) and war3mapSkin.w3* (art/skin: Name unam, Model File umdl, Icon uico, Tooltip utip). wc3-forge read only the primary half, so custom units loaded the right row count (from w3u) but displayed the base name + default model — the real name/model/icon live only in the never-read war3mapSkin.w3u.

  • Session.Open now reads the war3mapSkin.w3* companion for every kind (read-only, keyed by kind).
  • MergedObjects layers it under the primary shadow (primary wins on any field it sets; skin fills the art fields the primary leaves unset).
  • Skin tables are never marked dirty → Save preserves them verbatim via the lossless copy-through (no data-loss risk).
  • New objects_skin_test.go covers the overlay + primary-wins precedence.

Precedence note (for review): "primary wins, skin fills gaps" is faithful for real maps (name/model live only in the skin table) and keeps the edit path coherent — edits route to war3map.w3u and are never masked by the skin overlay. Writing edits back into the skin table (full WE round-trip semantics) is intentionally left as a follow-up.

2. Placed units rendered with the wrong (base) model — confirmed bug

Separate code path from #1: the viewport's unit merge (typeindex.go) carried only name + icon, never the model file, so custom-skin units drew the base model. Doodads already did this correctly.

  • unitFieldMap: umdl → file, usca → modelscale; applyUnitOverrides sets the model (extension-stripped) + scale.
  • mergeUnitIndex layers the skin companion then the primary (same precedence as feat: native macOS support (needs Windows regression triage) #1).
  • Frontend: the unit type index is now per-map — scene-instances.ts drops the process-wide cache on each loadMap (it was cached for the process lifetime, which would have hidden the fix). (Tint uclr/g/b deliberately skipped for now to avoid a 0-vs-255 default regression.)

3. F4–F12 did nothing; the menu advertised dead F4/F6 labels

  • Wired the advertised keys: F4 → Trigger Editor, F6 → Object Editor.
  • Added F2/F3/F8 for Doodad/Terrain/Region mode (matching the existing Tab-cycle + header buttons).
  • Added a keyboard-shortcuts cheat sheet (? / Help menu; Esc/backdrop/× to close) — the discoverable index a WE veteran reaches for.

4. Terrain palette showed "No ground tiles" on a map full of terrain

Root cause turned out to be a missing mount-fetch (not the folder-extract theory): the panel mounts already-open but refresh() only ran from the FAB toggle or a map-changed event that had already fired before Terrain mode was entered.

  • refresh() now runs on mount.
  • The empty state distinguishes "This map has no terrain file (war3map.w3e)." vs "Couldn't load tiles." + a Retry, instead of always claiming "No ground tiles."

Verification

  • go build ./..., go vet ./... clean; go test ./... — all packages pass except a pre-existing mpq fixture-drift test (TestExtractMatchesStormLib_survival, unrelated personal fixture, fails identically on main).
  • frontend: npm run check (0 errors) + npm run build (green).

🤖 Generated with Claude Code

StephenSHorton and others added 6 commits July 6, 2026 19:04
…me/model

Reforged's World Editor splits per-map object overrides across two tables per
kind: war3map.w3* holds gameplay edits, war3mapSkin.w3* holds the art/skin
overrides (Name unam, Model File umdl, Icon uico, Tooltip utip). wc3-forge
loaded only the primary half, so a custom Peasant-based unit loaded the right
row COUNT (from w3u) but displayed the base "Peasant" name + default model —
its real name/model/icon live only in the never-read war3mapSkin.w3u.

Session.Open now reads the war3mapSkin.w3* companion for every registered kind
(read-only, keyed by kind) and MergedObjects layers it UNDER the primary shadow:
primary wins on any field it sets; the skin fills the art fields the primary
leaves unset. This is faithful for real maps (name/model live only in the skin
table) and keeps the edit path coherent — edits route to the primary table and
are never masked by the skin overlay. The skin tables are never marked dirty, so
Save preserves them verbatim via the lossless copy-through.

Adds objects_skin_test.go covering the overlay (name+model surface) and the
primary-wins precedence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdJMLPgZf25tirfS26CobN
The 3D viewport's unit merge (mergeUnitIndex/applyUnitOverrides) only carried
name + icon overrides, never the model file — so a custom unit that swaps its
"Art - Model File" (e.g. a Peasant-based unit using the Sylvanas model) rendered
with the base type's model, the exact mismatch users saw vs the World Editor.
Doodads already handled this via applyDoodadOverrides; units just missed the
`case "file"`.

- unitFieldMap: map umdl -> file and usca -> modelscale.
- applyUnitOverrides: set the model file (extension-stripped to the stem
  placeUnit expects) and model scale from the override.
- mergeUnitIndex: layer the war3mapSkin.w3* companion first, then the primary
  war3map.w3* on top (same primary-wins precedence as MergedObjects), so a
  custom unit's umdl — which usually lives only in the skin table — flows
  through to the renderer.
- scene-instances: the unit type index is now per-map (it folds in this map's
  overrides), so drop the process-wide cache on each loadMap and re-fetch —
  otherwise the fix would never surface.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdJMLPgZf25tirfS26CobN
The File/menu advertised F4 (Trigger Editor) and F6 (Object Editor) but the keys
were never wired — pressing them did nothing. World Editor veterans also had no
discoverable list of what shortcuts exist.

- Wire the advertised keys: F4 -> Trigger Editor, F6 -> Object Editor.
- Add F2/F3/F8 to switch the viewport editor mode (Doodad / Terrain / Region),
  matching the existing Tab-cycle and the header mode buttons.
- Add a keyboard-shortcuts cheat sheet overlay toggled by '?' (Shift+/), a
  Help-menu entry, and Escape / backdrop / × to dismiss. Lists every binding —
  the single highest-value discoverability fix for a WE veteran.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdJMLPgZf25tirfS26CobN
The terrain palette mounts already-open, but refresh() only ran from the FAB
toggle or a map-changed event — and map-changed has already fired by the time
the user enters Terrain mode. So on first entry the grid stayed empty and showed
"No ground tiles." on a map full of terrain (the exact symptom a user reported).

- Call refresh() on mount so the tiles load the first time Terrain mode opens.
- Distinguish the empty-grid reasons instead of always claiming "No ground
  tiles.": "This map has no terrain file (war3map.w3e)." when GetTerrain reports
  no terrain, vs "Couldn't load tiles." with a Retry on an unexpected failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdJMLPgZf25tirfS26CobN
The "file" column is shared across several unit FourCCs, so a per-map Model File
override (umdl) is stored under its own FourCC-keyed slot (storeCol), not under
"file". The preview's unitModelPath only read "file", so a custom-skin unit's 3D
preview kept showing the base model even though the override was loaded (the
list name + the model field value were already correct). Check the "umdl" slot
first, falling through to the stock file/file:hd/file:sd columns otherwise.

Verified end-to-end on Enfo FFB v2.64f: N01O "Lord of the Everlasting Flame"
(base Firelord) preview model_path now resolves to war3mapImported\HeroFlameLord
instead of the stock Firelord path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdJMLPgZf25tirfS26CobN
…s, terrain

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdJMLPgZf25tirfS26CobN
@StephenSHorton
StephenSHorton marked this pull request as ready for review July 7, 2026 01:42
@StephenSHorton
StephenSHorton merged commit ddcfaae into main Jul 7, 2026
0 of 2 checks passed
@StephenSHorton
StephenSHorton deleted the fix/discord-feedback-jul06 branch July 7, 2026 01:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant