Extract UO gump art for authentic paperdoll/container/button UI #19

Closed
opened 2026-07-17 10:09:49 +02:00 by marco · 4 comments
Owner

Context

The legacy UO client is available locally at ~/Games/UO (gumpart.mul + gumpidx.mul). The client UI currently uses placeholder rectangles/text. Goal: an authentic layered UO paperdoll look for the inventory/equip UI, plus real container background and button/scrollbar art.

Gumps are pure client-side presentation — no protocol, no authoritative state, no server validation involved.

Decisions (locked)

  • Role: authentic layered paperdoll (body gump + frame + equip slots at real UO coordinates + container background behind the item grid + real buttons).
  • First sets: backpack/container background, paperdoll frame + equip slots, buttons/scrollbar.
  • Equip slots show the item icon (art.mul) for now; true worn-clothing gump layering is deferred until items are typed (needs an item catalog with UO gump IDs + Layer + hue — touches Shared/server, out of scope here).
  • Male body only (matches HumanMaleBody = 400); hue 0 for the first pass.
  • Extracted PNGs stay git-ignored (assets policy) — only source .mul stay local; repo remains shippable without assets (placeholder fallback).

Scope

  • Phase AGumpMulReader (gump idx/mul decode: extra=(w<<16)|h, per-row RLE, ARGB1555, no hue) + an AssetExtractor "dump gumps by id/range → PNG" mode to visually identify the right IDs. Unit-tested against synthetic gump bytes.
  • Phase B — freeze a named manifest name→gumpId for the confirmed set; client loads them at runtime (Texture2D.FromStream) with placeholder fallback.
  • Phase C — rework InventoryPanel/paperdoll to authentic layout: body+frame, equip slots at UO coordinates, container background behind the grid, real buttons; per-Layer render skeleton (0/1 layers until items are typed).

Independent of #16 (statics) — shares no code, can proceed in parallel.

Definition of Done

  • GumpMulReader decodes a synthetic multi-row RLE gump to exact expected pixels (transparent run = alpha 0, colored run = opaque), verified by a unit test that constructs the idx/mul bytes in-memory (no .mul committed).
  • GumpMulReader returns null (not throw) for an empty/absent gump entry (lookup < 0 or length <= 0), covered by a test.
  • Running the dump mode against ~/Games/UO writes one PNG per requested gump id whose pixel dimensions equal the extra width/height from gumpidx.mul (demonstrable by opening a dumped PNG and matching its size to the idx entry).
  • The named gump manifest is committed as source (IDs + names only, no art); each entry documents what the id is.
  • Client renders the real backpack/container background and paperdoll frame; with assets absent it falls back to the current placeholder and still runs (demonstrable by launching with and without ~/Games/UO extracted).
  • Equip slots display the equipped item’s art.mul icon at the correct paperdoll slot coordinates.
## Context The legacy UO client is available locally at `~/Games/UO` (`gumpart.mul` + `gumpidx.mul`). The client UI currently uses placeholder rectangles/text. Goal: an **authentic layered UO paperdoll** look for the inventory/equip UI, plus real container background and button/scrollbar art. Gumps are pure **client-side presentation** — no protocol, no authoritative state, no server validation involved. ## Decisions (locked) - Role: **authentic layered paperdoll** (body gump + frame + equip slots at real UO coordinates + container background behind the item grid + real buttons). - First sets: backpack/container background, paperdoll frame + equip slots, buttons/scrollbar. - Equip slots show the **item icon (art.mul)** for now; true worn-clothing gump layering is **deferred** until items are typed (needs an item catalog with UO gump IDs + Layer + hue — touches Shared/server, out of scope here). - Male body only (matches `HumanMaleBody = 400`); hue 0 for the first pass. - Extracted PNGs stay **git-ignored** (assets policy) — only source `.mul` stay local; repo remains shippable without assets (placeholder fallback). ## Scope - **Phase A** — `GumpMulReader` (gump idx/mul decode: `extra=(w<<16)|h`, per-row RLE, ARGB1555, no hue) + an AssetExtractor "dump gumps by id/range → PNG" mode to visually identify the right IDs. Unit-tested against synthetic gump bytes. - **Phase B** — freeze a named manifest `name→gumpId` for the confirmed set; client loads them at runtime (`Texture2D.FromStream`) with placeholder fallback. - **Phase C** — rework `InventoryPanel`/paperdoll to authentic layout: body+frame, equip slots at UO coordinates, container background behind the grid, real buttons; per-Layer render skeleton (0/1 layers until items are typed). Independent of #16 (statics) — shares no code, can proceed in parallel. ## Definition of Done - `GumpMulReader` decodes a synthetic multi-row RLE gump to exact expected pixels (transparent run = alpha 0, colored run = opaque), verified by a unit test that constructs the idx/mul bytes in-memory (no `.mul` committed). - `GumpMulReader` returns null (not throw) for an empty/absent gump entry (lookup < 0 or length <= 0), covered by a test. - Running the dump mode against `~/Games/UO` writes one PNG per requested gump id whose pixel dimensions equal the `extra` width/height from `gumpidx.mul` (demonstrable by opening a dumped PNG and matching its size to the idx entry). - The named gump manifest is committed as source (IDs + names only, no art); each entry documents what the id is. - Client renders the real backpack/container background and paperdoll frame; with assets absent it falls back to the current placeholder and still runs (demonstrable by launching with and without `~/Games/UO` extracted). - Equip slots display the equipped item’s art.mul icon at the correct paperdoll slot coordinates.
Author
Owner

