This project is a local-first proof of concept for a retro, BBS-style ASCII RPG. The goal is to validate:
- screen layout
- rendering approach
- input model
- engine/UI separation
Once validated locally, the same engine and assets can be migrated to:
- a web-based terminal UI
- an SSH-based BBS-style interface
- Fixed-size ASCII screen (100 columns × 30 rows)
- Deterministic rendering (no scrolling UI in core gameplay screens)
- Single-key input (no Enter required)
- Nostalgic presentation with modern code structure
- Clean separation between game logic and presentation
main.py— game loop and state orchestrationapp/bootstrap.py— app initialization and wiringapp/config.py— filesystem paths and configapp/models.py— core dataclasses (Player,Opponent,Frame)app/combat.py— combat helpers and timingapp/input.py— single-key input handlingapp/shop.py— shop interaction helpersapp/commands/— command registry and command modulesapp/loop.py— main loop helper functionsapp/ui/— layout, rendering, and screen composition helpersapp/data_access/— JSON data loadersdata/— JSON content packsdata/music.json— data-driven music/sfx patterns, sequences, and songssaves/— local save slotsdocs/— web build (Pyodide assets + terminal UI)tests/— unit tests
Audio cues are defined in data/music.json and synthesized at runtime (no binary assets).
- Patterns / Sequences / Songs: define note patterns, wrap them as sequences (tempo/scale/wave), and group sequences into songs.
- Note tuples:
[degree, beats=1, octave_shift=0, accidental=0]with rest as[0, beats]. - Staccato: set
staccato: trueon a sequence or song step to split each note 50/50 (tone + rest). - Repeat: songs can be lists of steps or
{ repeat, steps }to loop a song.
CLI utility:
python3 music.py sequence <sequence_name> <root_note>
python3 music.py song <song_name>Runtime behavior:
- Audio mode cycles via Options/Title: On / Off / Music Only / SFX Only (stored in
player.flags.audio_mode). - Web build uses
docs/audio.js(Web Audio) with the samedata/music.jsonschema.
- Battle start plays
battle_minor. - Battle victory plays
battle_victoryunless a level-up occurs. - Level-up plays
level_up. - ATTACK plays
attack_sfx. - Quest list open plays
quest_open. - Quest detail (continent 1/base) plays
continent_1_quest. - Town (continent 1/base) plays
town_continent_1.
- Make gameplay/code changes in the root tree first.
- Sync runtime files into
docs/withscripts/build.sh(preserves web-only assets like HTML/CSS/JS). - Web UI includes a tips modal with keyboard guidance and a Browser Note about Pyodide + WebKit constraints.
- Debug modal explains save incompatibilities and offers a “Clear Saved Games” action (IDBFS reset + reload).
- Title screen with Continue/New/Quit, save slot selection, and overwrite confirmation
- Title screen uses a scrolling panorama with a centered logo overlay
- Save slots (
saves/slot1.json→slot5.json) + created/last played metadata - ANSI color rendering and ASCII scene art
- Town hub with Inn, Shop, Hall, Temple, Alchemist, Smithy, and Portal
- Elemental continents + portal travel; town/forest palettes shift by element
- Spellbook with rank selection and MP scaling (cost = base * rank)
- Support spells (Life Boost, Strength) with temporary buffs
- Items + elemental gear variants; element-aligned shops per continent
- Inventory equip/unequip workflow with actions menu
- Multi-opponent encounters (up to 3) with level-budget spawns
- Combat with variance, crits, misses, stuns, defend, and flee
- Followers: recruit via Socialize, follower abilities, end-of-round effects
- Leveling with stat allocation + banked points via Stats menu
- Forest does not auto-spawn a battle on entry; use Seek out monsters
- Scene/venue transitions use a melt-down/build-up animation
Top to bottom:
- Top border (1)
- Location line (centered) (1)
- Separator border (1)
- Body area (variable)
- Art block (if present)
- Divider (if art present)
- Narrative block with status lines
- Actions panel header (1)
- Actions panel content (3, auto-columns)
- Player stats header (1)
- Player stats (2)
- Bottom border (1)
Title Screen:
- D-pad to move selection
A/Enterto confirmS/Escto cancel/backShift/Tabopens Options
Town/Forest/Menus:
- Controls are data-driven from
data/commands.json,data/scenes.json,data/venues.json, anddata/menus.json. - The action panel reflects the active commands and their conditions.
- Target selection (Attack/Socialize/targeted spells) uses ←/→ to cycle,
Ato confirm,Sto cancel. - Forest encounters are started via the Seek out monsters action (no auto-spawn on entry).
Game data is externalized into JSON:
data/opponents.json— opponent stats, art, descriptionsdata/items.json— item effects, prices, descriptionsdata/scenes.json— scene configuration, object composition, commandsdata/npcs.json— NPC names and dialog snippetsdata/npc_parts.json— NPC part definitions (hat/face/torso/legs/shoes)data/venues.json— venue metadata and NPC linksdata/spells.json— spell definitions and costsdata/commands.json— global action commandsdata/menus.json— inventory/spellbook UI text and actionsdata/text.json— message templates for battle textdata/objects.json— object art, color masks, dynamic object defsdata/colors.json— color palette, gradient, random bandsdata/continents.json— continent names, unlock levels, descriptionsdata/elements.json— element palettes and metadatadata/glyphs.json— glyph art (atlas, etc.)data/spells_art.json— reusable spell animation artdata/abilities.json— follower ability definitions
- Resize your terminal to at least 100 columns × 30 rows
- Run:
python3 main.pypython3 color_map.py— print the current color map (including random bands)python3 render.py— render objects/NPCs/opponents/venues/spells from JSON
- macOS terminal
- Linux terminal
- Windows terminal (uses
msvcrt.getch()for single-key input)
- Web UI
- SSH/BBS frontend
This repository represents an exploratory prototype. Expect rapid iteration, breaking changes, and intentional simplicity.