obsolete.computer

the-maze/AGENTS.md

File Type: text/plain


# Agent notes for the-maze

## Session handoff

- At the **start** or **continuation** of a session, read `.grok/NOTES.md` if it exists. It is the personal, git-ignored progress log (synced between machines).
- When the user asks to wrap up, summarize the day, or update notes: **update** `.grok/NOTES.md` — refresh *Current focus*, *Status*, and *Next steps*, and append a dated entry under *Session log*. Keep it concise and actionable.
- Remember that the current agent session might not have been the last process to write to NOTES.md; it, along with the code and git history, might have been edited elsewhere, whether by a human or an agent running on a different computer & synced here. Its **purpose** is to keep those different agents in-the-loop with a similar session context.
- Do not commit `.grok/`; it is gitignored on purpose.

## Project goals

Hobby revival of a DOS QBasic maze game on Linux via FreeBASIC — not a commercial rewrite.

**Done / in progress**

- `game.bas` on `#lang "fb"` with structured types and a clear main loop.
- Windowed display: fixed 640×400 logical framebuffer (80×25 text cells), OpenGL scale / fullscreen / CLI flags; nearest-neighbor filtering when scaled.
- Logical `tileMap` + `TileId` separate collision/gameplay from drawing (`--ascii` vs `--tiles`; **custom tiles are the default**).
- Runtime `.til` text tilesets; non-blocking QB-style `Play` SFX via SDL2 (`play.bas`).
- Scope refactor: session settings and level defs in `GameConfig`; per-run play state (including `tileMap`) in `GameState`.
- Makefile builds into `out/` and copies assets; `make run` is the supported launch path.

**Near-term direction**

- Refine default / alternate tile art; optional runtime tint for multicolor monsters.
- Keep improving structure only where it helps gameplay or those goals — avoid drive-by rewrites.

**Non-goals**

- Full engine rewrite, new language port, or perfect OOP purity.
- Breaking the room item string format in `.dat` files without an explicit migration plan.
- Treating this like production software with heavy process; prefer small, testable steps and gameplay parity.

Reference originals live in `original-code-and-data/` (read-only history). License is public domain (CC0).

## Layout

| Path | Role |
|------|------|
| `game.bas` | Main game — modern FB dialect; primary development target |
| `play.bas` | QB-style `Play` MML + SDL2 square-wave audio (linked with game) |
| `edit.bas` | Level editor — still QB-ish (`fbc -lang qb`); launched via `Shell "./edit"` |
| `Makefile` | `fbc -w all` for `game.bas` + `play.bas`; `-lang qb` for edit; outputs under `out/` |
| `themaze.dat` | Default world (`DEFAULT_WORLDFILE`) |
| `tiles.til` | Default tileset (`DEFAULT_TILEFILE`) |
| `game.ini`, `edit.ini` | Still copied to `out/`; editor uses `edit.ini`. World/tileset for the game come from **defaults + CLI**, not `game.ini` |
| `original-code-and-data/` | Historical QBasic sources — do not “fix” in place |
| `README.md` | Player-facing build/run/keys docs — update when CLI or UX changes |
| `.grok/NOTES.md` | Personal session handoff (gitignored) |

## Build and run

```bash
make              # game + edit + assets → out/
make run          # build game and run from out/
make run ARGS='--ascii --scale=3'
make run ARGS='--worldfile=themaze.dat --tilefile=tiles.til'
make clean
  • Always run from out/ (or make run) so relative paths resolve.
  • Add new images/sounds/levels to ASSET_FILES in the Makefile so they are copied.
  • Compile warnings are enabled (-w all); don’t introduce new ones casually.
  • Needs FreeBASIC + SDL2 (+ OpenGL headers used for texture filter tweaks).

Architecture (game.bas)

Ownership / scope

Two main buckets after the scope refactor — prefer extending these over new globals:

Bucket Storage Holds
GameConfig Dim Shared config Video mode, scale, paths (worldFile, tileFile), tileDefs(), rooms() (level definitions + per-room flags)
GameState Local game, passed ByRef Player, entities, timers/cycles, checkpoints, tileMap, run flags (isDead, entityHurtPlayer, …)
  • Startup order: InitializeConfigInitTileTableParseCommandLineInitVideo / InitAudioInitializeGameRunGameLoop → shutdown.
  • InitializeConfig sets defaults (GFX_CUSTOM, DEFAULT_WORLDFILE / DEFAULT_TILEFILE, scale, etc.).
  • ParseCommandLine overrides config (including --worldfile, --tilefile, --ascii / --tiles, scale, fullscreen) and sets depth (8 ASCII / 32 custom).
  • Do not reintroduce free-floating Shared for playfield or video options; keep the config vs state split.

Grid, video, tiles

  • Dialect: #lang "fb" with fbgfx.bi, GL/gl.bi, Using FB.
  • Grid: Classic layout — GRID_COLS=80, play rows 1–20, HUD rows 21–23. Coordinates are 1-based text cells (Locate y, x), not pixels.
  • Video: Logical size always LOGICAL_W×LOGICAL_H (640×400). Scaling uses SET_GL_2D_MODE + SET_GL_SCALE before ScreenRes with GFX_OPENGL. Do not enlarge ScreenRes itself (breaks the default font). After a GL scale open, nearest filtering is set on the 2D texture.
  • Playfield map: Lives on game.tileMap. Helpers that read/write cells take the map explicitly, e.g. GetTile(game.tileMap(), y, x), DrawTile game.tileMap(), y, x, id. Call sites pass game.tileMap().
  • Presentation: DrawTile → ASCII (config.tileDefs glyph/color) or custom (config.tileDefs(i).img via Put). HUD/messages use DrawText (does not touch tileMap).
  • Custom art: LoadTileFile parses INI-style .til ([palette] + per-tile 8×16 grids: # primary, + dim, . black, 0-9 palette). Path is config.tileFile. Missing file → solid placeholders. Baked images live in config.tileDefs().img (freed in FreeTileGraphics).
  • Default graphics mode: custom tiles (GFX_CUSTOM). --ascii / -a for classic glyphs.

Timing, input, audio, CLI

  • Timing: Fixed timestep via Sleep SLEEPTIME + cycleCount / ShouldProcess (not busy-wait on Timer). Keep the CPU gentle.
  • Input: Movement uses MultiKey with fbgfx SC_UP / SC_DOWN / SC_LEFT / SC_RIGHT plus InKey$ arrow escapes. Don’t regress double-key or pegged-CPU issues. E launches the editor; Esc quits. (In-game level/tileset pickers were removed; use CLI --worldfile / --tilefile.)
  • CLI: ParseCommandLine mutates config; diagnostics via LogConsole (Open Cons — bare Print hits the graphics surface after ScreenRes).
  • Audio: InitAudio / ShutdownAudio / Play in play.bas (SDL2 queue; non-blocking). New Play clears prior queued audio (monophonic). Octave mapping matches QB (O3 = middle C) with optional PLAY_OCTAVE_SHIFT / SetPlayOctaveShift. If SDL audio fails, Play is a silent no-op.

Code style (prefer matching game.bas)

  • Indent: 4 spaces.
  • Names: PascalCase for types, enums, and routines (GameState, GameConfig, DrawRoom, LoadTileGraphics). Constants often UPPER_SNAKE (MAX_ROOMS, TILE_WALL, COL_YELLOW, DEFAULT_WORLDFILE). Fields/locals camelCase (playerX, cycleCount, entityHurtPlayer, worldFile). Enum members may use a short prefix (Dir.UpDir, TILE_*, GFX_*, COL_*, SC_* from fbgfx).
  • Types: Explicit As Integer / As String / etc. Use ByVal / ByRef on parameters; pass game As GameState by ref into gameplay subs. Array params for the map use tileMap(Any, Any) As Integer.
  • Control flow: Structured Sub/Function (no new GOSUB in game.bas). Prefer Select Case for tile/entity kinds. AndAlso / OrElse where short-circuit matters. += / similar ops are fine where already used.
  • Comments: Explain non-obvious constraints (fbgfx/OpenGL quirks, coordinate systems, .til / room string formats). Skip narrating obvious code.
  • Drawing vs logic: Collision and rules use TileId / GetTile on game.tileMap, not screen reads. Don’t reintroduce Screen()-based playfield queries.
  • Extending tiles: Add to TileId + InitTileTable (writes config.tileDefs) + .til section name in TileIdFromName if needed. Glyph/color table remains the ASCII source of truth.
  • FB keyword pitfalls: Avoid locals named line, val, color, rgb, name (clash with keywords/intrinsics).
  • Formatting: Match surrounding file; light cleanup only in code you touch. No repo-wide reformat drives.

Editor (edit.bas)

  • Still compiled with -lang qb and largely original style (GOSUB, untyped vars, Screen 9).
  • Touch only when game/editor integration or shared formats require it. Do not bulk-port the editor to -lang fb unless explicitly requested.
  • Game launches editor with Shell "./edit" from out/.

Level and tileset data

  • Room items are compact strings in config.rooms().data() (type char + coordinates), same spirit as the original .dat format.
  • Default world: themaze.dat. Preserve .dat compatibility unless the user asks for a format change.
  • Default tileset: tiles.til. Select via --tilefile=... (and --tiles / custom mode, which is already default).
  • Don’t casually overwrite shipped themaze.dat / tiles.til in docs or examples.

Working practices

  • Prefer incremental commits that leave the game buildable (make).
  • No need to suggest git commits; they’ll be done manually after confirming functionality via test runs.
  • After display/input/tile changes, sanity-check with make run (try --ascii when touching text mode; custom is default).
  • Update README.md only when command line options, flags, keys, build steps, or dependencies change.
  • Keep secrets and personal scratch in .grok/; keep durable agent instructions here in AGENTS.md.

```

Meta