feat: asset-extraction tooling + natural-size placed fixtures #131

Merged
marco merged 3 commits from chore/uo-art-extraction-ankh into main 2026-07-22 10:02:03 +02:00
Owner

Summary

Follow-up to #112: build the asset-extraction tooling to bring UO art into the pack, fix how
placed fixtures render, and give the ankh a UO-static placeholder (better than the flat colour
tile) while the true ankh art is still being tracked down.

  • Placed fixtures render at natural size. The ankh was squished into the uniform pickup box.
    GroundItem now carries Placed, and the client draws a placed fixture at its art's natural
    size, anchored bottom-centre (standing on its tile like a tree). Protocol bumped to 7.
  • Asset-extraction tooling (reusable for all future art):
    • findart <client> <name> — search tiledata.mul for statics by name (the authoritative
      way to locate art), backed by TileDataReader.
    • multiscan <client> <tileId> — find which Multi.mul multi contains a tile (MultiMulReader,
      classic 12-byte records), for houses/shrines/arena props (#89).
    • composite — preview a multi-tile static composite to a PNG (find offsets before baking).
  • docs/asset-extraction.md documents the whole pipeline (classic .mul source, just pack,
    finding/adding an art id, the tools above, natural-size fixtures, the .uop caveat) and is
    referenced from CLAUDE.md.
  • Ankh art is a placeholder. I regenerated the pack with item_ankh = 0x1D98, but that id
    turned out to be a decorative (gore) UO static, not a real ankh — and findart finds no
    static named "ankh" in this client, so the true ankh art isn't pinned yet. The owner chose to
    keep this UO-static placeholder for now (it reads better than the flat colour tile). The real
    ankh static + the resurrect-sparkle FX stay tracked in #130 (this PR does not close it).

Design note (reviewed via critical-design-review): asset + client-render only — the server is
unchanged, no gameplay rule touched; the one wire change (GroundItem.Placed) carries the
required protocol bump; multi-platform and the asset-fallback invariant hold (no pack →
placeholder).

Screenshots / recording

Captured via the debug harness (docs/debug-harness.md), login debug next to a persisted ankh.
The placed fixture now renders at natural size, standing on its tile (versus the earlier squished
pickup-sized sprite). The art itself is the placeholder UO static described above.

placed fixture at natural size

How it was tested

  • just test — all green (189 GameServer, 17 AssetExtractor incl. new MultiMulReader tests,
    72 Client.Core, 54 Shared, 12 Auth, 4 Assets).
  • just lint — build succeeds, 0 warnings.
  • Repacked assets.isoa from ~/Games/UO (classic .mul) and confirmed static/item_ankh is
    present; validated findart against known ids (finds swords) and multiscan (classic 12-byte).
  • Piloted the client via the harness for the screenshot above.

Checklist

  • just lint passes (CSharpier + analyzers, zero warnings)
  • just test is green
  • The whole solution builds (client and tools included)
  • Multi-platform preserved (server on Win/macOS/Linux, client on Win/macOS)
  • Tests added/updated for this change (MultiMulReader; discovery commands are dev tooling like the existing statics/land ones)
  • Linked the related issue (#130 stays open for the true ankh art + FX; repo still shippable without the pack)
## Summary Follow-up to #112: build the asset-extraction tooling to bring UO art into the pack, fix how placed fixtures render, and give the ankh a UO-static placeholder (better than the flat colour tile) while the *true* ankh art is still being tracked down. - **Placed fixtures render at natural size.** The ankh was squished into the uniform pickup box. `GroundItem` now carries `Placed`, and the client draws a placed fixture at its art's natural size, anchored bottom-centre (standing on its tile like a tree). Protocol bumped to **7**. - **Asset-extraction tooling** (reusable for all future art): - `findart <client> <name>` — search `tiledata.mul` for statics **by name** (the authoritative way to locate art), backed by `TileDataReader`. - `multiscan <client> <tileId>` — find which `Multi.mul` multi contains a tile (`MultiMulReader`, classic 12-byte records), for houses/shrines/arena props (#89). - `composite` — preview a multi-tile static composite to a PNG (find offsets before baking). - **`docs/asset-extraction.md`** documents the whole pipeline (classic `.mul` source, `just pack`, finding/adding an art id, the tools above, natural-size fixtures, the `.uop` caveat) and is referenced from `CLAUDE.md`. - **Ankh art is a placeholder.** I regenerated the pack with `item_ankh = 0x1D98`, but that id turned out to be a decorative (gore) UO static, **not** a real ankh — and `findart` finds no static named "ankh" in this client, so the true ankh art isn't pinned yet. The owner chose to keep this UO-static placeholder for now (it reads better than the flat colour tile). The real ankh static + the resurrect-sparkle FX stay tracked in **#130** (this PR does **not** close it). Design note (reviewed via `critical-design-review`): asset + client-render only — the server is unchanged, no gameplay rule touched; the one wire change (`GroundItem.Placed`) carries the required protocol bump; multi-platform and the asset-fallback invariant hold (no pack → placeholder). ## Screenshots / recording Captured via the debug harness (`docs/debug-harness.md`), `login debug` next to a persisted ankh. The placed fixture now renders at natural size, standing on its tile (versus the earlier squished pickup-sized sprite). The art itself is the placeholder UO static described above. ![placed fixture at natural size](https://git.homelab.devncode.it/attachments/06cd533c-ca2d-490e-b894-6ec9eb9519a4) ## How it was tested - `just test` — all green (189 GameServer, **17 AssetExtractor** incl. new `MultiMulReader` tests, 72 Client.Core, 54 Shared, 12 Auth, 4 Assets). - `just lint` — build succeeds, **0 warnings**. - Repacked `assets.isoa` from `~/Games/UO` (classic `.mul`) and confirmed `static/item_ankh` is present; validated `findart` against known ids (finds swords) and `multiscan` (classic 12-byte). - Piloted the client via the harness for the screenshot above. ## Checklist - [x] `just lint` passes (CSharpier + analyzers, zero warnings) - [x] `just test` is green - [x] The whole solution builds (client and tools included) - [x] Multi-platform preserved (server on Win/macOS/Linux, client on Win/macOS) - [x] Tests added/updated for this change (`MultiMulReader`; discovery commands are dev tooling like the existing `statics`/`land` ones) - [x] Linked the related issue (#130 stays open for the true ankh art + FX; repo still shippable without the pack)
The ankh (#112) rendered as a placeholder because its static wasn't in the pack.
Regenerated assets.isoa from the classic .mul source (~/Games/UO), which picks up
the item_ankh (0x1D98) manifest entry added in #112 — the ankh now renders as the
real UO static, no client change. Adds docs/asset-extraction.md documenting the
extraction pipeline (classic .mul source, `just pack`, finding/adding an art id,
the .uop caveat) and references it from CLAUDE.md. Closes #130.
feat: render placed fixtures at natural size + Multi.mul reader tooling
All checks were successful
ci / Lint & Test (pull_request) Successful in 1m35s
0bc2e21783
The ankh art was in the pack but rendered squished into the uniform pickup box.
Placed world fixtures now carry GroundItem.Placed and the client draws them at
natural size, anchored bottom-centre (standing on the tile like a tree), so the
tall ankh static shows in full. Protocol bumped to 7.

Adds a Multi.mul reader + `multiscan` discovery command (classic 12-byte records)
as reusable infrastructure for real multis (houses, arena props #89) — used here
to confirm the resurrection ankh is NOT a multi (its four tiles are placed
individually), so we use the single body tile at natural size. Documented in
docs/asset-extraction.md.
feat: findart (tiledata name search) + composite preview tooling
All checks were successful
ci / Lint & Test (pull_request) Successful in 2m13s
0a718c11bd
Adds two more asset-discovery commands to the extractor: `findart <client> <name>`
(searches tiledata.mul for statics by name — the authoritative way to locate art
like the ankh) and `composite` (preview a multi-tile static composite as a PNG,
to find offsets before baking). Backed by TileDataReader + a CompositeStatics
helper.

Honesty pass on the ankh art: 0x1D98 turned out to be a decorative (gore) static,
not the true ankh — kept as a temporary UO-static placeholder (better than the
flat colour tile) with the real ankh art + resurrect FX tracked in #130. Comments
and docs updated to say so.
marco changed title from feat: real UO ankh art + natural-size placed fixtures (#130) to feat: asset-extraction tooling + natural-size placed fixtures 2026-07-22 09:59:37 +02:00
marco merged commit 3d57834be7 into main 2026-07-22 10:02:03 +02:00
marco deleted branch chore/uo-art-extraction-ankh 2026-07-22 10:02:03 +02:00
Sign in to join this conversation.
No reviewers
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!131
No description provided.