LLM-driven RPG game engine
Find a file
2026-07-01 06:00:08 +03:00
cmd/slopgame chore(rename): llm-rpg -> slopgame (excluding docs/) 2026-06-23 03:18:33 +03:00
docs docs: ROMANCE_GUIDE.md edited 2026-07-01 06:00:08 +03:00
internal test(domain): harden ItemKind.UseHint coverage via AST (closes #26) 2026-07-01 04:11:51 +03:00
.gitignore docs: adding title logo 2026-06-23 09:13:14 +03:00
AGENTS.md fix(turn): prefer top-level director narration (closes #24) 2026-06-30 06:20:19 +03:00
go.mod fix(tui,admin): size debug overlay on auto-open, stop /debug polluting HISTORY, render snapshot as glamour Markdown 2026-06-25 10:43:22 +03:00
go.sum fix(tui,admin): size debug overlay on auto-open, stop /debug polluting HISTORY, render snapshot as glamour Markdown 2026-06-25 10:43:22 +03:00
README.md docs: ROMANCE_GUIDE.md added 2026-07-01 04:01:35 +03:00

Slopgame

SlopGame title logo

Local-first LLM-backed TUI RPG foundation in Go.

Slopgame is a single-player RPG that runs entirely on your machine. The engine is written in Go, game state lives in an on-disk SQLite database, and any OpenAI-compatible local LLM endpoint drives scene narration. See docs/LLM_RPG_MVP_S1.md for the design notes that drove this milestone.

How to run

Prerequisites:

  • Go 1.26 or newer
  • An OpenAI-compatible HTTP LLM endpoint (the default points at http://localhost:8080/v1)

Build and launch the TUI:

go run ./cmd/slopgame

If your checkout lacks full Git metadata and Go refuses to stamp the build, disable VCS stamping:

go run -buildvcs=false ./cmd/slopgame

When the TUI opens you walk through the session-creation wizard, which collects a title, LLM base URL / model, setting outline, tone, content rating, worldgen seed, and player profile.

Once a session is bootstrapped:

  • Type free-text input and press Enter to submit a turn.
  • Type / followed by an admin command (see below) to bypass the LLM and apply deterministic state changes.
  • Press F1 at any time to see the keyboard help overlay.
  • Press F2 to reopen the latest debug command result.
  • Press Ctrl+Q to quit (asks for confirmation) or Ctrl+C to force-quit.

While a turn is in flight the input field is locked and the turn assist panel shows every pipeline stage with the current stage highlighted.

How to play

[TBW]

Admin commands

Slopgame exposes a closed set of slash commands that mutate game state deterministically without going through the LLM. Type /help in the TUI for the live list.

Command Description
/help Print the command list.
/debug state Dump the current state snapshot (characters, items, etc.).
/debug context Show the context the LLM would see on the next turn.
/debug last-events [n] Show the last n game events (default 10).
/debug relationships [character-id-or-name] Per-pair view of every relationship: all 9 axes (trust, respect, affection, attraction, comfort, commitment, tension, fear, resentment), label, romance flags, and timestamps. Optional character filter narrows to one source.
/inventory add <item-id> <qty> Add qty of item-id to the player inventory.
/inventory remove <entry-id> <qty> Remove qty from inventory entry entry-id.
/character set <character-id> mood <mood> Set a character's mood.
/character hp <character-id> <delta> Adjust a character's HP by delta (negative for damage).
/party add <character-id> Add a character to the active party.
/party remove <character-id> Remove a character from the active party.
/relationship <character-id> <axis> <delta> Adjust a relationship axis score by delta.
/status add <character-id> <status-id> [dur] Apply a status effect, optionally with a duration.
/status remove <character-id> <status-id> Remove a status effect.
/event generate <description> Force a deterministic game event from a free-text prompt.
/save Force-flush the session to disk.

Identifiers: character-id, item-id, entry-id, and status-id must be the canonical IDs (e.g. char_*, npc_*) currently present in the session. Run /debug state to list them, or read them off the *_id fields from /debug last-events.

Session storage

By default sessions live under ~/.local/share/slopgame/sessions/<session-id>/. Each session directory contains:

  • session.yaml — the canonical YAML config (id, title, model, setting, worldgen seed, player seed).
  • game.db — the SQLite database with characters, factions, locations, items, quests, lore facts, summaries, memory snapshots, game events, and the raw visible log.
  • logs/ — append-only log files:
    • engine.log, worldgen.log, llm.log, admin.log — per-subsystem progress lines.
    • worldgen/<NNN>_<stage>.{prompt.md,response.txt,parsed.json} — per-stage artifacts from the worldgen orchestrator.
    • turns/<turn-id>/<subsystem>.{prompt.md,response.txt,parsed.json} — per-turn trace artifacts from the curator.

Configuration

Slopgame speaks the OpenAI-compatible HTTP API. Point the wizard at any server that exposes /v1/models and /v1/chat/completions (llama.cpp, vLLM, llama-swap in front of llama.cpp, an OpenAI endpoint, etc.).

Environment variables

The wizard reads two optional environment variables so the operator does not have to retype the same endpoints on every session:

Variable Effect
SLOPGAME_BASE_URL Pre-fills the base-URL field (default http://localhost:8080/v1).
SLOPGAME_MODEL Pre-selects the model in the discovery list, or pre-fills it in the manual-entry step when the server does not advertise it.

Both variables are still editable in the wizard; they only set the starting values. Empty or whitespace-only values are ignored. They also take precedence over values saved in session.yaml.

Starting a new session from an existing one

Pick New session at the top-level menu. When at least one session already exists on disk, the wizard stops at a small chooser with two options: Blank session and From existing session.... Pick From existing session... to pre-populate every field from the chosen source session's session.yaml; the worldgen seed is reset to "blank → use current time" so the duplicate gets a fresh world, and the title gets (copy) appended.

When no sessions exist yet, the chooser is skipped and you go straight from New session to a blank wizard. If the source session is removed between picking it and confirming, the TUI surfaces the failure and returns to the menu instead of launching a wizard with missing data.

The engine never blocks on the LLM longer than the configured request timeout. Failed LLM calls surface in the turn assist panel so the operator can retry; previously committed events are preserved.

How to test

The test suite uses fake LLM clients and temporary session directories. Nothing in go test ./... hits the network.

go test ./...

The acceptance smoke test that proves the S1 pipeline runs end to end lives at internal/app/smoke_test.go. It:

  1. Creates a session through SessionCreationService.
  2. Runs all eight worldgen stages with a scripted fake LLM.
  3. Reloads the session through SessionLoadingService.
  4. Submits one player turn through the turn Orchestrator.
  5. Runs the Memory Curator with a fake LLM response so at least one lore fact and one summary are committed.
  6. Reloads the session and confirms the raw log, lore facts, and summaries survive handle close/reopen.

Two supporting tests cover:

  • LLM error surfacing (TestSmoke_S1LLMErrorIsSurfaced).
  • Raw log persistence across reload (TestSmoke_S1ReloadPreservesRawLog).

To run only the smoke tests:

go test -run TestSmoke ./internal/app/

To re-run the full suite without the test cache:

go test -count=1 ./...

If Go complains about VCS stamping, repeat with -buildvcs=false.