Phase A landed on branch feat/gump-art-extraction (gump.mul reader + PNG dump mode, unit-tested; decoder verified against real data — paperdoll body 0x000C, backpack container 0x003C, and paperdoll buttons 0x07D6+ all decode correctly). Phases B/C (manifest + client paperdoll rendering) deferred: per priority call, character sprites and map tiles come first. Branch parked for later resume.

Phase A landed on branch feat/gump-art-extraction (gump.mul reader + PNG dump mode, unit-tested; decoder verified against real data — paperdoll body 0x000C, backpack container 0x003C, and paperdoll buttons 0x07D6+ all decode correctly). **Phases B/C (manifest + client paperdoll rendering) deferred**: per priority call, character sprites and map tiles come first. Branch parked for later resume.
Author
Owner

Follow-up from #32 (targeting): the targeting cursor currently uses a placeholder crosshair. When gump art is extracted, swap it for the real UO targeting cursor gump — it's a single-texture swap in GameScreen.DrawTargetCursor.

Follow-up from #32 (targeting): the targeting cursor currently uses a placeholder crosshair. When gump art is extracted, swap it for the **real UO targeting cursor gump** — it's a single-texture swap in `GameScreen.DrawTargetCursor`.
Author
Owner

Direction shifted with #100: the backpack container gump is now our own art (baked via the OwnArt step in the AssetExtractor), not UO-extracted — in line with the "UO art is a temporary placeholder, to be replaced with our own original art" policy.

So UO gump extraction is now only potentially useful as interim placeholders for buttons / scrollbars / the paperdoll frame. Re-scope to those, or close if we go straight to own art for the remaining UI chrome too.

Direction shifted with #100: the backpack container gump is now **our own art** (baked via the `OwnArt` step in the AssetExtractor), not UO-extracted — in line with the "UO art is a temporary placeholder, to be replaced with our own original art" policy. So UO gump extraction is now only potentially useful as **interim placeholders for buttons / scrollbars / the paperdoll frame**. Re-scope to those, or close if we go straight to own art for the remaining UI chrome too.
marco added this to the Alpha milestone 2026-07-20 18:37:57 +02:00
Author
Owner

Done — UO gump extraction shipped (the #116 line): `GumpMulReader` decodes idx/mul RLE, with tests for multi-row RLE / empty-entry-null / id-out-of-index-null; the `gumps` dump command exists; the named manifest `gumps.json` is committed. Consumed by the paperdoll (#106) and the /admin panel (#117). Closing.

Done — UO gump extraction shipped (the #116 line): \`GumpMulReader\` decodes idx/mul RLE, with tests for multi-row RLE / empty-entry-null / id-out-of-index-null; the \`gumps\` dump command exists; the named manifest \`gumps.json\` is committed. Consumed by the paperdoll (#106) and the /admin panel (#117). Closing.
marco closed this issue 2026-07-22 06:07:58 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#19
No description provided.