feat(net+app): SubPalette overlays applied to palette-indexed textures (Phase 5b)
Implements the other half of ObjDesc: SubPalettes (palette-range
overlays) that repaint palette-indexed textures with per-entity color
schemes. Ported algorithm from ACViewer Render/TextureCache.IndexToColor
after the user pointed out I was prematurely implementing from scratch
instead of checking all the reference repos.
The Nullified Statue of a Drudge sends (setup=0x020007DD with a drudge
GfxObj animPart replacing part 1, plus 2 texChanges targeted at part 1,
plus 1 subpalette id=0x04001351 offset=0 length=0). The TextureChanges
swap fine detail surfaces; the SubPalette with length=0 ("entire palette"
per Chorizite docs) remaps the drudge's flesh-tone palette to stone.
Without this commit, the statue looked like a normal flesh drudge
because palette-indexed textures decoded with the base flesh palette.
Added:
- Core/World/PaletteOverride.cs: per-entity record carrying
BasePaletteId + a list of (SubPaletteId, Offset, Length) range
overlays. Documents the "offset/length are wire-scaled by 8"
convention and the "length=0 means whole palette" sentinel.
- WorldEntity.PaletteOverride nullable field. Per-entity (same across
all parts), in contrast to MeshRef.SurfaceOverrides which is per-part.
- TextureCache.GetOrUploadWithPaletteOverride: new entry point that
composes the effective palette at decode time. Composite cache key
is (surfaceId, origTexOverride, paletteHash) so entities with
equivalent palette setups share the GL texture.
- ComposePalette: ports ACViewer's IndexToColor overlay loop:
for each subpalette sp:
startIdx = sp.Offset * 8 // multiply back from wire
count = sp.Length == 0 ? 2048 : sp.Length * 8 // sentinel
for j in [0, count):
composed[j + startIdx] = subPal.Colors[j + startIdx]
Critical detail: copies from the SAME offset in the sub palette, not
from [0]. Both base and sub are treated as full palettes sharing an
index space.
- StaticMeshRenderer.Draw: three-way switch on (entity.PaletteOverride,
meshRef.SurfaceOverrides) picks the right TextureCache path:
- Both → palette override (it handles origTex override internally)
- Only tex override → GetOrUploadWithOrigTextureOverride
- Neither → plain GetOrUpload
- GameWindow.OnLiveEntitySpawned: builds PaletteOverride from
spawn.BasePaletteId + spawn.SubPalettes when the server sent any.
Reference note: the user asked "but I mean THIS MUST BE IN WORLDBUILDER"
which was the right push. WorldBuilder is actually a dat VIEWER and its
ClothingTableBrowserViewModel is a 10-line stub — it doesn't apply
palette overlays because it doesn't need to. The actual algorithm lives
in ACViewer (a MonoGame character viewer), which I should have checked
earlier. CLAUDE.md updated with a standing rule: always cross-reference
all four of references/ACE, ACViewer, WorldBuilder, Chorizite.ACProtocol,
plus holtburger. A single reference can be misleading; the intersection
is usually the truth.
Tests: 77 core + 83 net = 160, all green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b69d776179
commit
733f8ff601
6 changed files with 238 additions and 14 deletions
38
CLAUDE.md
38
CLAUDE.md
|
|
@ -48,3 +48,41 @@ Things you should just do without asking:
|
|||
Before claiming a phase or sub-step is done: run `dotnet build` and
|
||||
`dotnet test` green, commit with a message that explains the "why", update
|
||||
memory if there's a durable lesson, and move to the next todo item.
|
||||
|
||||
## Reference repos: check ALL FOUR, not just one
|
||||
|
||||
When researching a protocol detail, dat format, rendering algorithm, or
|
||||
any "how does AC do X" question, **check all four of the vendored
|
||||
references in `references/`** before committing to an approach. Do not
|
||||
settle on the first hit and move on — cross-reference at least two of
|
||||
these, ideally all four:
|
||||
|
||||
- **`references/ACE/`** — ACEmulator server. Authority on the wire
|
||||
protocol (packet framing, ISAAC, game message opcodes, serialization
|
||||
order). The things a server has to know to parse and produce bytes.
|
||||
- **`references/ACViewer/`** — MonoGame-based dat viewer that actually
|
||||
renders characters + world. Authority on the client-side visual
|
||||
pipeline: ObjDesc application, palette overlays, texture decoding
|
||||
for the palette-indexed formats. See
|
||||
`ACViewer/Render/TextureCache.cs::IndexToColor` for the canonical
|
||||
subpalette overlay algorithm.
|
||||
- **`references/WorldBuilder/`** — C# + Silk.NET dat editor. Exact-stack
|
||||
match to acdream for rendering approaches: terrain blending, texture
|
||||
atlases, shader patterns. Most useful for "how do I do this GL thing
|
||||
with Silk.NET on net10 idiomatically?" Less useful for protocol or
|
||||
character appearance (dat editor, not game client).
|
||||
- **`references/Chorizite.ACProtocol/`** — clean-room C# protocol
|
||||
library generated from a protocol XML description. Useful sanity check
|
||||
on field order, packed-dword conventions, type-prefix handling. The
|
||||
generated Types/*.cs files have accurate field comments (e.g. "If
|
||||
it is 0, it defaults to 256*8") that ACE's server-side code doesn't.
|
||||
- **`references/holtburger/`** — Rust AC client crate. Cross-references
|
||||
handshake quirks, race delays, and per-message encoding decisions
|
||||
that ACE doesn't document because it's server-side.
|
||||
|
||||
Pattern: when you encounter an unknown behavior, grep all four for the
|
||||
relevant term, read each hit, and compose a multi-source understanding
|
||||
BEFORE writing acdream code. A single reference can be misleading; the
|
||||
intersection of all four is almost always the truth. The user has
|
||||
repeatedly had to remind me about this when I narrowly searched one ref
|
||||
and missed obvious answers in another.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue