▄▄▄ ▓█████▄ ██▒ █▓▓█████ ███▄ █ ▄▄▄█████▓ █ ██ ██▀███ ▓█████
▒████▄ ▒██▀ ██▌▓██░ █▒▓█ ▀ ██ ▀█ █ ▓ ██▒ ▓▒ ██ ▓██▒▓██ ▒ ██▒▓█ ▀
▒██ ▀█▄ ░██ █▌ ▓██ █▒░▒███ ▓██ ▀█ ██▒▒ ▓██░ ▒░▓██ ▒██░▓██ ░▄█ ▒▒███
░██▄▄▄▄██ ░▓█▄ ▌ ▒██ █░░▒▓█ ▄ ▓██▒ ▐▌██▒░ ▓██▓ ░ ▓▓█ ░██░▒██▀▀█▄ ▒▓█ ▄
▓█ ▓██▒ ░▒████▓ ▒▀█░ ░▒████▒▒██░ ▓██░ ▒██▒ ░ ▒▒█████▓ ░██▓ ▒██▒░▒████▒
▒▒ ▓▒█░ ▒▒▓ ▒ ░ ▐░ ░░ ▒░ ░░ ▒░ ▒ ▒ ▒ ░░ ░▒▓▒ ▒ ▒ ░ ▒▓ ░▒▓░░░ ▒░ ░
Changelog · Development · Architecture · Economy · Dungeons · Contributing
You take four into the dark. What you bring back is yours. What you drop down there stays down there.
Adventure is a browser-based, MUD-style dungeon crawler built on Flask and Socket.IO. Everything happens in real time over WebSockets — movement, combat, chat, loot — with no page reloads. Dungeons are procedurally generated and deterministic per seed: the same seed always produces the same layout, but no two parties spend it the same way.
The hook is extraction. Your run-purse is at risk from the moment you step through the door. Only what you carry out becomes permanent, banked into your Hoard. A party wipe loses the run's haul — and a character who dies and is not raised can be looted, then left behind for good.
flowchart LR
A["🏰 Town<br/>roster · train · trade"] --> B["🗝️ Delve<br/>pick a seed"]
B --> C["🕯️ Explore<br/>rooms · doors · traps"]
C --> D["⚔️ Fight<br/>turn-based, per character"]
D --> C
C --> E{"Push on<br/>or leave?"}
E -->|push| C
E -->|extract| F["💰 Hoard<br/>haul banked, XP kept"]
E -->|wipe| G["💀 Lost<br/>haul gone"]
F --> A
G --> A
The whole design lives in that fork. Going deeper is where the good loot is; every step deeper is more you stand to lose.
|
Roll up to twelve classes — fighter, barbarian, monk, mage, sorcerer, cleric, paladin, druid, ranger, rogue, bard, warlock — and field four at a time. Each class has its own signature skill tree on top of a shared archetype line, so no two classes play the same. Twenty levels, and every one of them unlocks something. Talent points buy skills you choose; you will never have enough for all of them. |
Turn-based and initiative-driven, where every character acts on their own turn — not as one lumpy "party turn". Attack, defend, flee, drink, or spend a skill. Server-authoritative with optimistic-concurrency versioning, so the screen can never disagree with the real fight. |
|
Rooms, corridors, locked doors, secret passages and teleport pads — all generated from a seed and fully reproducible. Movement and searching advance a shared game clock that paces encounters and patrols. The world only moves when you do. |
Procedural gear with prefixes and suffixes — Brutal Longsword of the Bear — where rarity changes what an item is worth, not just how many words it has. Deeper tiers roll better. Gear wears down and can be repaired. |
Note
Two systems ship switched off while their numbers get playtested: multi-enemy packs (combat_pack_max) and monster AI (ai_enabled). The engine handles both; turning either on makes fights markedly harder, so they are deliberately opt-in. Monsters currently attack and nothing else.
Important
PostgreSQL is required. SQLite is explicitly rejected at startup — the app raises unless DATABASE_URL points at Postgres.
📜 Quick start
# 1. Database
createdb adventure
# 2. Environment
export DATABASE_URL="postgresql://username:password@localhost/adventure"
export SECRET_KEY="your-secret-key-here"
# 3. Dependencies
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# 4. Schema
alembic upgrade head
# 5. Seed the world — idempotent, safe to re-run
python run.py reseed-items
python run.py seed-merchants
python run.py seed-skills
# (or all three: ./manage.sh db seed)
# 6. Open the doors
python run.py server # http://localhost:5000🪄 Or let the bootstrap script do it
Handles .env generation, migrations and an admin account for you:
python scripts/setup_adventure.py🧪 Tests, lint, format
pytest -q
ruff check .
black --check .The suite needs an explicit test database — see docs/TESTING.md. Conventions and the admin CLI are in docs/DEVELOPMENT.md.
app/models/ |
Database models |
app/routes/ |
Flask blueprints — auth, dashboard, dungeon, combat, admin |
app/services/ |
Game logic — combat, progression, loot, status effects, the clock |
app/dungeon/ |
Procedural generation pipeline |
app/websockets/ |
Socket.IO event handlers |
app/static/, app/templates/ |
Frontend assets and Jinja templates |
migrations/ |
Alembic schema migrations |
tests/, e2e/ |
pytest suite and Playwright browser smoke |
Open the full documentation index
| Topic | Doc |
|---|---|
| Release history | CHANGELOG.md |
| Local dev, lint/test conventions, admin CLI | docs/DEVELOPMENT.md |
| System architecture | docs/architecture.md |
| Economy, currency, hoard, progression | docs/ECONOMY_PROGRESSION.md |
| Combat system — actions, formulas, balance | docs/COMBAT_SYSTEM.md |
| Combat visual effects | docs/COMBAT_EFFECTS.md |
| Loot — rarity, placement algorithm | docs/LOOT_SYSTEM.md |
| Dungeon generation & invariants | docs/DUNGEON_GENERATION.md |
| Teleports | docs/TELEPORTS.md |
| Monster AI | docs/MONSTER_AI.md |
| Locked doors & lockpicking | docs/LOCKED_DOORS.md |
| Party system | docs/PARTY_SYSTEM.md |
| Skill trees | docs/SKILL_TREE_SYSTEM.md |
| Achievements | docs/ACHIEVEMENT_SYSTEM.md |
| Trading | docs/TRADING_SYSTEM.md |
| Frontend style guide | docs/STYLE_GUIDE.md |
| Art assets & licences | docs/ASSETS.md |
| Testing conventions | docs/TESTING.md |
| Deployment | docs/DEPLOYMENT.md · docs/DOCKER_SETUP.md |
| Release process | docs/RELEASING.md |
Dungeon tile art: Dungeon Gathering — Under The Castle Set by SnowHex — lovely 16×16 work, and worth your money.
Warning
The art is licence-restricted and is not included in this repository. Run scripts/import_tiles.sh against a copy you own to enable it — or just play without it, and the map renders procedurally. See docs/ASSETS.md.
Coding conventions, pre-commit policy, asset guidelines and test instructions are in docs/CONTRIBUTING.md.
Built with Flask, Socket.IO, PostgreSQL — and an unreasonable number of tests.