Checks / web (push) Canceled after 0s
Port forced movement (turn delays, bumps, ice, currents, pits), dynamic return warps, elevators, Surf, Cut, Strength and cycling rules, working map blocks with changeblock/changemapblocks, and the remaining NPC movement types and movement commands. Add tileset animation from the ROM's command tables, grass sprite priority and special map palettes; room decorations with ownership, the bedroom PC and save migration; and Key Items access to the Bicycle. Fourteen original-engine traces compare positions, facing, actors, event flags and scripted poses. Tile animation matches original VRAM for four tilesets, and LoadMapPals matches 192 palette cases. skyfall_top now keeps the $60 sprite offset its step function leaves behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
1.6 KiB
TypeScript
50 lines
1.6 KiB
TypeScript
import type { Palette } from "../world/types";
|
|
|
|
/** Decode Game Boy planar pixels directly to RGBA; no display hardware emulation. */
|
|
export function drawTile(
|
|
target: Uint8ClampedArray,
|
|
width: number,
|
|
height: number,
|
|
graphics: Uint8Array,
|
|
tile: number,
|
|
x: number,
|
|
y: number,
|
|
palette: Palette,
|
|
options: {
|
|
transparent?: boolean;
|
|
flipX?: boolean;
|
|
flipY?: boolean;
|
|
/** Background color indices per pixel: a background tile records them;
|
|
* an OAM_PRIO sprite (`behind`) only draws over color 0. */
|
|
indices?: Uint8Array;
|
|
behind?: boolean;
|
|
} = {},
|
|
) {
|
|
const offset = tile * 16;
|
|
if (offset < 0 || offset + 16 > graphics.length) return;
|
|
for (let row = 0; row < 8; row++) {
|
|
const sourceRow = options.flipY ? 7 - row : row;
|
|
const lo = graphics[offset + sourceRow * 2],
|
|
hi = graphics[offset + sourceRow * 2 + 1];
|
|
for (let col = 0; col < 8; col++) {
|
|
const bit = options.flipX ? col : 7 - col;
|
|
const index = ((lo >> bit) & 1) | (((hi >> bit) & 1) << 1);
|
|
if (options.transparent && index === 0) continue;
|
|
const px = x + col,
|
|
py = y + row;
|
|
if (px < 0 || py < 0 || px >= width || py >= height) continue;
|
|
if (options.indices) {
|
|
if (options.behind) {
|
|
if (options.indices[py * width + px]) continue;
|
|
} else if (!options.transparent) options.indices[py * width + px] = index;
|
|
}
|
|
const destination = (py * width + px) * 4,
|
|
color = palette[index];
|
|
target[destination] = color[0];
|
|
target[destination + 1] = color[1];
|
|
target[destination + 2] = color[2];
|
|
target[destination + 3] = 255;
|
|
}
|
|
}
|
|
}
|