mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
examples/war: working end-to-end War card game
A complete, playable port of the card game War: title screen with 0/1/2 player menu, animated deal, sliding cards, deck-count HUD, a "WAR!" tie-break with buried cards, and a victory screen with a fanfare. Source split across examples/war/*.ne (constants, assets, audio, deck/queue logic, RNG, render helpers, and one state file per game state) and pulled in via examples/war.ne. Drives nearly every NEScript subsystem at once: custom 88-tile sprite sheet (card frames, ranks, suits, font, BIG WAR letters); felt background nametable; pulse-1 / pulse-2 / noise sfx; looping march on pulse 2; an 8-bit Galois LFSR PRNG; queue-based decks that conserve cards across rounds; a phase machine inside the Playing state that handles draw/reveal/win/war/check; and an autopilot that boots straight into 0-PLAYERS mode so the headless jsnes harness captures real gameplay at frame 180. While building this I uncovered five compiler bugs / limitations in the v0.1 implementation; each is documented with a minimal reproduction, root cause, current workaround, and proposed fix in examples/war/COMPILER_BUGS.md. The most painful was the parameter-VarId aliasing one (#1b) — two functions sharing a parameter NAME end up sharing a single zero-page slot mapping across the whole program. Once those compiler bugs are fixed, the workarounds in war/*.ne should be reverted in the same PR. https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
This commit is contained in:
parent
86db78a31f
commit
8ababdcec4
18 changed files with 2886 additions and 0 deletions
100
examples/war.ne
Normal file
100
examples/war.ne
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// War — a full NES port of the card game.
|
||||
//
|
||||
// This is a production-quality example: a title screen with a menu,
|
||||
// an animated deal, cards that slide between two decks and a central
|
||||
// play area with a running card count, a dedicated "WAR!" tie-break
|
||||
// with buried cards, and a victory screen with a fanfare. Every
|
||||
// piece of the game (logic, art, sound, states) is authored in
|
||||
// NEScript itself — no external assets, no external tooling.
|
||||
//
|
||||
// The source is split across examples/war/*.ne files. This top-level
|
||||
// file declares the hardware config, the reset-time palette and
|
||||
// background, the one big Tileset sprite block that carries every
|
||||
// custom CHR tile, the audio includes, the state includes, and the
|
||||
// `start` declaration. Every include is processed by the parser's
|
||||
// preprocess pass before lexing, so it's exactly as if the whole
|
||||
// game lived in one .ne file.
|
||||
//
|
||||
// The file layout mirrors how a real NES game would be organised:
|
||||
//
|
||||
// examples/war.ne — this file, the top-level game
|
||||
// examples/war/PLAN.md — living design doc
|
||||
// examples/war/constants.ne — layout + gameplay constants
|
||||
// examples/war/audio.ne — sfx + music declarations
|
||||
// examples/war/state.ne — global variables
|
||||
// examples/war/rng.ne — 8-bit LFSR PRNG
|
||||
// examples/war/deck.ne — queue operations on the decks
|
||||
// examples/war/compare.ne — card rank/suit extraction
|
||||
// examples/war/render.ne — card + digit drawing helpers
|
||||
// examples/war/title_state.ne — Title state + menu
|
||||
// examples/war/deal_state.ne — Deal state + dealing animation
|
||||
// examples/war/play_state.ne — Playing state + phase machine
|
||||
// examples/war/victory_state.ne — Victory state
|
||||
//
|
||||
// Controls (human players):
|
||||
// D-pad up/down — move the title menu cursor
|
||||
// A / Start — confirm a menu choice / draw the next card
|
||||
// / return to the title from the victory screen
|
||||
//
|
||||
// In 0-player mode both players are CPU and the game plays itself;
|
||||
// in 1-player mode you are player A and the CPU is player B; in
|
||||
// 2-player mode both sides are human. The headless jsnes golden
|
||||
// harness boots straight into 0-player mode (title auto-confirms
|
||||
// after ~45 frames with no input) so the captured frame is always a
|
||||
// scene from actual gameplay.
|
||||
//
|
||||
// Build: cargo run --release -- build examples/war.ne
|
||||
// Output: examples/war.nes
|
||||
|
||||
game "War" {
|
||||
mapper: NROM
|
||||
mirroring: horizontal
|
||||
}
|
||||
|
||||
// ── Palette ─────────────────────────────────────────────────
|
||||
//
|
||||
// Grouped form with a shared `universal:` so the $3F10 mirror
|
||||
// trap is handled automatically. The sprite sub-palette 0 is the
|
||||
// one the NEScript codegen hardwires into the OAM attribute byte,
|
||||
// so every sprite in the game renders through it. Four colours:
|
||||
//
|
||||
// 0 = transparent (universal — dk_green felt)
|
||||
// 1 = red (hearts, diamonds, accents)
|
||||
// 2 = white (card face, text)
|
||||
// 3 = black (outlines, spades, clubs)
|
||||
//
|
||||
// The three bg sub-palettes carry the felt, the cream banner, and
|
||||
// a warm accent for the "WAR!" flash; bg3 stays reserved for
|
||||
// future tweaks.
|
||||
palette Main {
|
||||
universal: dk_green // green felt table colour
|
||||
|
||||
bg0: [forest, green, mint] // felt table base + subtle pattern
|
||||
bg1: [dk_red, red, white] // cream / red banners
|
||||
bg2: [black, lt_gray, white] // card body on the nametable
|
||||
bg3: [dk_olive, olive, yellow] // warm accent, "WAR!" flash
|
||||
|
||||
sp0: [red, white, black] // every sprite uses this
|
||||
sp1: [dk_red, red, peach] // reserved
|
||||
sp2: [dk_blue, blue, sky_blue] // reserved
|
||||
sp3: [dk_gray, lt_gray, white] // reserved
|
||||
}
|
||||
|
||||
// Pull in everything else. Order matters only for symbol visibility:
|
||||
// constants before the state variables that use them, state variables
|
||||
// before helper functions, helper functions before state handlers.
|
||||
include "war/constants.ne"
|
||||
include "war/assets.ne"
|
||||
include "war/background.ne"
|
||||
include "war/audio.ne"
|
||||
include "war/state.ne"
|
||||
include "war/rng.ne"
|
||||
include "war/deck.ne"
|
||||
include "war/compare.ne"
|
||||
include "war/render.ne"
|
||||
include "war/title_state.ne"
|
||||
include "war/deal_state.ne"
|
||||
include "war/play_state.ne"
|
||||
include "war/victory_state.ne"
|
||||
|
||||
start Title
|
||||
Loading…
Add table
Add a link
Reference in a new issue