mirror of
https://github.com/imjasonh/nescript
synced 2026-07-09 09:18:01 +00:00
examples: adopt the friendly asset syntax
Rewrites every example with non-trivial asset declarations to use
the pleasant QoL syntax introduced in the previous commit. Every
example still compiles to a byte-identical ROM (verified by a
temp-path diff before committing), so the committed `.nes` files
and the 23 emulator goldens are unchanged.
* platformer.ne — the centerpiece end-to-end demo:
- `palette Main` goes to grouped form with a shared
`universal: 0x22` (sky blue), one shared colour per
sub-palette, and named NES colours throughout; the
long-standing `$3F10` mirror-trap warning is now handled
by the parser and the manual pitfall comment is gone.
- `sprite Tileset` is 15 tiles of ASCII pixel art instead
of 240 bytes of inline hex.
- `background Level` uses a `legend { '.': 15, '#': 9, ... }`
block plus `map:` strings for the 32×30 nametable, and
`palette_map:` rows for the attribute table. The map
reads top-down like the rendered screen.
- SFX latch-once `pitch: 0x30` scalars + `envelope:` alias.
- `music Theme` uses note names + `tempo: 10` default.
* audio_demo.ne — scalar sfx pitches, `envelope:` alias, and a
note-name `C4, E4, G4, ...` music track.
* palette_and_background.ne — grouped CoolBlues / WarmReds
palettes with `universal: black` + named colours, plus
`legend` + `map:` tilemaps for the two backgrounds.
* sprites_and_palettes.ne — Arrow and Heart sprites rewritten
as `pixels:` ASCII art.
Along the way, two small parser extensions support the rewrites:
- `parse_pixel_art` now accepts `a/b/c` as aliases for `#/%/@`,
matching the vocabulary every NES editor (and our own
gen_platformer_tiles.rs generator) uses.
- `palette_map_to_attrs` allows up to 16 metatile rows (the
full attribute-table coverage, including the off-screen
bottom half) and auto-replicates row 14 → row 15 when only
15 rows are supplied so the visible bottom of the screen
gets consistent sub-palette assignments by default. The old
15-row cap couldn't match a hand-packed `0xAA` attribute
table for the last row; the platformer required this to
stay byte-identical.
`scripts/gen_platformer_tiles.rs` is updated to emit the new
syntax directly (pixel-art `pixels:` block + `legend`/`map:`/
`palette_map:` for the background), so regenerating the
platformer tiles stays a one-liner.
474 lib tests + 64 integration tests pass (3 new parser tests
for `palette_map:` 15/16/17 rows and the `abc` alias). All 23
emulator goldens still match pixel- and sample-for-sample.
https://claude.ai/code/session_01PzaSFj3VahDzxEYTKCESkz
This commit is contained in:
parent
48832ccb13
commit
ad951a835f
8 changed files with 676 additions and 353 deletions
|
|
@ -822,18 +822,24 @@ impl Parser {
|
|||
for (ry, row) in rows.iter().enumerate() {
|
||||
let mut line = Vec::with_capacity(width);
|
||||
for (rx, ch) in row.chars().enumerate() {
|
||||
// Three vocabularies map to the same 0-3 index so
|
||||
// artists can use whichever feels natural:
|
||||
// `. # % @` — shade-intensity glyphs (dense = hi)
|
||||
// `0 1 2 3` — literal palette-index digits
|
||||
// `. a b c` — letter form used by most NES tools
|
||||
let val = match ch {
|
||||
'.' | ' ' | '0' => 0u8,
|
||||
'#' | '1' => 1,
|
||||
'%' | '2' => 2,
|
||||
'@' | '3' => 3,
|
||||
'#' | '1' | 'a' | 'A' => 1,
|
||||
'%' | '2' | 'b' | 'B' => 2,
|
||||
'@' | '3' | 'c' | 'C' => 3,
|
||||
other => {
|
||||
return Err(Diagnostic::error(
|
||||
ErrorCode::E0201,
|
||||
format!(
|
||||
"sprite '{sprite_name}' pixel at ({rx}, {ry}) has \
|
||||
invalid character '{other}'; use '.'/' '/'0' for \
|
||||
index 0, '#'/'1' for 1, '%'/'2' for 2, '@'/'3' for 3"
|
||||
index 0, '#'/'1'/'a' for 1, '%'/'2'/'b' for 2, \
|
||||
'@'/'3'/'c' for 3"
|
||||
),
|
||||
key_span,
|
||||
));
|
||||
|
|
@ -2947,18 +2953,21 @@ fn tilemap_to_bytes(
|
|||
/// The attribute layout is notoriously awkward: each attribute byte
|
||||
/// covers a 32×32-pixel region (four 16×16 metatiles) packed as
|
||||
/// `BR BL TR TL` — top-left in the low bits, bottom-right in the high
|
||||
/// bits. A 32×30-pixel nametable has a 16×15 metatile grid but the
|
||||
/// attribute table is a fixed 8×8 = 64 bytes, so the last half-row
|
||||
/// of metatiles (rows 14-15, which render below the visible area) is
|
||||
/// always zero-padded.
|
||||
/// bits. The attribute table is a fixed 8×8 = 64 bytes covering 16
|
||||
/// metatile rows, even though only the top 15 (the visible 240
|
||||
/// scanlines) render on screen. Programs may declare up to 16 rows
|
||||
/// so the off-screen half picks up sensible attribute bytes; if
|
||||
/// exactly 15 are given, the parser auto-replicates row 14 down
|
||||
/// into row 15 so the last attribute byte stays consistent with
|
||||
/// what's visible.
|
||||
fn palette_map_to_attrs(bg_name: &str, rows: &[String], span: Span) -> Result<Vec<u8>, Diagnostic> {
|
||||
// 16 metatile cols × up to 15 metatile rows.
|
||||
if rows.len() > 15 {
|
||||
if rows.len() > 16 {
|
||||
return Err(Diagnostic::error(
|
||||
ErrorCode::E0201,
|
||||
format!(
|
||||
"background '{bg_name}' palette_map has {} rows; maximum is 15 \
|
||||
(one entry per 16×16 metatile row)",
|
||||
"background '{bg_name}' palette_map has {} rows; maximum is 16 \
|
||||
(15 visible metatile rows + 1 off-screen row for the bottom \
|
||||
half of the last attribute byte)",
|
||||
rows.len()
|
||||
),
|
||||
span,
|
||||
|
|
@ -3002,6 +3011,14 @@ fn palette_map_to_attrs(bg_name: &str, rows: &[String], span: Span) -> Result<Ve
|
|||
grid[ry][rx] = idx;
|
||||
}
|
||||
}
|
||||
// If the user gave exactly 15 rows, replicate row 14 into row 15
|
||||
// so the last attribute byte's bottom-half picks up the same
|
||||
// sub-palette as the visible bottom of the screen. Users who
|
||||
// want explicit control over the off-screen row can supply all
|
||||
// 16 rows.
|
||||
if rows.len() == 15 {
|
||||
grid[15] = grid[14];
|
||||
}
|
||||
// Pack into the 8×8 attribute table. Each attribute byte covers
|
||||
// a 2×2 block of metatiles:
|
||||
// bits 0-1 = top-left (grid[ay*2 ][ax*2 ])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue