Skip to content

feat(vapor): add reactive GBA RPG proof of concept - #218

Draft
doodlewind wants to merge 5 commits into
mainfrom
navy-client
Draft

feat(vapor): add reactive GBA RPG proof of concept#218
doodlewind wants to merge 5 commits into
mainfrom
navy-client

Conversation

@doodlewind

@doodlewind doodlewind commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add reactive held-input walking through the official onFrame and BTN PocketJS APIs, with no device SDK concepts in app code
  • move the player at 2 pixels per semantic frame, chain held steps without an idle gap, finish an accepted step after release, and commit collision/events only at integer-cell arrival
  • add four directional four-frame walk cycles for the enlarged 32x32 hero, generated through the PixelLab persistent-character animation pipeline and normalized to stable anchors and a shared foot line
  • make the 15x10 camera follow the same fractional timeline while the hero remains pinned at focus; commit scroll, OAM, and BG shadows in VBlank with DMA3
  • keep the fixed HUD visible throughout movement, make the native RpgScreen explicitly own the full display, and preserve dialogue/battle behavior
  • expand deterministic libmGBA, compiler, asset provenance, video-hash, HUD, OAM, VRAM, camera, and full gameplay-loop receipts

Architecture

Gameplay remains ordinary reactive ref / computed state compiled to C. The app stores one deterministic walkPx presentation ref: -1 while idle and 0, 2, ..., 14 during an accepted step. Only the final frame commits playerX or playerY and dispatches the destination event. Collision and quest semantics therefore stay on integer cells, while presentation can update every frame.

The GBA host maps held hardware state to the shared PocketJS BTN mask and invokes the compiled app_on_frame(u32 buttons) hook. RpgScreen receives fractional player offsets and a derived walk frame; the native renderer selects the correct four-direction atlas frame, scrolls an overscanned 16x11-cell BG1 cache, positions actors against the same pixel camera, and keeps UI in fixed BG0/window space. RpgScreen must be the only root render unit so generic row clearing cannot invalidate its cached HUD.

All display register, BG map, and OAM changes are shadowed and committed during VBlank. The 32x22 active map region and used OAM slots are copied with DMA3.

Pixel art pipeline

  • PixelLab /characters/animations with template walking-4-frames produced four directions with four 88x88 source frames each from the same persistent hero character.
  • The offline builder uses one scale and horizontal crop per direction, nearest-neighbor projection, hard alpha, the existing hero palette, stable torso alignment, and foot line y=31.
  • generation.json records the animation group, background-removal jobs, and SHA-256 hashes without storing the API key.
  • Provenance tests require the complete 30-file source set, verify every source hash, and cross-check all 16 walk records against the actual PNGs.
  • Final packing contains 54 BG tiles, 6 static and 16 walking 32x32 world frames, 2 64x64 battle frames, and four fixed 4bpp palette banks: 17,216 bytes copied to GBA VRAM.

Validation

  • bunx tsc -p vapor/tsconfig.json --noEmit
  • bun run vapor:rpg:assets:check
  • bun test vapor/tests/ --max-concurrency 1 --timeout 120000 - 94 passed, 0 failed, 57,152 assertions
  • bun run vapor:rpg
  • libmGBA walking receipt: offsets 0,2,4,6,8,10,12,14,0, OAM advances exactly 2px per frame, and walk tiles cycle through all four authored frames
  • continuous-hold and release receipts; destination event fires only after arrival; complete dialogue, quest, battle, victory, and turn-in tape remains green
  • camera receipt keeps hero OAM at focus while scroll follows the fractional timeline; all 18 distinct DBG_FRAME samples have distinct video hashes across two cell boundaries
  • moving-frame HUD mirror remains QUEST: TALK TO THE ELDER; world/dialog/battle hardware captures remain distinct and multi-color; tripwires remain zero
  • exact generated BG/OBJ tile and palette bytes match libmGBA VRAM readback
  • final ROM: 31,864 bytes, title VAPOR QUEST, game code PVPR01, SHA-256 d609c808a08773d4a1d4ff03bc0c8a2d69e61a06d31473601671c4cc7ac8b1dd

Current POC boundary

GBA only, tile-based movement/collision, printable English ASCII, no saves/audio/CJK, and no browser pixel renderer yet. Physical EZ-Flash boot, display, and input acceptance remain the manual real-hardware follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant