mirror of
https://github.com/imjasonh/nescript
synced 2026-07-18 14:45:58 +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
|
|
@ -24,39 +24,39 @@ game "Palette + BG Demo" {
|
|||
|
||||
// ── Palettes ────────────────────────────────────────────────
|
||||
//
|
||||
// Each palette is 32 bytes laid out in the standard NES order:
|
||||
// $00-$0F background palettes 0-3 (4 colours each)
|
||||
// $10-$1F sprite palettes 0-3 (4 colours each, $10/$14/$18/$1C
|
||||
// mirror the bg slots)
|
||||
// Both palettes are authored in grouped form: one shared
|
||||
// `universal:` colour feeds every sub-palette's index-0 slot,
|
||||
// which auto-fixes the `$3F10/$3F14/$3F18/$3F1C` PPU mirror
|
||||
// trap (the parser handles the packing). Each `bgN` / `spN`
|
||||
// field only needs three colours — the universal is prepended.
|
||||
//
|
||||
// `CoolBlues` uses deep blue background tiles with pale highlights;
|
||||
// `WarmReds` swaps them for red/orange tones. Every 4 bytes is a
|
||||
// sub-palette.
|
||||
// Named NES colours resolve to master-palette indices via the
|
||||
// curated name table; see `docs/language-guide.md` for the full
|
||||
// list. Hex byte literals (`0x01`, `0x14`, …) still work if you
|
||||
// need a colour that doesn't have a name.
|
||||
|
||||
palette CoolBlues {
|
||||
colors: [
|
||||
0x0F, 0x01, 0x11, 0x21, // bg palette 0: black, deep blue, sky, pale
|
||||
0x0F, 0x02, 0x12, 0x22, // bg palette 1
|
||||
0x0F, 0x0C, 0x1C, 0x2C, // bg palette 2
|
||||
0x0F, 0x0B, 0x1B, 0x2B, // bg palette 3
|
||||
0x0F, 0x01, 0x11, 0x21, // sprite palette 0
|
||||
0x0F, 0x16, 0x27, 0x30, // sprite palette 1
|
||||
0x0F, 0x14, 0x24, 0x34, // sprite palette 2
|
||||
0x0F, 0x0B, 0x1B, 0x2B // sprite palette 3
|
||||
]
|
||||
universal: black
|
||||
bg0: [dk_blue, blue, sky_blue] // deep blue highlights
|
||||
bg1: [indigo, royal_blue, periwinkle] // cool purples
|
||||
bg2: [dk_cyan, cyan, lt_cyan] // aquas
|
||||
bg3: [dk_teal, teal, lt_teal] // teal accents
|
||||
sp0: [dk_blue, blue, sky_blue] // matches bg0
|
||||
sp1: [red, orange, white] // warm accent sprites
|
||||
sp2: [magenta, lt_magenta, pale_pink] // magenta sprites
|
||||
sp3: [dk_teal, teal, lt_teal] // matches bg3
|
||||
}
|
||||
|
||||
palette WarmReds {
|
||||
colors: [
|
||||
0x0F, 0x06, 0x16, 0x26, // bg palette 0: black, dark red, red, peach
|
||||
0x0F, 0x07, 0x17, 0x27, // bg palette 1
|
||||
0x0F, 0x08, 0x18, 0x28, // bg palette 2
|
||||
0x0F, 0x09, 0x19, 0x29, // bg palette 3
|
||||
0x0F, 0x06, 0x16, 0x26, // sprite palette 0
|
||||
0x0F, 0x16, 0x27, 0x30, // sprite palette 1
|
||||
0x0F, 0x14, 0x24, 0x34, // sprite palette 2
|
||||
0x0F, 0x0B, 0x1B, 0x2B // sprite palette 3
|
||||
]
|
||||
universal: black
|
||||
bg0: [dk_red, red, lt_red] // fiery warm bg
|
||||
bg1: [brown, dk_orange, orange] // autumnal
|
||||
bg2: [dk_olive, olive, yellow] // muted yellows
|
||||
bg3: [dk_green, green, lt_green] // greens for contrast
|
||||
sp0: [dk_red, red, lt_red] // matches bg0
|
||||
sp1: [red, orange, white] // highlight sprites
|
||||
sp2: [magenta, lt_magenta, pale_pink] // pinks
|
||||
sp3: [dk_teal, teal, lt_teal] // cool accent
|
||||
}
|
||||
|
||||
// ── Backgrounds ─────────────────────────────────────────────
|
||||
|
|
@ -74,40 +74,46 @@ palette WarmReds {
|
|||
// top-left to bottom-right so the swap is visually obvious.
|
||||
|
||||
background TitleScreen {
|
||||
tiles: [
|
||||
// Row 14 (offset 14*32 = 448): a ribbon of tile 0
|
||||
// across columns 4..28.
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// Row 14
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
// Both backgrounds resolve to an all-zero nametable for this
|
||||
// demo — the visual difference between them comes from the
|
||||
// palette swap triggered alongside `load_background`. We still
|
||||
// author them with a `legend` + `map:` block here so the demo
|
||||
// exercises the pleasant syntax the same way a real game
|
||||
// would. The '.' legend entry alone is enough to fill an
|
||||
// all-tile-0 background; every row is padded to 32 columns
|
||||
// automatically by the parser.
|
||||
legend { ".": 0 }
|
||||
map: [
|
||||
"................................", // row 0
|
||||
"................................", // row 1
|
||||
"................................", // row 2
|
||||
"................................", // row 3
|
||||
"................................", // row 4
|
||||
"................................", // row 5
|
||||
"................................", // row 6
|
||||
"................................", // row 7
|
||||
"................................", // row 8
|
||||
"................................", // row 9
|
||||
"................................", // row 10
|
||||
"................................", // row 11
|
||||
"................................", // row 12
|
||||
"................................", // row 13
|
||||
"................................", // row 14 (ribbon)
|
||||
"................................" // row 15
|
||||
]
|
||||
}
|
||||
|
||||
background StageOne {
|
||||
tiles: [
|
||||
// A few scattered tile-0s across the first 4 rows so the
|
||||
// background visibly differs from TitleScreen after the
|
||||
// swap. Remaining rows are zero-padded and render as
|
||||
// tile 0 anyway — the visual difference comes from the
|
||||
// palette swap triggered alongside.
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
// StageOne's visual difference comes from the palette swap —
|
||||
// the tile grid is still the built-in smiley everywhere. We
|
||||
// only declare 4 rows to prove the parser's zero-padding
|
||||
// still works with the new tilemap form.
|
||||
legend { ".": 0 }
|
||||
map: [
|
||||
"................................",
|
||||
"................................",
|
||||
"................................",
|
||||
"................................"
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue