13117e475bf14cffedbae18ff39ead3dd02dc815
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0091e6d93b |
the sparkle was playing behind the transition wipe
Armed on the occupant-change edge, which happens while the screen is still mid-wipe -- so the burst spent its whole three-quarter-second life underneath it. The instrumentation is what settled it: armed=1, quads=450, which is 45 frames times 10 stars, exactly LIFE. It was never missing. It was drawing where nobody could see it. Two gates, because the first one was still wrong. Holding the clock until the scene DREW the side does not help: the battle renders under the wipe for about a second before the wipe is gone. The burst now waits until the battle is the top of the stack -- the wipe popped, somebody watching -- and only then starts. Armed and released are separate moments now, which is what they always were. Also, a shiny no longer discolours a PERSON. Both sides can be holding a trainer pic rather than a Pokemon (the foe's portrait before the send-out, the player's own back until "Go!"), and the tint was going straight through it. Shininess is a fact about a Pokemon, not its owner. sideTexture asks the same two questions it already used to label the finished texture. And the capture drivers build the party BEFORE pinning the odds. Pokemon.new is where shininess is decided, so setting odds to 1 first made the player's own Pikachu shiny too -- which tinted the player's side, which during the intro is the trainer sprite. That is what "the player trainer sprite seems discolored" was. Lineup is now Charizard, Electrode, Vaporeon, Dratini. Verified by strip: the burst lands in the frames right after the wipe clears, and the trainer back sprite is its own colour again. |
||
|
|
322defbbd0 |
size the sparkle to the Pokemon it belongs to
Flat numbers cannot work here and both previous attempts proved it in opposite directions. The first ring was 7 world units across, which is inside anything bigger than a Rattata -- and additive drawing keeps the depth test, so all sixty quads a frame were rejected and nobody saw them. Correcting by eye gave a ring 24 across starting 20 units up, which is taller than the tallest Pokemon in the game: it hung in the sky above a Ponyta with nothing underneath it. A mon on the map is 5 to 18 world pixels tall (StadiumMon REF_HEIGHT 14). One ring cannot fit a Diglett and a Gyarados, so every distance is now a fraction of the mon: Stadium pushes each side's worldHeight and worldRadius into ShinyFx every frame -- worldRadius exists precisely so "a caller can size something to its footprint" -- and the burst is measured off those. Two more things the pictures showed. The ring is an ELLIPSE with its axes measured separately: flattened enough to read as a ring from the battle's low seat, a single radius reaches only a third of the body's height, so the top and bottom stars sit on the Pokemon. And it OPENS from clear of the body rather than from a point -- springing out of nothing means every star spends the first frames stacked at the centre, which is the middle of the mon and looks exactly like the sparkles being stuck inside it. Shot against Ponyta and Gyarados, an order of magnitude apart in size: the ring hugs each silhouette from outside and no star lands on either body. |
||
|
|
6b4dfe6b9b |
shiny: tint from the body colour, and the fixes the screenshots found
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. |
||
|
|
a3fb18a589 |
shiny Pokemon, on by default
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> |