Files
pokepinballrs/tools/scripts
Cyphgirl 37f87ccee2 Colourize board OBJ sheets, starting with the intro sprites
The 203 segment PNGs behind the seven boards' intro_sprite sheets were
checked in as 4-bit greyscale: a 4bpp sheet carries no palette of its own,
so nothing in the tree recorded which of the 16 OBJ banks a sprite is drawn
through, and every sheet was an unreadable grey blob. All seven are now
indexed colour, byte-for-byte identical through the build.

The inversion
- gbagfx converts a palette-less PNG with invertColors = !image.hasPalette
  (main.c:71), so a greyscale sheet stores 15 - index, and gaining a PLTE
  turns that off. The recolour inverts on the way in; passing the samples
  through unchanged would have quietly rewritten every tile.

Deriving the bank (tools/scripts/objpalette.py)
- data/rom_2.s spells out every OAM entry with its tileNum and paletteNum,
  and the g<Board>BoardSpriteSets tables say which a board uses. Segments lie
  end to end in tile order, so each owns a tile range and the entries landing
  wholly inside it name its bank. An entry crossing a boundary is reading a
  different sheet -- OBJ VRAM at 0x06010000 is rewritten constantly -- and is
  dropped. Second pass over the g<Board>*OamData arrays for the boss sheets,
  which the code indexes frame by frame instead of via a sprite set.
- Tile counts come from the built per-segment .4bpp: -oam and -oamshape
  segments are not width*height/64, and PNG dimensions drift ruby by 13 tiles.
- Banks holding under four distinct colours cannot win the vote. Those are
  flash states and banks a board leaves at zero, and would render solid.
- The result is recorded in each gfx config as "palette"/"palbank", so it is
  reviewable and `objpalette.py check` can verify the PNGs against it.

Scratch regions
Eight tile ranges are streamed over at run time from a table of interchangeable
art, and coloured by a palette that arrives with the replacement tiles rather
than sitting in the board's own set. Which entry the sheet ships is recoverable
by searching the ROM for the segment's tiles -- exact for all but the Geodude
portrait, matched by likeness. Listed with their writers in VRAM_LAYOUT.md.

OBJ bank 11 on the main fields is never its resting ROM value either:
UpdateSpoinkAnimation rewrites it every frame from gFieldPaletteVariants,
three brightness sets crossed with which half of the board is on screen.

Ruby's palset_0 is 0x120 bytes, not the 0x200 the DMA copies, so its OBJ banks
9-15 come from the next label, gBonusStageObjPal. Sapphire leaves banks 12-15
at zero and loads them when the sprites that use them appear.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:45:17 -04:00
..