Context:
We wanted a "livre dont vous etes le heros" playable on the Freenove
master with NO model in RAM: narration as say()-rendered WAV staged on
the SD card, and navigation on the physical 5-way pad. Nothing in the
firmware drove a branching, button-navigated story before this.
Approach:
A self-contained gamebook component reads a JSON book from the SD,
drives the display and audio, and owns the 5-way pad through a
registered display_ui key hook (so display_ui keeps no dependency on
gamebook — no cycle). A dedicated full-screen gamebook view renders
each page (title / wrapped body / choice list).
Changes:
- New gamebook component: loads /sdcard/gamebook/gamebook.json; per
passage plays its WAV (media_manager, absolute /sdcard path) and
renders the title, wrapped text, and a choice list with a ">" cursor.
- Navigation: D-pad up/down (and left/right) move the cursor, the
center click confirms; endings (no choices) restart on click; a
choice interrupts the current narration (media_manager_stop + play).
- display_ui: add display_ui_set_key_hook (consume keys when a takeover
mode is active) and a dedicated gamebook view (title + height-bounded
body band + reserved bottom menu band) with display_ui_gamebook_show
/ display_ui_gamebook_hide; the gamebook view wins the view selector
while open.
- game_endpoint: POST /game/gamebook[?action=stop] to start/stop.
- ota_server: bump max_uri_handlers 20 -> 22 for the new endpoint.
- main: call gamebook_init() once the buttons are up.
Impact:
The Freenove plays a branching audio gamebook driven entirely by the
pad, fully offline (no gateway, no model). Hardware-validated end to
end: cursor navigation, click select, WAV narration from SD, endings
restart.