docs(link): player bodies face you, everything else does not
Player character bodies take direction index 0 (head-on); every other body takes index 1 (front three-quarter). Direction still does not appear in the key — both indices are extractor configuration. The split follows the legibility caveat rather than fighting it: a humanoid reads fine head-on and a character portrait should look at you, while a wolf seen head-on is a dark blob and the same wolf at index 1 is obviously a wolf. §5.2 is new: which bodies count as player characters is asked of the shard, via Race.AllRaces and each race's MaleBody/FemaleBody/MaleGhostBody/FemaleGhostBody, never hardcoded. Twelve ids on stock 57.4. Two things say a hardcoded list would have been wrong — RaceDefinitions.cs passes the gargoyle's ghost bodies in the opposite order to the other two races (695 male, 694 female), and a shard that calls RegisterRace adds ids no table of ours would contain. And the finding that matters most: SIX OF THOSE TWELVE do not decode at all with ServUO's vendored Animations — both human ghosts and every gargoyle body, because they live in AnimationFrame*.uop which that library never reads. The one part of the scope with the most attention on it is the part the vendored library serves worst, which is now the strongest single argument for §4's recommendation to own the decoders. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
81
link/v8.md
81
link/v8.md
@@ -169,10 +169,12 @@ application, and a minimal PNG writer over `System.IO.Compression.DeflateStream`
|
||||
into a `Bitmap` via `LockBits`, so System.Drawing is in the *decode*, not just the encode — a
|
||||
Linux/Mono shard needs libgdiplus even to read a sprite. Writing our own pixels and our own PNG
|
||||
makes the feature portable by construction.
|
||||
3. **It removes the UOP gap.** ServUO's vendored `Animations` reads legacy `anim*.mul` only. This
|
||||
client has a full 195 MB `anim.mul` so most bodies resolve — but **gargoyle bodies 666/667
|
||||
returned nothing**, because gargoyles live in `AnimationFrame*.uop`. A player race missing from
|
||||
an asset store built for "player models and everything" is not a caveat, it is a defect.
|
||||
3. **It removes the UOP gap, which lands squarely on the player bodies.** ServUO's vendored
|
||||
`Animations` reads legacy `anim*.mul` only, never `AnimationFrame*.uop`. This client has a full
|
||||
195 MB `anim.mul` so most creatures resolve — but **six of the twelve stock player-character
|
||||
bodies return nothing**, including every gargoyle and both human ghosts (§5.2). A missing
|
||||
playable race in an asset store built for "player models and everything" is not a caveat, it is
|
||||
a defect, and it is in the highest-attention part of the scope.
|
||||
4. **We are already in this business.** §9 writes a cliloc decompressor from scratch regardless.
|
||||
|
||||
A2 also ends the dependency on whatever version of `Ultima` a given ServUO happens to vendor,
|
||||
@@ -210,13 +212,20 @@ Three properties this shape buys:
|
||||
### 5.1 There is no direction segment, because only one direction is wanted
|
||||
|
||||
Bodies are stored in **five** directions and the client mirrors three of them to reach eight. Only
|
||||
one is needed here — the one facing the viewer — so **direction is fixed by the extractor and is
|
||||
not part of the key**. Leaving it in would advertise a choice nobody is going to make and would
|
||||
five-fold every count in §11 for nothing.
|
||||
one is needed here, so **direction is fixed by the extractor and is not part of the key**. Leaving
|
||||
it in would advertise a choice nobody is going to vary and would five-fold every count in §11 for
|
||||
nothing.
|
||||
|
||||
**The front-facing index is 0.** This was determined by rendering all five for a human, a wolf and
|
||||
a dragon rather than from a table, because the answer is not obvious and the small-thumbnail version
|
||||
of the same test suggested the opposite:
|
||||
**Which one depends on whether the body is a player character:**
|
||||
|
||||
| Body | Direction | Why |
|
||||
|---|---|---|
|
||||
| A player character body | **0** — head-on, facing the viewer | A character is a portrait; it should look at you |
|
||||
| Everything else | **1** — front three-quarter | The view that actually reads as a creature (see the caveat below) |
|
||||
|
||||
Which index is which was determined by **rendering all five** for a human, a wolf and a dragon
|
||||
rather than from a table, because the answer is not obvious and the small-thumbnail version of the
|
||||
same test suggested the exact opposite:
|
||||
|
||||
| Index | View |
|
||||
|---|---|
|
||||
@@ -226,12 +235,45 @@ of the same test suggested the opposite:
|
||||
| 3 | Rear three-quarter |
|
||||
| 4 | Directly away — back of the head, and a quadruped's tail toward the camera |
|
||||
|
||||
One caveat the render made obvious, and it is the reason this is written down rather than assumed:
|
||||
**index 0 is the least legible view for four-legged and long-bodied creatures.** A wolf seen head-on
|
||||
is a dark blob; the same wolf at index 2 is unmistakably a wolf, and at index 1 is the view
|
||||
UOFiddler's own thumbnail list picks. For a bestiary tile that matters. The extractor therefore
|
||||
takes the index as a **configuration value defaulting to 0**, so changing the catalogue's mind later
|
||||
is a setting and a re-import, not a protocol change.
|
||||
The caveat the render made obvious is what produced the split: **index 0 is the least legible view
|
||||
for four-legged and long-bodied creatures.** A wolf seen head-on is a dark blob; the same wolf at
|
||||
index 1 is unmistakably a wolf, which is also why UOFiddler's own thumbnail list picks that view. A
|
||||
humanoid has no such problem — it reads fine head-on, and head-on is what a character portrait
|
||||
wants.
|
||||
|
||||
Both indices stay **configuration values** (defaulting to 0 and 1), so changing the catalogue's mind
|
||||
later is a setting and a re-import, not a protocol change.
|
||||
|
||||
### 5.2 "Player character body" is asked of the shard, never hardcoded
|
||||
|
||||
`Server.Race.AllRaces` gives every registered race, and each carries `MaleBody`, `FemaleBody`,
|
||||
`MaleGhostBody` and `FemaleGhostBody`. The plugin enumerates those four ids per race and that set —
|
||||
nothing else — takes index 0. On stock ServUO 57.4 that is twelve ids:
|
||||
|
||||
| Race | Male | Female | Male ghost | Female ghost |
|
||||
|---|---|---|---|---|
|
||||
| Human | 400 | 401 | 402 | 403 |
|
||||
| Elf | 605 | 606 | 607 | 608 |
|
||||
| Gargoyle | 666 | 667 | **695** | **694** |
|
||||
|
||||
This is the §8 argument again in miniature: only code inside ServUO can answer it, and asking is
|
||||
the only thing that works on a shard with a custom race. Two details make the case that a
|
||||
hardcoded list would have been wrong — `RaceDefinitions.cs` passes the gargoyle's ghost bodies in
|
||||
the **opposite order** to the other two races (695 male, 694 female), and a shard that calls
|
||||
`RegisterRace` adds ids no table of ours would contain.
|
||||
|
||||
**Half of that set does not decode with ServUO's vendored library.** Measured:
|
||||
|
||||
| Decodes | Does not |
|
||||
|---|---|
|
||||
| Human male/female (400, 401) | **Human ghosts (402, 403)** |
|
||||
| Elf male/female (605, 606) | **Every gargoyle body (666, 667, 694, 695)** |
|
||||
| Elf ghosts (607, 608) | |
|
||||
|
||||
Six of twelve, including a whole playable race. So the one part of the asset scope with the most
|
||||
attention on it — the player character, head-on — is precisely the part the vendored `Animations`
|
||||
serves worst, and §4's recommendation to own the decoders is what fixes it. This is the single
|
||||
strongest piece of evidence for that choice.
|
||||
|
||||
---
|
||||
|
||||
@@ -505,8 +547,9 @@ prove byte-identical output *before* building six phases on top of it.
|
||||
libgdiplus dependency, the UOP gap and the vendor-drift risk all going away at once.
|
||||
2. **§11: gump art deferred.** Confirm that paperdoll and equipment gump art is genuinely
|
||||
out of scope for 8, given it is the one kind whose existing decoder crashes.
|
||||
3. **§5.1: direction is fixed at index 0** (head-on) and is not in the key — settled 2026-09-10.
|
||||
What is *not* settled is whether the catalogue should default to index 1 or 2 instead, since a
|
||||
head-on wolf is a dark blob and a side-on wolf is a wolf. It is a setting either way.
|
||||
3. **§5.1/§5.2: direction — settled 2026-09-10.** Player character bodies use index 0, everything
|
||||
else index 1, direction is not in the key, and the player-body set is enumerated from
|
||||
`Race.AllRaces` rather than hardcoded. Nothing outstanding; recorded here because it changes
|
||||
every count in §11.
|
||||
4. **§13: the default audience** for asset serving — inheriting the using page's audience is
|
||||
proposed; the operator sets the policy either way.
|
||||
|
||||
Reference in New Issue
Block a user