Four things, all found by shooting the feature rather than by reading it.
The flat-pic tint was a no-op for the five colour-table species. tintFor ran
synthetic reference colours through the lookup, none of them were IN it, so
every ratio came back 1 and the tint was discarded -- a shiny Gyarados drew
an ordinary blue pic. Those species now measure the tint from the table's own
entries.
It was also a no-op for most SLIDE species, for a better-hidden reason: a hue
rotation moves red toward cyan and cyan toward red, so averaged over a
balanced set of references the ratios cancel and every species reports no
tint. Charizard and Ponyta both came back neutral. The tint is now measured
against the colour each species is actually MADE of -- a modal body colour,
generated into data/shiny_colors.lua as `dom`. 132 of 151 now carry a usable
tint; the rest genuinely shift too little for one to mean anything.
The sparkle drew sixty quads a frame that nobody could see. It was built to
numbers a tenth of the scale of a mon card -- a ring seven units across,
inside a Gyarados -- and additive drawing keeps the depth test, so all of it
was rejected. Sized against the card now, and pulled toward the camera the
way the move-animation card is.
And the summary PIC cannot be recoloured by touching pixels: the art is
four-shade DMG grey and the colour is applied afterwards by the palette pass.
That attempt is reverted, with the reason left where the next person will
look for it. The star is the designation that works there.
Also: tools/shiny_colors.py now resolves its own paths instead of hardcoding
a worktree, and the extract test gained a hard failure when NOTHING
recolours -- which is what a missing colour table looks like from the
outside, and it passed through it once already.
Evidence in .claude/shiny_update, every case beside its own control.
Gen 1 has no shininess of its own, but it has the four DVs Gen 2 reads to
decide it -- and the engine already ships that reading (Stats.isShiny, its
own comment calling it "the RBY virtual shiny", allowlisted for mods
precisely so an indicator mod can call it). Nothing new is stored on a
Pokemon and nothing migrates: every save already contains the answer, and
this starts drawing it. Random DVs land on the pattern 1 in 8192, which is
the classic rate and the default the odds dial ships at.
Deriving rather than storing is what makes it survive a save, a box, a
trade and an evolution with no second copy of the truth to drift. mon.shiny
is a cache written from the DVs, never read as the source.
The roll goes in Pokemon.new -- every wild, gift, starter and traded mon is
built there, and it is before the battle bakes its sprite, which
battle.started is already too late for. It draws from the mod's own random
stream so installing this does not shift the sequence damage rolls and
encounter slots come out of. Trainers stay ordinary by themselves: the
engine pins their DVs, as the real games do.
The models are genuinely recoloured, as part of the extraction. Each
species is decoded once, packed as usual, then recoloured and packed again
as NNNs.dsm. The colours are Stadium's own HSL slide (hue in degrees,
saturation and lightness on a -8..+8 scale at 12.5% a step); five species
carry an explicit colour table instead, because Stadium gives them a real
alternate texture that no single slide reproduces -- Jigglypuff's body must
stay pink while its irises rotate to green.
Extraction is the right moment because StadiumFx's generated frames are
still marked there and the packer drops the marker: it is the last point a
flame is distinguishable from a hide. A shiny Charizard has a shiny hide
and an ordinary fire. The normal packs are written BEFORE the recolour, so
they come out byte-identical and stadium_extract_test still diffs all 151
against the Python oracle unchanged -- no format change, no DSM4, no second
implementation to keep in step. REV goes to 3 so an existing cache rebuilds.
Flat art is tinted instead, because the engine bakes a species palette into
a cache with no notion of which individual is drawn. The tint comes from
that species' own slide rather than a generic gold. A multiply can only
darken, so species whose shiny is lighter read quieter there than on the
model; the status page's star is the mode-proof mark.
Tests: 58 assertions in tests/shiny_test.lua, including the colour
transform against 640 real colour pairs lifted from the verified texture
set, the DV model, the read side, and the end-to-end through the engine's
own constructor. stadium_extract_test gains --mod (worktrees have neither
the ROM nor the packs, both gitignored) and now also checks that every
shiny pack is the same length as its twin and actually differs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The mat inside every gym door ($06 over $16, 28 placements and nowhere
else) is drawn from straight above, but $06 had been swept into the wall
band -- so the mat's top half stood as a 16px box in the doorway, and the
cell rule could not save it because a pin outranks the cell. Both halves
are now pinned ground.
And the `post` pool no longer takes the support lift. A post stands IN
the ground by definition rather than on top of something, but its cell is
blocked like any other post, so the blocked-cell test that separates
furniture-top props from floor seats cannot tell it apart. Pinning the
cliff slope chain gave posts along a cliff edge an authored box to their
south and hoisted them onto the clifftop.
The Poke Mart register shares its two tiles with the counter, which draws
a full-height black rule down each outer edge. Black always survives the
shade flood, so the billboard pool extruded those rules too and the
machine stood flanked by a pair of tall black slabs.
A white column separates them from the register body, so they are their
own connected components -- which is exactly what `cutout`'s one-object
contract drops. `cutout` is a single voxel though, and a register wants
body, so this adds `console`: the billboard treatment at ten voxels with
that same contract. It cannot be the default for the thick pools,
because a cluster there may legitimately hold several objects (two
stools side by side, a leaf beside a vase).
The tower's twelve ROUTE_10 rows are `gabled_block_6x6` tile for tile --
the artist drew the top of the tower as an ordinary six-cell block -- so
that template matched at (24,132) and stood a whole second building
behind it. Templates were matched and stamped independently, with
nothing stopping two grids claiming one drawing.
Placement is now first-claim-wins: a template never stamps into cells
another already claimed, which makes the list order the priority order.
The tower's own two templates move to the front so they take those
cells first.
Also corrects the split: the Route 10 half is TWELVE rows, not eight --
the 64px purple roof band plus two window courses -- so topRows and the
claimOnly twin now carry all of it, and the model is the complete
twenty-row drawing (96px facade under a real 64px roof). Both templates
verified to place exactly once world-wide.
The mound probe gains a MOUND_LEVEL knob; the low rungs are where this
was visible.
The tower's drawing straddles the map boundary -- roof band and top
window courses in Route 10's last rows, the rest in Lavender -- so each
map folded its half upright alone: two half-buildings, Lavender's under
a fabricated yellow cap.
Buildings templates gain `topRows`: drawing rows composited ABOVE the
matched grid, for a drawing no single map holds. The model is built
from the complete 16-row artwork (96px facade under the real 32px
purple band) while placement still matches only the on-map rows; depth
now derives from the matched footprint rather than sprite height,
identical for every whole-drawing template. A `claimOnly` twin template
claims Route 10's roof rows and stamps nothing, so the top half no
longer also stands as its own building. Both templates verified to
place exactly once, world-wide, at the intended coordinates.
Adds `when_above` conditional pins, resolved per position in
TileShape.at: one graphic can mean two things (the gates' $32 is both
wall base course and counter front), and a flat pin has to pick one.
Gates get half-height counters and level walls.
Pokemon Tower gains a buildings entry -- it is the drawing the map edge
cuts off, sealed on the north -- and the Indigo Plateau statues are
built like the gym statues.
Fixes: grass stands one full-height clump per tile instead of two
half-cut stubs at different depths; ledge pillars drop to ledge height;
a prop only rides furniture when its own cell is blocked (chairs were
being lifted onto tables); and the caves had water and rock pinned
backwards, which stood the Seafoam sea up as rock and cut trenches
through Mt Moon. No cave tile is below the datum now.
Grass: each 8x8 tile is one whole clump, so it stands as ONE full-height
slab at its own depth. The first cut split every tile again into its top
and bottom four art rows and stood those at two different depths, which
cut each blade in half -- two 4px stubs 4px apart rather than a tuft.
A cell's 2x2 tiles still stand independently, so the player walks
between the north and south rows.
Ledges: $34 is the cliff slope's foot AND the pillar between hop-down
segments. Pinned `wall` with the rest of the slope chain it stood 16px
beside a 6px lip, breaking the ledge line with blocks nearly three times
its height. At ledge height the run reads continuous, and the Diglett
mound is unchanged -- its foot row reads as the talus it is drawn as.
Extends the shape profile from ten tilesets to twenty-three -- 1,190
pinned tiles over ~130 maps -- surveyed against the standard the
finished interiors set: one 16px wall band carrying its built-ins,
half-cell counters, bookcase-collapsed shelving, thin-pool plant
standees, objects riding the furniture they are drawn above.
The recurring failures were rooms sunk into ponds by the three
stale-cache water ids (collision is per cell, so one trapped tile sank
its neighbours too), counters and cave edges towering to 32-48px,
racks fusing into deep monoliths, and anything in a walkable cell --
stools, tables, staircases, gravestone arches -- vanishing into the
floor.
Also fixes the door fold overwriting profile pins: it replaced the
resolved shape unconditionally, so stair pins on tiles their tileset
lists in doorTiles (Celadon Mansion, Pokemon Mansion 3F) silently did
nothing.
The trees' 2x2-cell drawings unfolded onto the ground as quarter-hull
pancakes -- each tree read as cut in half. The carver is now parametric
over its canvas: a `canopy` anchor tile carves the whole 2x2-cell
drawing as ONE 32px hull, and stamps carry their footprint radius. The
stumps' drawn tops are a cut face, not body: the `stump` class builds
the hull from the bark rows and projects the ring ellipse across the
round flat top, near arc south (stump_cap = the ellipse's drawn rows).
The forest was raw detector output: ragged mixed-height tree boxes
with 0px rim collars, stump rows fused into crate walls, and signs
broken by the $32 water-fallback trap. Every tile of the tree drawing
and the stumps now takes the per-cell voxel hull the border forest
wears (a tree's four quarter-hulls tile into one lumpy canopy), signs
take the signpost slab, and the white filler is flat ground.
The MART tileset shares the Center's atlas but not its pins, so marts
were raw detector output: 32px walls, the racks fused into one deep
monolith, the clerk booth a 48px slab. Pinned like the references --
16px wall band with the built-in cases, bookcase-collapsed racks,
half-cell counter with the poster on top, register standing on it.
The cliff drawing's dark east slope ($02/$24/$34) broke the repeat scan
at its corners and rose to 32px towers; a folded doorway column read
its own drawn extent and towered a block over the 16px mound around
Diglett's Cave. The slope chain is pinned to one 16px course, and a
doorway column now takes height AND top from its region: roofed among
drawn facades (houses), flat among flat repeats (the mound). Adds the
voxel_mound_probe driver that prints per-column detector decisions.
Flowers stand as 1px animated cutouts on their own pulled mesh (own-cell
flowers hide behind the walker, the cell south still overdraws feet).
Cuttable bushes, gym statues (single-block plinth) and Lt. Surge's trash
cans stand as 5-voxel prop cutouts; prop_ground names the tile painted
beneath. Block edits refresh meshes in place instead of blinking to 2D.