1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-18 06:36:57 +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:
Claude 2026-04-13 18:04:21 +00:00
parent 48832ccb13
commit ad951a835f
No known key found for this signature in database
8 changed files with 676 additions and 353 deletions

View file

@ -35,43 +35,45 @@ game "Audio Demo" {
// ── User-declared sound effects ──
//
// An `sfx` block is a frame-accurate envelope for pulse 1. `pitch`
// latches the pulse period once on trigger; `volume` runs one entry
// per frame, so the envelope length controls the effect duration.
// `duty` (0-3) picks the pulse waveform shape (2 = 50% square).
// is latched into `$4002` once on trigger — the v1 audio driver
// never updates the pulse period mid-effect — so a single scalar
// byte is the natural form for the current pipeline. `envelope:`
// is a friendlier alias for `volume:`: both describe the per-frame
// volume ramp that shapes the effect. `duty` (0-3) picks the pulse
// waveform (2 = 50% square).
// A gentle rising chirp, longer than the builtin coin.
sfx LongCoin {
duty: 2
pitch: [0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50]
volume: [15, 14, 13, 12, 11, 9, 7, 5, 3, 1]
pitch: 0x50
envelope: [15, 14, 13, 12, 11, 9, 7, 5, 3, 1]
}
// A sharp two-part zap — quick high spike into silence.
sfx Zap {
duty: 3
pitch: [0x20, 0x20, 0x20, 0x20, 0x20]
volume: [15, 12, 8, 4, 1]
pitch: 0x20
envelope: [15, 12, 8, 4, 1]
}
// ── User-declared music tracks ──
//
// A `music` block is a flat list of `(pitch, duration)` note pairs.
// Pitch 0 = rest; 1-60 = period table index (C1..B5, middle C = 37).
// Duration is in frames (so at 60 fps, 30 = half second per note).
// Music loops by default; set `repeat: false` for one-shot cues.
// Notes are written in note-name form (`C4`, `E4`, `G4`, …) with a
// default frames-per-note set by `tempo:`. Any note can override
// the default by trailing the name with an explicit duration
// (e.g. `G4 40` holds twice as long). `rest` (or the alias `_`) is
// a silent beat. Use the legacy `37, 20, 41, 20, ...` pair form by
// leaving `tempo:` out if you'd rather pick pitches by index.
// A cheerful four-note looping theme.
// A cheerful four-note looping theme — every note is one-third of
// a second long, and the whole loop is 120 frames = 2 seconds.
music Theme {
duty: 2
volume: 10
repeat: true
tempo: 20
notes: [
37, 20, // C4
41, 20, // E4
44, 20, // G4
49, 20, // C5
44, 20, // G4
41, 20, // E4
C4, E4, G4, C5, G4, E4
]
}