Checkpoint current interactive fiction state
This commit is contained in:
@@ -1,38 +1,186 @@
|
||||
# AI Interactive Fiction
|
||||
|
||||
A modern take on classic text adventures that combines traditional world modeling with Large Language Models (LLMs) to create natural language interactive fiction experiences.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This application reimagines the classic text adventure game genre by replacing the traditional parser with an LLM. The system consists of:
|
||||
|
||||
1. **World Model**: A traditional game engine that manages rooms, objects, actions, and game state - similar to old-school Infocom games.
|
||||
|
||||
2. **LLM Interface**: An AI layer that processes natural language input from players and translates it into actions the game engine can understand.
|
||||
|
||||
3. **Narrative Generation**: The LLM converts the world state changes into rich, contextual prose for the player.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Natural Language Understanding**: Players can express their intent in plain language without worrying about specific command syntax.
|
||||
- **Rich Narrative**: Dynamic descriptions that adapt to the current game state and player history.
|
||||
- **Consistent World Model**: The underlying game engine enforces world rules to prevent hallucinations or inconsistencies.
|
||||
- **Modular Design**: Easily swap between different world models, including YAML-based custom worlds or integrations with classic Z-machine games.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Player enters natural language input
|
||||
2. LLM analyzes input and translates it into game actions
|
||||
3. Game engine processes valid actions and updates the game state
|
||||
4. LLM receives the state change information and generates narrative prose
|
||||
5. Player receives the beautifully written response
|
||||
|
||||
## Technical Structure
|
||||
|
||||
- YAML-based world definition (rooms, objects, actions)
|
||||
- OpenRouter API integration for accessing suitable LLMs
|
||||
- Modular design allowing for Z-machine integration in the future
|
||||
|
||||
## Getting Started
|
||||
|
||||
[Installation and running instructions will be added here]
|
||||
# AI Interactive Fiction
|
||||
|
||||
AI Interactive Fiction is a web and CLI text adventure prototype that combines a deterministic world model with LLM-assisted command interpretation and narrative output. The web client presents the story as an animated, novel-like book page with synchronized text animation, optional TTS, music, and sound effects.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Use Node.js 22 LTS for development. The project accepts Node >= 18.17, but current development has been done on Node 22.
|
||||
|
||||
```powershell
|
||||
nvm install 22
|
||||
nvm use 22
|
||||
npm install
|
||||
npm run build
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The web app starts on `http://localhost:3000` when launched through `src/index.ts` or `dist/index.js`. The lower-level server module defaults to `3001` when started directly. Set `PORT` to choose a port; the server will try the next few ports if the requested one is already in use.
|
||||
|
||||
## Commands
|
||||
|
||||
```powershell
|
||||
npm run dev # Start the web UI through ts-node/nodemon
|
||||
npm run start # Run the compiled web server from dist/
|
||||
npm run build # Compile TypeScript
|
||||
npm run test # Run Jest tests
|
||||
npm run lint # Run ESLint on src/
|
||||
npm run start:cli # Run the CLI interface
|
||||
npm run dev:cli # Run the CLI interface through ts-node/nodemon
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Environment variables are loaded from `.env`.
|
||||
|
||||
- `PORT`: preferred web server port.
|
||||
- `DEFAULT_WORLD_FILE`: YAML world file to load. Defaults to `./data/worlds/example_world.yml`.
|
||||
- `OPENROUTER_API_KEY`: API key for LLM command interpretation.
|
||||
- `OPENROUTER_MODEL`: OpenRouter model name.
|
||||
|
||||
TTS provider settings are configured in the browser options menu and persisted in browser storage. Providers currently include `none`, browser speech synthesis, Kokoro, ElevenLabs, and OpenAI. Production should not assume a universal TTS default; the game or player state selects the active mode, and `none` is the safe fallback.
|
||||
|
||||
## Starting A Game
|
||||
|
||||
The web client no longer starts the game automatically. Browsers require a user gesture before audio playback, so the right page initially shows a start prompt and the command input is hidden. Use `new game` or `load` in the top bar to start.
|
||||
|
||||
The placeholder server API supports:
|
||||
|
||||
- `newGame()`
|
||||
- `loadGame(slot)`
|
||||
- `saveGame(slot)`
|
||||
- `hasSaveGame(slot)`
|
||||
- `getSaveGames()`
|
||||
- `isGameRunning()`
|
||||
|
||||
Save slots are positive integers. In the current placeholder implementation, save availability is per socket session and is lost on reload. Loading a save starts the demo content like `newGame()`.
|
||||
|
||||
## Web Client
|
||||
|
||||
The browser app is built from native ES modules in `public/js/`. The loader dynamically imports modules, applies a cache-busting query string during development, resolves declared dependencies, and awaits module initialization in dependency order before the UI becomes usable.
|
||||
|
||||
Major modules:
|
||||
|
||||
- `module-registry.js`, `base-module.js`, `loader.js`: module lifecycle, dependency graph, progress overlay, state reporting.
|
||||
- `text-processor-module.js`, `paragraph-layout-module.js`, `layout-renderer-module.js`: SmartyPants, language-aware hyphenation, Knuth-Plass line breaking, DOM rendering.
|
||||
- `markup-parser-module.js`: story markup for chapters, sections, Markdown emphasis, images, SFX, and music.
|
||||
- `sentence-queue-module.js`, `playback-coordinator-module.js`, `animation-queue-module.js`: sentence preparation, synchronized playback, timing, fast-forward.
|
||||
- `tts-factory-module.js` plus provider modules: TTS provider selection, voice settings, speed mapping, caching, and playback.
|
||||
- `audio-manager-module.js`: master, speech, music, and sound effect volume, music playback, sound effects, and music ducking.
|
||||
- `ui-controller-module.js`, `ui-display-handler-module.js`, `ui-input-handler-module.js`, `options-ui-module.js`: book UI, command input, options, top-bar controls, and game API calls.
|
||||
|
||||
The static server sends no-cache headers for local development so stale ES modules do not mask changes. If the browser console shows `onpage-dialog.preload.js:121 Uncaught ReferenceError: browser is not defined`, ignore it; that comes from the installed ad blocker, not this project.
|
||||
|
||||
## Story Markup
|
||||
|
||||
Plain paragraphs are rendered paragraph by paragraph. Normal following paragraphs are horizontally indented and do not get a blank line between them. Special block markers change the treatment of the next paragraph.
|
||||
|
||||
Inline Markdown emphasis:
|
||||
|
||||
```text
|
||||
*italic* or _italic_
|
||||
**bold** or __bold__
|
||||
***bold italic*** or ___bold italic___
|
||||
```
|
||||
|
||||
Chapter:
|
||||
|
||||
```text
|
||||
::chapter[The Mysterious Mansion]
|
||||
|
||||
The first paragraph uses a drop cap and no first-line indent.
|
||||
|
||||
Following paragraphs use the normal paragraph indent.
|
||||
```
|
||||
|
||||
The heading is centered, italic, and uses the same text face as the body. The first paragraph after a chapter marker is unindented and receives the drop cap treatment.
|
||||
|
||||
Section or text block:
|
||||
|
||||
```text
|
||||
::section
|
||||
|
||||
The first paragraph starts a separated block without horizontal indent.
|
||||
|
||||
The following paragraph returns to the normal indent.
|
||||
```
|
||||
|
||||
`::textblock` is treated the same way. The first paragraph after the marker is separated from previous content by one line of vertical space.
|
||||
|
||||
Images are parsed for future rendering:
|
||||
|
||||
```text
|
||||
::image[widescreen](mansion-rain.jpg)
|
||||
::image[portrait](portrait-letter.jpg)
|
||||
```
|
||||
|
||||
Image file names are relative to `public/images/`. `widescreen` means 100% page width and 50% page height. `portrait` means 100% page width and 100% page height.
|
||||
|
||||
Sound effects can be placed inline:
|
||||
|
||||
```text
|
||||
The door opens {{sfx:squeaky-door.ogg}} and the hall exhales.
|
||||
```
|
||||
|
||||
The marker is removed from display text and TTS text. It becomes a timed media cue that fires when the text animation reaches that point. Sound effect paths are relative to `public/sounds/`.
|
||||
|
||||
Music can be placed as a block:
|
||||
|
||||
```text
|
||||
::music[crossfade, loop, lead=4](rain-theme.ogg)
|
||||
```
|
||||
|
||||
Music can also be placed inline:
|
||||
|
||||
```text
|
||||
The candles gutter. {{music:cut:danger.ogg}} Something moves upstairs.
|
||||
```
|
||||
|
||||
Music paths are relative to `public/music/`. Supported modes are `queue`, `crossfade`, and `cut`. Use `loop` or `once` to control repetition. `lead=<seconds>` delays the following text/TTS paragraph so the music can play alone before narration continues.
|
||||
|
||||
## Assets
|
||||
|
||||
- `public/sounds/`: sound effects referenced by `{{sfx:file}}`.
|
||||
- `public/music/`: background music referenced by `::music[...]` or `{{music:mode:file}}`.
|
||||
- `public/images/`: story images referenced by `::image[...]`.
|
||||
- `public/fonts/`: font assets used by the book UI.
|
||||
|
||||
Keep third-party assets licensed for local redistribution, and document source and license in the folder README or alongside the file.
|
||||
|
||||
## Typography And Playback Behavior
|
||||
|
||||
The renderer is designed to behave like a scaled static book page. The page keeps its aspect ratio, and text sizes and word positions scale relative to the page instead of reflowing unpredictably at small browser sizes.
|
||||
|
||||
Text processing order:
|
||||
|
||||
1. Parse story markup and remove non-display media markers.
|
||||
2. Apply Markdown emphasis spans.
|
||||
3. Run SmartyPants for typographic punctuation.
|
||||
4. Apply Hyphenopoly for the selected language.
|
||||
5. Calculate line breaks with the Knuth-Plass algorithm.
|
||||
6. Render absolutely positioned word spans and animate them in sync with audio or estimated duration.
|
||||
|
||||
When real TTS audio is available, animation duration is driven by measured audio length. With TTS disabled or unavailable, duration is estimated from text length and the persisted speed setting.
|
||||
|
||||
Fast-forwarding by page click or space completes the active animation and fades/stops current TTS playback so queued content can proceed.
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-05-14
|
||||
|
||||
- Consolidated usage, markup, and architecture documentation into `README.md` and `CLIENT_TODO.md`.
|
||||
- Added no-cache static serving and module URL cache busting so browser reloads pick up JS changes reliably during development.
|
||||
- Fixed module loader dependency ordering so modules are initialized only after their declared dependencies are ready.
|
||||
- Added the placeholder game API for `newGame`, `loadGame`, `saveGame`, `hasSaveGame`, `getSaveGames`, and `isGameRunning`.
|
||||
- Changed the web UI to require a manual game start before showing the command input, which keeps browser audio autoplay restrictions manageable.
|
||||
- Implemented story markup for chapters, text blocks, Markdown emphasis, image placeholders, sound effects, and music cues.
|
||||
- Added music block parameters for playback mode, loop/once behavior, and lead-in delay.
|
||||
- Added sound and music asset folders and playback plumbing for sound effects and background music.
|
||||
- Added music ducking while TTS is active.
|
||||
- Reworked book typography around Knuth-Plass line breaking, Hyphenopoly hyphenation, SmartyPants, paragraph indentation rules, drop caps, and responsive page scaling.
|
||||
- Reworked TTS provider behavior, speed mapping, persistence, caching keys, top-bar/options synchronization, and OpenAI voice validation.
|
||||
- Added development notes for ignoring the unrelated ad-blocker console error.
|
||||
|
||||
### Earlier Prototype Work
|
||||
|
||||
- Established the original animated fiction prototype with inkjs, SmartyPants, Hyphenopoly, Knuth-Plass line breaking, custom animation scheduling, save/load concepts, and media tags.
|
||||
- Split the client from a monolithic prototype into focused modules for text processing, layout, animation, audio, persistence, TTS, and UI control.
|
||||
|
||||
Reference in New Issue
Block a user