README as a small game website in Crookfinger's voice; INSTALL.md, BESTIARY.md, playable standalone file

- README: logo, badges (Rust, WebAssembly, Claude Code, MIT), intro addressed to the master, features with screenshots, the household, how to play, licences and credits
- INSTALL.md: server, PWA, building, tests, savegame service and API, languages, how to refresh generated files
- BESTIARY.md generated from the game's data (tools/make_bestiary.py) with exported sprites and icons (docs/img, test export_doc_images)
- play/DungeonCraft.html: the whole game in one file to download and play offline

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-04 07:46:37 +01:00
1 parent 4ce2c2919e
commit b3583f5e7e
124 files changed
+1178 -104

No files matched your search

+2
View File
@@ -1,3 +1,5 @@
target
.git
server
docs
play
+468
View File
@@ -0,0 +1,468 @@
# Crookfinger's Bestiary
<p align="center"><img src="docs/screenshots/bestiary.png" width="720" alt="The bestiary in the game"></p>
> *Ah, master, you want to know who lives in your dungeon? Sit down. Mind the bones. I have written everything down -
> lovingly about our own, and with a peg on my nose about the others.*
> *- Crookfinger, imp, advisor, keeper of the tea*
Every creature gains experience by fighting, training, studying and sparring. With every level it grows tougher
(+15% health, +12% attack per level), its badge changes (bronze chevrons, silver bars, golden stars) and its gear grows
(shoulder pieces, helmet, cape, golden armour with a crown and a halo). Its attacks look bigger, too.
- [Your creatures](#your-creatures)
- [The humans](#the-humans)
- [The neutrals](#the-neutrals)
- [Rooms of the dungeon](#rooms-of-the-dungeon)
- [Towers, traps and doors](#towers-traps-and-doors)
- [Spells](#spells)
## Your creatures
### Pit Imp
<img src="docs/img/units/worker_1.png" width="72" alt="Pit Imp, level 1"> <img src="docs/img/units/worker_5.png" width="72" alt="Pit Imp, level 5"> <img src="docs/img/units/worker_10.png" width="72" alt="Pit Imp, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 45 | 3 every 1.0 s (3/s) | close combat | 3.2 tiles/s | 0 gold |
**Comes for:** the dungeon itself; the summon spell brings more
**Loves:** Treasury
**From level 3:** Old hand - digs a quarter faster.
My little cousins. They dig, they claim, they carry gold, they repair towers and they never complain - well, they complain, but in a language nobody understands.
Up to five imps help each other with one task. Treat them well: without imps there is no dungeon.
### Kobold
<img src="docs/img/units/kobold_1.png" width="72" alt="Kobold, level 1"> <img src="docs/img/units/kobold_5.png" width="72" alt="Kobold, level 5"> <img src="docs/img/units/kobold_10.png" width="72" alt="Kobold, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 75 | 8 every 0.9 s (9/s) | close combat | 2.6 tiles/s | 40 gold |
**Comes for:** any lair: always the first through the portal
**Loves:** Training Hall
**Cannot stand:** Shadow Mage
**From level 5:** Dash - leaps at enemies a few tiles away with a heavy blow.
Small, scaly, sharp-toothed and endlessly enthusiastic. A kobold will fight anything, including things ten times its size, and lose with a happy face.
Cheap to keep, quick on its feet. I keep a jar of their shed scales on my shelf.
### Cave Spider
<img src="docs/img/units/spider_1.png" width="72" alt="Cave Spider, level 1"> <img src="docs/img/units/spider_5.png" width="72" alt="Cave Spider, level 5"> <img src="docs/img/units/spider_10.png" width="72" alt="Cave Spider, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 95 | 10 every 1.0 s (10/s) | close combat | 2.9 tiles/s | 60 gold |
**Comes for:** a mushroom garden of 6+ tiles
**Loves:** Mushroom Garden
**Cannot stand:** Giant Bat
**From level 4:** Web - its bites often leave enemies stuck and slow.
Eight legs, eight eyes and a heart of pure velvet. Cave spiders love the damp warmth of a mushroom garden and the crunchy sound of armour.
Quick and tough. Do not let the knights see them first: knights scream in a very high voice.
### Shadow Mage
<img src="docs/img/units/mage_1.png" width="72" alt="Shadow Mage, level 1"> <img src="docs/img/units/mage_5.png" width="72" alt="Shadow Mage, level 5"> <img src="docs/img/units/mage_10.png" width="72" alt="Shadow Mage, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 60 | 13 every 1.4 s (9/s) | 4.5 tiles | 2.0 tiles/s | 110 gold |
**Comes for:** a library of 6+ tiles
**Loves:** Library
**Cannot stand:** Kobold, Stone Ogre
**From level 6:** Shadow step - badly hurt in a fight, it vanishes back to its bed.
Shadow mages read everything, forget nothing and hurl dark bolts from a safe distance. They are polite, quiet and slightly terrifying.
Fragile, so keep them behind the ogres. They pay you back in burnt paladins.
### Stone Ogre
<img src="docs/img/units/ogre_1.png" width="72" alt="Stone Ogre, level 1"> <img src="docs/img/units/ogre_5.png" width="72" alt="Stone Ogre, level 5"> <img src="docs/img/units/ogre_10.png" width="72" alt="Stone Ogre, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 230 | 20 every 1.4 s (14/s) | close combat | 1.8 tiles/s | 150 gold |
**Comes for:** a training hall of 9+ tiles
**Loves:** Training Hall
**Cannot stand:** Shadow Mage, Cogling
**From level 4:** Stomp - shakes the ground: enemies around it are hurt and stunned.
A stone ogre is a wall that walks and hugs. Mostly it hugs enemies, and they do not get up again.
Slow, stubborn and wonderfully sturdy. Feed it well, pay it on time and it will stand in front of your core until the end of the world.
### Jailer
<img src="docs/img/units/jailer_1.png" width="72" alt="Jailer, level 1"> <img src="docs/img/units/jailer_5.png" width="72" alt="Jailer, level 5"> <img src="docs/img/units/jailer_10.png" width="72" alt="Jailer, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 260 | 16 every 1.3 s (12/s) | close combat | 1.6 tiles/s | 140 gold |
**Comes for:** a torture chamber of 6+ tiles
**Loves:** Torture Chamber
**Cannot stand:** Flame Priestess
**From level 7:** Shackle - its blows sometimes chain enemies in place.
The jailer pulls enemies close with his chain and has a gift for persuasion: prisoners he looks after change sides much more often.
Big, quiet, hums while he works. Children love him. Well, ours do.
### Flame Priestess
<img src="docs/img/units/priestess_1.png" width="72" alt="Flame Priestess, level 1"> <img src="docs/img/units/priestess_5.png" width="72" alt="Flame Priestess, level 5"> <img src="docs/img/units/priestess_10.png" width="72" alt="Flame Priestess, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 70 | 14 every 1.6 s (9/s) | 5.0 tiles | 2.0 tiles/s | 120 gold |
**Comes for:** a temple of 6+ tiles
**Loves:** Temple
**Cannot stand:** Jailer
**From level 5:** Healing pulse - heals all hurt creatures around her at once.
The flame priestess throws fireballs that splash onto everyone around the target, and she heals the soul core with her prayers.
She sleeps on glowing embers and smells of cinnamon. Do not ask her about the knights who burned her temple. Just let her work.
### Chainbreaker
<img src="docs/img/units/chainbreaker_1.png" width="72" alt="Chainbreaker, level 1"> <img src="docs/img/units/chainbreaker_5.png" width="72" alt="Chainbreaker, level 5"> <img src="docs/img/units/chainbreaker_10.png" width="72" alt="Chainbreaker, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 600 | 36 every 1.1 s (33/s) | close combat | 2.3 tiles/s | 400 gold |
**Comes for:** a big dungeon: 40+ room tiles, a Lv 5 veteran and 4000 gold
**Loves:** Training Hall
**From level 6:** Whirlwind - spins its axes through every enemy around it.
A legend. Twin axes, broken chains, and a temper that has flattened whole fortresses. Only one at a time, and only for a master who is worth it.
Every swing also hits a second enemy. He gets restless when the dungeon is small - give him space and enemies, and he is yours.
### Cogling
<img src="docs/img/units/cogling_1.png" width="72" alt="Cogling, level 1"> <img src="docs/img/units/cogling_5.png" width="72" alt="Cogling, level 5"> <img src="docs/img/units/cogling_10.png" width="72" alt="Cogling, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 90 | 9 every 0.9 s (10/s) | close combat | 2.4 tiles/s | 70 gold |
**Comes for:** a forge of 6+ tiles
**Loves:** Forge
**Cannot stand:** Stone Ogre
**From level 4:** Bomb - throws a home-made bomb that explodes among the enemies.
A cogling is soot, brass goggles and pure enthusiasm. It builds traps two and a half times as fast as anyone else and hums while it hammers.
It cannot fight much, but every corridor it touches becomes a terrible place to walk. I adore them.
### Revenant
<img src="docs/img/units/revenant_1.png" width="72" alt="Revenant, level 1"> <img src="docs/img/units/revenant_5.png" width="72" alt="Revenant, level 5"> <img src="docs/img/units/revenant_10.png" width="72" alt="Revenant, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 160 | 14 every 1.2 s (12/s) | close combat | 1.9 tiles/s | 30 gold |
**Comes for:** rises from the graveyard
**Loves:** Graveyard
**Cannot stand:** Flame Priestess
**From level 5:** Life drain - heals itself by a third of the damage it deals.
A revenant does not eat, does not sleep and does not complain - the perfect employee, if you can stand the smell.
It drinks the strength of those it strikes once it has some experience. The priestess and it do not get along, for obvious reasons.
### Giant Bat
<img src="docs/img/units/bat_1.png" width="72" alt="Giant Bat, level 1"> <img src="docs/img/units/bat_5.png" width="72" alt="Giant Bat, level 5"> <img src="docs/img/units/bat_10.png" width="72" alt="Giant Bat, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 55 | 6 every 0.7 s (9/s) | close combat | 3.6 tiles/s | 30 gold |
**Comes for:** an aerie of 4+ tiles
**Loves:** Aerie
**Cannot stand:** Cave Spider
**From level 5:** Shriek - a piercing cry that stuns enemies nearby.
Giant bats! Fast, cheeky, and they fly over water and lava. They patrol the edges of your realm and dive at lonely enemies.
Weak alone, a plague in swarms. They flee when badly hurt and come back once they have rested.
### Chain Gargoyle
<img src="docs/img/units/gargoyle_1.png" width="72" alt="Chain Gargoyle, level 1"> <img src="docs/img/units/gargoyle_5.png" width="72" alt="Chain Gargoyle, level 5"> <img src="docs/img/units/gargoyle_10.png" width="72" alt="Chain Gargoyle, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 260 | 15 every 1.2 s (12/s) | close combat | 2.2 tiles/s | 120 gold |
**Comes for:** an aerie of 6+ tiles and a torture chamber of 4+
**Loves:** Torture Chamber
**Cannot stand:** Arcane Owl
**From level 5:** Stone form - badly hurt, it turns to stone and takes half damage for a while.
A chain gargoyle is carved stone with bad manners and a soft spot for dungeons with a proper torture chamber.
Tough as a castle wall, flies over rivers and does not mind arrows at all. It sits very still in the aerie - that is how it relaxes.
### Arcane Owl
<img src="docs/img/units/owl_1.png" width="72" alt="Arcane Owl, level 1"> <img src="docs/img/units/owl_5.png" width="72" alt="Arcane Owl, level 5"> <img src="docs/img/units/owl_10.png" width="72" alt="Arcane Owl, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 70 | 12 every 1.3 s (9/s) | 4.5 tiles | 2.8 tiles/s | 100 gold |
**Comes for:** an aerie of 6+ tiles and a library of 6+
**Loves:** Library
**Cannot stand:** Chain Gargoyle
**From level 5:** Volley - every bolt is followed by a second one at another enemy.
The arcane owl has read every book in your library twice and now shoots glowing bolts at heroes from the air.
Clever, fragile and terribly judgemental. It hoots when you build crooked rooms.
### Ember Wyrm
<img src="docs/img/units/wyrm_1.png" width="72" alt="Ember Wyrm, level 1"> <img src="docs/img/units/wyrm_5.png" width="72" alt="Ember Wyrm, level 5"> <img src="docs/img/units/wyrm_10.png" width="72" alt="Ember Wyrm, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Wage |
|---|---|---|---|---|
| 650 | 30 every 1.4 s (21/s) | 3.0 tiles | 2.5 tiles/s | 380 gold |
**Comes for:** an aerie of 16+ tiles and a treasury of 16+ (only one)
**Loves:** Treasury
**Cannot stand:** Giant Bat
**From level 7:** Inferno - its fire breath spreads much wider.
An ember wyrm. In your dungeon. Breathing fire on knights. Master, I may cry.
Very rare, very strong, burns everything around its target. It wants a huge aerie and a rich treasury, and it will not share the sky with another wyrm.
## The humans
> *Hold your nose, master. Here come the heroes.*
### Knight
<img src="docs/img/units/knight_1.png" width="72" alt="Knight, level 1"> <img src="docs/img/units/knight_5.png" width="72" alt="Knight, level 5"> <img src="docs/img/units/knight_10.png" width="72" alt="Knight, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 140 | 11 every 1.1 s (10/s) | close combat | 1.9 tiles/s | 120 gold |
The backbone of the Order: polished, punctual and utterly devoid of imagination. Knights march in circles, sing about the sun and smell of boot wax.
They hit hard and hold the line. Disgusting. Capture them if you can - some of them can be persuaded in the torture chamber.
### Archer
<img src="docs/img/units/archer_1.png" width="72" alt="Archer, level 1"> <img src="docs/img/units/archer_5.png" width="72" alt="Archer, level 5"> <img src="docs/img/units/archer_10.png" width="72" alt="Archer, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 75 | 9 every 1.3 s (7/s) | 4.5 tiles | 2.1 tiles/s | 100 gold |
Archers stand far away, shoot from behind walls and call it bravery. They wear little green hats. Little green hats, master.
Weak up close. Send the bats, they hate it.
### Priest
<img src="docs/img/units/priest_1.png" width="72" alt="Priest, level 1"> <img src="docs/img/units/priest_5.png" width="72" alt="Priest, level 5"> <img src="docs/img/units/priest_10.png" width="72" alt="Priest, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 80 | 5 every 1.5 s (3/s) | 3.5 tiles | 1.9 tiles/s | 120 gold |
Priests heal other heroes from afar and pray loudly enough to curdle milk. Every one of them has a sermon about us, and none of them is short.
Kill them first, or the fight never ends.
### Dwarf Miner
<img src="docs/img/units/dwarf_1.png" width="72" alt="Dwarf Miner, level 1"> <img src="docs/img/units/dwarf_5.png" width="72" alt="Dwarf Miner, level 5"> <img src="docs/img/units/dwarf_10.png" width="72" alt="Dwarf Miner, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 110 | 9 every 1.0 s (9/s) | close combat | 2.0 tiles/s | 100 gold |
The dwarf miners of the Order dig their way through earth three times as fast as anyone else, straight towards your treasury. They have no respect for walls - your walls in particular.
Reinforce your walls. Steel walls make them weep.
### Fairy Spy
<img src="docs/img/units/fairy_1.png" width="72" alt="Fairy Spy, level 1"> <img src="docs/img/units/fairy_5.png" width="72" alt="Fairy Spy, level 5"> <img src="docs/img/units/fairy_10.png" width="72" alt="Fairy Spy, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 70 | 5 every 1.2 s (4/s) | 3.5 tiles | 3.2 tiles/s | 150 gold |
The Order's little informer: a glowing gnat with wings, sent ahead to find your traps and tell everybody. Once it has seen a trap, the heroes smash it.
It also sprinkles healing dust on the heroes. Swat it first. Twice, to be sure.
### Battle Mage
<img src="docs/img/units/battlemage_1.png" width="72" alt="Battle Mage, level 1"> <img src="docs/img/units/battlemage_5.png" width="72" alt="Battle Mage, level 5"> <img src="docs/img/units/battlemage_10.png" width="72" alt="Battle Mage, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 120 | 18 every 1.8 s (10/s) | 5.0 tiles | 1.8 tiles/s | 250 gold |
A mage in the Order's robes who thinks walls are a personal insult. Reinforced walls burst under his fireballs, and he never stops talking about it.
Fragile up close. Steel walls annoy him most - good.
### Giant
<img src="docs/img/units/giant_1.png" width="72" alt="Giant, level 1"> <img src="docs/img/units/giant_5.png" width="72" alt="Giant, level 5"> <img src="docs/img/units/giant_10.png" width="72" alt="Giant, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 900 | 34 every 1.6 s (21/s) | close combat | 1.3 tiles/s | 600 gold |
A giant is what happens when a knight eats his vegetables for three hundred years. Slow, enormous, and it treats doors as a suggestion.
It wrecks traps simply by standing next to them. Revolting manners. Hit it with everything, preferably from afar.
### Paladin of Light
<img src="docs/img/units/paladin_1.png" width="72" alt="Paladin of Light, level 1"> <img src="docs/img/units/paladin_5.png" width="72" alt="Paladin of Light, level 5"> <img src="docs/img/units/paladin_10.png" width="72" alt="Paladin of Light, level 10">
*Level 1, 5 and 10 - the gear grows with the rank.*
| Health | Attack | Range | Speed | Bounty |
|---|---|---|---|---|
| 700 | 26 every 1.2 s (22/s) | close combat | 1.7 tiles/s | 1000 gold |
Paladins of light: glowing, enormous and absolutely convinced of themselves. They are the Order's heroes and have the bounty to prove it.
Tough as an ogre, twice as smug. Bring everything you have, and maybe a bucket for the glare.
## The neutrals
### Cave dwarf
<img src="docs/img/units/dwarf_5.png" width="72" alt="cave dwarf">
Not all dwarves serve the Order. The cave dwarves keep to themselves in caves full of gold and gems and want nothing but to be left alone.
They fight whoever comes close and never leave their cave. The more your army grows, the tougher they get - they have heard of you. Some of their chests hold the plans of the rune forge tower.
## Rooms of the dungeon
| | Room | |
|---|---|---|
| <img src="docs/img/icons/treasury.png" width="40"> | **Treasury** | Stores gold: +500 capacity per tile. *Where the gold sleeps. The richer the treasury, the happier everyone is. Imps bring their loads to the nearest treasury, and payday is paid from here. | Each tile holds 500 gold; the core keeps a reserve when it is full.* |
| <img src="docs/img/icons/lair.png" width="40"> | **Lair** | One bed per tile. Determines how many creatures come. *Every creature wants its own bed, and only arrives when one is free. A big lair also brings new creatures faster.* |
| <img src="docs/img/icons/farm.png" width="40"> | **Mushroom Garden** | Food for creatures. With 6 tiles or more, cave spiders come. *Mushrooms! Juicy, pale, slightly glowing. Hungry creatures are grumpy creatures, and spiders love a big garden.* |
| <img src="docs/img/icons/training.png" width="40"> | **Training Hall** | Creatures gain levels (costs gold). With 9 tiles or more, stone ogres come. *Creatures train here and gain levels. Training is billed on payday. Ogres come for a big hall.* |
| <img src="docs/img/icons/library.png" width="40"> | **Library** | Creatures generate mana. With 6 tiles or more, shadow mages come. *Research, quiet reading and the occasional explosion. Mages and owls come for a library.* |
| <img src="docs/img/icons/torture.png" width="40"> | **Torture Chamber** | Holds one prisoner per 2 tiles. Defeated heroes are carried here and tortured until they die or defect. From 6 tiles on, Jailers come. *Captured heroes end up here. With time and a jailer, some of them change sides. Gargoyles feel at home with one.* |
| <img src="docs/img/icons/prison.png" width="40"> | **Prison** | Captured heroes sit in cells here - apart from the torture chamber. Without torture they do not change sides, and hunger wears them down. Pick a prisoner up with the hand to move it to the torture chamber. *Rows of cells. Heroes rot here quietly until somebody decides what to do with them.* |
| <img src="docs/img/icons/temple.png" width="40"> | **Temple** | Creatures pray here: more mana than studying, and it lifts their mood. From 6 tiles on, Flame Priestesses come. *Prayers heal the soul core and keep spirits high. The flame priestess comes for a temple.* |
| <img src="docs/img/icons/aerie.png" width="40"> | **Aerie** | Perches and nests for flying creatures. 4+ tiles attract giant bats; with a torture chamber chain gargoyles, with a library arcane owls; a big aerie (16+) and treasury (16+) may lure an ember wyrm. Flyers cross water and lava. *Perches and nests for flying creatures. Bats, gargoyles, owls and even the ember wyrm need room here.* |
| <img src="docs/img/icons/forge.png" width="40"> | **Forge** | Creatures manufacture traps and doors here - work, no gold. Finished pieces wait in crates until an imp installs them. Coglings come for a forge of 6+ tiles. *The clang of hammers, the smell of hot iron. Every trap and door is made here first - the more creatures work, the faster.* |
| <img src="docs/img/icons/graveyard.png" width="40"> | **Graveyard** | The fallen - heroes and creatures - are buried here. After a while some of them rise as revenants and serve you. *Moss, stones and fresh earth. Nobody stays dead for long down here.* |
| <img src="docs/img/icons/guardpost.png" width="40"> | **Guard Post** | Creatures hold the position here for a while and watch far: they attack every enemy that comes into view. *A brazier, a banner and a bored creature with sharp eyes.* |
| <img src="docs/img/icons/arena.png" width="40"> | **Arena** | Creatures spar in pairs: twice as much experience as training and free of charge, but they get bruised. *Sand, sweat and the occasional tooth. Experience comes fast here.* |
| <img src="docs/img/icons/altar.png" width="40"> | **Sacrifice Altar** | Drop a creature on the altar to sacrifice it. Certain pairs of sacrifices bring a rare creature, otherwise the dungeon is blessed: better mood and mana. *Black marble that drinks. Some say it listens to pairs. Ogre and spider, mage and bat, jailer and priestess, kobold and cogling...* |
## Towers, traps and doors
| | | |
|---|---|---|
| <img src="docs/img/icons/arrow.png" width="40"> | **Arrow Tower** | Cheap and fast. Arrows pierce up to two more heroes behind the target. Click your tower with this tool to upgrade it. *The arrow pierces up to two more enemies behind the target. Cheap and reliable.* |
| <img src="docs/img/icons/cannon.png" width="40"> | **Cannon Tower** | Slow, long range, splash damage that knocks heroes back. Cannot hit targets right next to it. *Splash damage and knockback - push the knights into the lava.* |
| <img src="docs/img/icons/magic.png" width="40"> | **Magic Tower** | Uses mana per shot. Never misses, slows the target and jumps to a second hero. *Never misses, slows the target and jumps on to a second enemy. Uses mana.* |
| <img src="docs/img/icons/rune.png" width="40"> | **Rune Forge Tower** | Plans from a dwarven chest: built for free, one tower per chest. Huge range, rune bolts jump on to three more enemies. Sell it and the base tower comes back once, in the next mission. *The masterpiece of the cave dwarves: rune bolts that jump on to three more enemies, enormous range and walls of granite. One free build per chest; sell it and it comes back once.* |
| <img src="docs/img/icons/spikes.png" width="40"> | **Spike Pit** | Spikes shoot up when an enemy steps on it. Strikes again and again. *Simple, honest, reusable. Knights hate it when the floor bites back.* |
| <img src="docs/img/icons/poison.png" width="40"> | **Poison Vent** | Releases a cloud of poison that hurts every enemy inside for a while. *A puff of green and a lot of coughing. Best in narrow corridors.* |
| <img src="docs/img/icons/fire.png" width="40"> | **Fire Jet** | A demon head spits a jet of fire at enemies up to three tiles away. *A demon head with a temper. Put it where the heroes queue.* |
| <img src="docs/img/icons/roller.png" width="40"> | **Stone Roller** | Sends a boulder rolling down the corridor: hits hard and knocks enemies aside. *A boulder and a long corridor - physics does the rest.* |
| <img src="docs/img/icons/alarm.png" width="40"> | **Alarm Bell** | Rings when enemies come near: your creatures nearby rush to fight them. *Not dangerous itself, but it calls everybody who is.* |
| <img src="docs/img/icons/mine.png" width="40"> | **Rune Mine** | Explodes once with great force when an enemy steps close. *One step too many and the corridor gets redecorated.* |
| <img src="docs/img/icons/frost.png" width="40"> | **Frost Glow** | An ice crystal: enemies around it are slowed and chilled, always. *Cold makes heroes slow, and slow heroes make easy targets.* |
| <img src="docs/img/icons/door_wood.png" width="40"> | **Wooden Door** | Your creatures walk through, enemies have to smash it. Click a door again to lock it. *Keeps the draught and the knights out - for a while.* |
| <img src="docs/img/icons/door_iron.png" width="40"> | **Iron Door** | A heavy door that takes a long time to break. Click a door again to lock it. *Heavy, riveted, stubborn. Bring a battering ram, heroes.* |
| <img src="docs/img/icons/door_magic.png" width="40"> | **Magic Door** | The strongest door: it hurls back whoever beats on it. Click a door again to lock it. *Strike it and it strikes back. Locked, it is the end of every corridor.* |
## Spells
| | Spell | Mana | |
|---|---|---|---|
| <img src="docs/img/icons/lightning.png" width="40"> | **Lightning** | 50 | 55 damage to heroes in the area. |
| <img src="docs/img/icons/heal.png" width="40"> | **Heal** | 60 | Heals your units in the area by 50%. |
| <img src="docs/img/icons/summon.png" width="40"> | **Summon Pit Imp** | 80+ | Creates a new Pit Imp on your own territory. |
| <img src="docs/img/icons/eye.png" width="40"> | **Seer's Eye** | 40 | Opens a magic eye: a wide area around the spot is revealed for 20 seconds. |
| <img src="docs/img/icons/cavein.png" width="40"> | **Cave-in** | 70 | The ceiling comes down: open floor turns back into earth (3x3), whoever stands there is hurt and pushed out. Blocks corridors. |
| <img src="docs/img/icons/haste.png" width="40"> | **Haste** | 30 | Your creatures around the spot move and act much faster for 30 seconds. |
| <img src="docs/img/icons/stoneskin.png" width="40"> | **Stoneskin** | 40 | Your creatures around the spot take only half damage for 30 seconds. |
| <img src="docs/img/icons/goldrain.png" width="40"> | **Gold Rain** | 100 | Turns mana into 250 gold for your treasury. |
| <img src="docs/img/icons/invisible.png" width="40"> | **Invisibility** | 50 | One creature becomes invisible for 25 seconds: enemies and towers ignore it until it attacks. |
| <img src="docs/img/icons/plague.png" width="40"> | **Plague** | 80 | A sickness that eats at the enemies around the spot and jumps from one to the next. |
| <img src="docs/img/icons/quake.png" width="40"> | **Quake** | 90 | The ground shakes: enemies around the spot are hurt and stunned for two seconds. Your creatures keep their footing. |
---
*This page is written from the game's own data: `python3 tools/make_bestiary.py`.*
+131
View File
@@ -0,0 +1,131 @@
# Installing and running DungeonCraft
> *Technical matters, master. Crookfinger does not understand half of it, but the imps say it works.*
**Just want to play?** Download [`play/DungeonCraft.html`](play/DungeonCraft.html) and open it in a browser -
no server, no installation, no login. Savegames are downloaded as `.dcsave` files and loaded again with
*Load Game* (or dropped onto the game window).
Everything below is for running your own server (with user accounts and savegames on the server),
building the game yourself or working on it.
## Running a server
The easiest way is Docker; the image builds the WASM file and serves it with nginx. A second container runs the savegame service (see below):
```sh
docker compose up -d --build
# open http://localhost:18093
```
### Install as an app (PWA)
DungeonCraft is a progressive web app: browsers offer to install it (Chrome/Edge: install icon in the address bar or "Install app" in the menu; Android: "Add to home screen"; iOS Safari: Share → "Add to Home Screen"). The installed game starts in full screen and also works offline (the savegame list on the server then falls back to savegame files).
Browsers only allow the installation over **HTTPS** (or on `localhost`), so a public installation needs a reverse proxy with a certificate in front of port 18093.
### Building without Docker
Requirements: Rust with the `wasm32-unknown-unknown` target.
```sh
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
cp target/wasm32-unknown-unknown/release/dungeoncraft.wasm www/
cp ~/.cargo/registry/src/*/macroquad-*/js/mq_js_bundle.js www/
python3 -m http.server 8080 -d www # open http://localhost:8080
```
### Testing the game logic
The tests run natively: a headless simulation of whole games, a savegame round trip, rendering of all music modules and the audio engine:
```sh
cargo test --release
```
## Music
The soundtrack consists of FastTracker 2 modules in `assets/music/`, generated by `tools/compose_music.py` (`python3 tools/compose_music.py`). They can be opened and edited in MilkyTracker. The game plays them with its own XM/MOD replayer (`src/xm.rs`); sound effects are synthesised chip sounds (`src/sfx.rs`). Both are mixed in `src/audio.rs` and streamed to the browser by `www/dc_plugin.js`.
## Controls
| Input | Action |
|---|---|
| Esc | Pause menu (save, load, settings) |
| Left click / drag | Use the active tool (dig and rooms: drag a rectangle) |
| Right click | Drop a unit from your hand (prisoners anywhere on your ground; onto a free torture place = locked up) |
| Shift + left click (hand) | Backhand own units (15 s faster, costs a little HP and mood) / punch heroes on your ground (15 damage, 1 s stun) |
| Right drag, WASD, arrows, screen edge | Move the camera |
| Middle drag, Q/E, R/F | Rotate / tilt the camera |
| Mouse wheel | Zoom |
| 1–9, 0, X, V, K, J, B, C, T, U, Y, O, N | Select a tool or spell (more can be bound in the settings) |
| P / Space | Pause |
| M, +/- | Sound on/off, volume |
| G | Tile outlines on/off |
| H | Help |
All keys except Esc and the arrow keys can be changed in Settings → Keys.
The toolbar groups the tools into categories (Dig, Hand, Rooms, Towers, Traps, Spells, Orders) plus a menu button; categories with several tools open a row above the toolbar. The keys select tools directly.
### Touch (phones and tablets)
The touch interface switches on automatically on touch devices or after the first touch (Settings → Game → Touch interface: Auto / On / Off). It uses a larger interface and these gestures:
| Gesture | Action |
|---|---|
| Tap | Use the tool (hand: pick up a unit; spells, flag, towers: at that spot) |
| Drag | Dig, rooms and sell: mark a rectangle; other tools: move the map |
| Two fingers | Move the map, pinch to zoom, twist to rotate the camera |
| Long press | Information about the spot; hand: drop the units there |
| Double tap (hand) | Backhand own units / punch heroes on your ground |
The savegame name is entered with the browser's text dialog when the name field is tapped.
## Savegames
When the game is served together with the service (`server/`, started by `docker compose`), it starts with a **login**:
- The very first login creates the **admin account** with the entered name and password - do this right after installing.
- The admin creates further users (title screen -> *Account*); every user can change the password there and log out.
- Every user only sees the **own savegames** on the server: *Save Game* and *Load Game* open the list (save under a new name, overwrite, load, delete).
- *Download Standalone* (title screen) downloads `DungeonCraft.html`: the whole game in one file, without login and without server. Savegame files (*Download as file*) open there as well.
If the service cannot be reached (for example the standalone file, `www/` opened as local files or a plain web server), there is no login and the game uses savegame files: *Save Game* downloads a `.dcsave` file (JSON), *Load Game* opens the file picker; dropping the file onto the game window works too.
The game also autosaves into the browser every two minutes and when the tab is closed; *Continue* on the title screen resumes from there.
### Savegame service
A small Rust service (axum) in `server/` keeps the user accounts (Argon2 password hashes) and the savegames as files in the Docker volume `savegames` (one folder per user). nginx forwards `/api/` to it; the service itself is not published. The standalone file is served under `/standalone/` only for logged-in users (nginx `auth_request`).
| Request | Meaning |
|---|---|
| `GET /api/health` | `ok` when the service runs (`ok setup` while there is no account yet) |
| `POST /api/login` | `{name, password}` -> `{token, name, admin}`; the first login creates the admin |
| `POST /api/logout`, `GET /api/me`, `POST /api/password` | session, own account, change password |
| `GET/POST /api/users`, `DELETE /api/users/{name}` | admin: list, create, delete users |
| `GET /api/saves` | own savegames (id, name, date, map size, wave, play time), newest first |
| `POST /api/saves?name=...` | store a new savegame (body: savegame JSON) |
| `PUT /api/saves/{id}?name=...` | overwrite a savegame |
| `GET /api/saves/{id}` | savegame JSON |
| `DELETE /api/saves/{id}` | delete a savegame |
A forgotten password is set again on the server with `docker compose stop saves && docker compose run --rm --no-deps saves dungeoncraft-saves set-password <name> '<password>' && docker compose up -d saves` (ends the user's sessions). To start over with no accounts at all, delete `users.json` and `sessions.json` in the `savegames` volume.
All requests except health and login need `Authorization: Bearer <token>`. Limits: 1 MB per savegame, 500 MB in total, 30 write requests and 20 login attempts per minute. Settings: `SAVE_DIR` (default `/data`), `LISTEN` (default `0.0.0.0:8080`).
## Adding a language
1. Copy `lang/en.lang` to `lang/<code>.lang` and translate the texts (placeholders `{0}`, `{1}` stay as they are).
2. Add the file to `SOURCES` in `src/i18n.rs`.
3. Note: the DINOBYTE font only covers the CP437 character set.
## Refreshing the generated files
| File | How |
|---|---|
| `play/DungeonCraft.html` | build the WASM file, then `python3 tools/make_standalone.py <www dir with dungeoncraft.wasm and mq_js_bundle.js> play/DungeonCraft.html` (the Docker image builds the same file under `/standalone/`) |
| `docs/img/units`, `docs/img/icons` | `cargo test --release export_doc_images -- --ignored` |
| `BESTIARY.md` | `python3 tools/make_bestiary.py` |
+138 -104
View File
@@ -1,135 +1,169 @@
# DungeonCraft
<p align="center">
<img src="docs/img/logo.png" width="128" alt="DungeonCraft - the soul core">
</p>
> **Made with the help of [Claude Code](https://claude.com/claude-code).** The code, graphics, texts and
> documentation of this game were written together with Claude Code, Anthropic's AI coding assistant.
<h1 align="center">DungeonCraft</h1>
An original dungeon building & defence game that runs in the browser (Rust + [macroquad](https://github.com/not-fl3/macroquad), compiled to WebAssembly).
<p align="center"><b>Dig. Build. Defend. Conquer.</b><br>
An evil little dungeon game for the browser - in glorious pixels, in 3D, for free.</p>
Dig out your underground realm, claim the portal to attract creatures, keep them fed, rested and paid, fend off endless hero waves that adapt to your army – and finally destroy the Hero Gate.
<p align="center">
<img src="https://img.shields.io/badge/Rust-macroquad-b7410e?logo=rust&logoColor=white" alt="Rust">
<img src="https://img.shields.io/badge/WebAssembly-runs%20in%20the%20browser-654ff0?logo=webassembly&logoColor=white" alt="WebAssembly">
<img src="https://img.shields.io/badge/made%20with-Claude%20Code-d97757?logo=anthropic&logoColor=white" alt="Made with Claude Code">
<img src="https://img.shields.io/badge/license-MIT-2ea44f" alt="MIT License">
<img src="https://img.shields.io/badge/languages-EN%20%7C%20DE-555" alt="English and German">
</p>
- 3D isometric view with a freely rotatable and tiltable camera
- Procedural pixel art, irregular cave walls, flickering torch light
- CP437 text-mode style interface with the DINOBYTE bitmap font
- Map sizes from 64×64 to 256×256 tiles (one portal per 64×64 tiles, dwarf caves with treasure and rune forge plans)
- English and German (language files in `lang/`, easy to extend)
<p align="center">
<a href="play/DungeonCraft.html"><b>▶ Play now - download one HTML file, open it, done</b></a><br>
<a href="BESTIARY.md">Bestiary</a> · <a href="INSTALL.md">Run your own server</a> · <a href="DESIGN.md">Design notes</a>
</p>
See [DESIGN.md](DESIGN.md) for all design decisions and [CREDITS.md](CREDITS.md) for third-party assets.
<p align="center">
<img src="docs/screenshots/title-battle.png" width="820" alt="The title screen with the endless bridge battle">
</p>
## Running
---
The easiest way is Docker; the image builds the WASM file and serves it with nginx. A second container runs the savegame service (see below):
## Wake up, master.
```sh
docker compose up -d --build
# open http://localhost:18093
```
Yes, you. Don't look around, there's nobody else down here - just you, four imps and me, old **Crookfinger**.
### Install as an app (PWA)
A hundred years ago the Order of the Lightwatch smashed your soul core into shards and carried them off like trophies.
They polished their boots, sang songs about the sun and forgot about you. **Big mistake.**
DungeonCraft is a progressive web app: browsers offer to install it (Chrome/Edge: install icon in the address bar or "Install app" in the menu; Android: "Add to home screen"; iOS Safari: Share → "Add to Home Screen"). The installed game starts in full screen and also works offline (the savegame list on the server then falls back to savegame files).
Your heart still beats. Hear that? *Lub-dub. Lub-dub.* That's the sound of a grudge.
Browsers only allow the installation over **HTTPS** (or on `localhost`), so a public installation needs a reverse proxy with a certificate in front of port 18093.
So pick up the pickaxe - well, point at the rock, the imps carry the pickaxes - and let's dig ourselves a realm.
Gold is in the walls, creatures are waiting behind the portal, and the knights... the knights are about to have
a very bad century.
### Building without Docker
<p align="center">
<img src="docs/screenshots/dungeon-heart.png" width="820" alt="The beating crystal heart, the demon gate and the dark towers">
<br><i>Your heart, master, held in chains on its obsidian throne. And the demon gate. I polished it myself.</i>
</p>
Requirements: Rust with the `wasm32-unknown-unknown` target.
## What awaits you down here
```sh
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
cp target/wasm32-unknown-unknown/release/dungeoncraft.wasm www/
cp ~/.cargo/registry/src/*/macroquad-*/js/mq_js_bundle.js www/
python3 -m http.server 8080 -d www # open http://localhost:8080
```
**🕳 Dig your realm.** Mark the rock and your imps dig, claim the ground, carry gold, gems and diamonds to the
treasury and hang torches on the walls. Up to five imps help each other with one job. Every tile you claim is *yours*.
### Testing the game logic
**🏰 Build rooms that bring the right guests.** A lair and a mushroom garden open the portal. A library brings
shadow mages, a training hall stone ogres, a temple the flame priestess, an aerie giant bats, gargoyles, arcane
owls - and maybe, one day, an **ember wyrm**. Fourteen rooms in total, from the forge to the sacrifice altar.
The tests run natively: a headless simulation of whole games, a savegame round trip, rendering of all music modules and the audio engine:
**👹 Keep them happy, or they leave.** Every creature wants a bed, food and its wage on payday - and it walks to the
treasury to fetch it. Each has a favourite room, and some of them simply cannot stand each other.
Put a mage next to an ogre and watch the fur fly. (Nobody dies. Probably.)
```sh
cargo test --release
```
**⚔ Train them into legends.** Ten ranks from Recruit to Warlord, with chevrons, bars and stars - and gear that grows
with them, up to golden armour, a crown and a halo. With the level come abilities: the ogre learns to *stomp*, the mage
to *step through shadows*, the chainbreaker to spin like a *whirlwind*.
## Music
<p align="center">
<img src="docs/screenshots/bridge-battle.png" width="820" alt="A battle at a bridge over an underground river">
<br><i>The knights brought a bridge. How thoughtful. Now they're bringing it back, one piece at a time.</i>
</p>
The soundtrack consists of FastTracker 2 modules in `assets/music/`, generated by `tools/compose_music.py` (`python3 tools/compose_music.py`). They can be opened and edited in MilkyTracker. The game plays them with its own XM/MOD replayer (`src/xm.rs`); sound effects are synthesised chip sounds (`src/sfx.rs`). Both are mixed in `src/audio.rs` and streamed to the browser by `www/dc_plugin.js`.
**🔥 Defend with towers, traps and doors.** Arrow spires, hellmouth cannons and dark spires on the walls. In the
**forge** your creatures build spike pits, poison vents, fire jets, stone rollers, alarm bells, rune mines and
frost glows - and doors of wood, iron and magic. Lock them, and nobody gets through. Not even the giant. Well. Not for a while.
## Controls
**✨ Cast nasty spells.** Lightning, healing, seer's eye, cave-in, haste, stoneskin, gold rain, invisibility,
plague and quake. Your scholars **research** them in the library - one discovery after another.
| Input | Action |
**🌫 Explore the dark.** Everything beyond your walls is black until your creatures go there.
Somewhere out there: a cave of neutral **dwarves** guarding gold and gems - and sometimes a chest with the plans
of a dwarven **rune forge tower**. Islands in lava rivers. Mana springs. Creatures in chains, waiting to be freed.
<p align="center">
<img src="docs/screenshots/forge-and-traps.png" width="400" alt="The forge with coglings and every kind of trap and door">
<img src="docs/screenshots/dark-rooms.png" width="400" alt="Prison, graveyard, arena, altar and guard posts">
<br><i>Left: the forge and its little coglings. Right: prison, graveyard, arena, guard posts and the altar. Cosy.</i>
</p>
**👑 Other masters want your realm, too.** Mossgrim the Mouldy (green, smells like a wet cellar) and Lady Nightwell
(blue, writes threatening letters on scented paper) dig their own dungeons in free play and in the campaign.
And the humans attack *everybody*. Let them bite each other first.
**📜 A campaign of thirty missions.** Take back the shards of your soul core - from the first little outpost by the
border brook to the citadel of Grand Master Valerian Brightshield. Rivers, fortresses, treasure islands, sealed
caves, rival masters, and me, telling you the story. With tea.
<p align="center">
<img src="docs/screenshots/dwarf-cave.png" width="400" alt="A dwarf cave with a rune chest">
<img src="docs/screenshots/fog-of-war.png" width="400" alt="The start of a game in the fog of war">
<br><i>A dwarf cave, master. Nice dwarves. Don't go too close. - And this is where every game begins: in the dark.</i>
</p>
## Meet the household
<p align="center">
<img src="docs/img/units/worker_5.png" width="56" alt="Pit imp">
<img src="docs/img/units/kobold_5.png" width="56" alt="Kobold">
<img src="docs/img/units/spider_5.png" width="56" alt="Cave spider">
<img src="docs/img/units/mage_5.png" width="56" alt="Shadow mage">
<img src="docs/img/units/ogre_5.png" width="56" alt="Stone ogre">
<img src="docs/img/units/jailer_5.png" width="56" alt="Jailer">
<img src="docs/img/units/priestess_5.png" width="56" alt="Flame priestess">
<img src="docs/img/units/cogling_5.png" width="56" alt="Cogling">
<img src="docs/img/units/revenant_5.png" width="56" alt="Revenant">
<img src="docs/img/units/bat_5.png" width="56" alt="Giant bat">
<img src="docs/img/units/gargoyle_5.png" width="56" alt="Chain gargoyle">
<img src="docs/img/units/owl_5.png" width="56" alt="Arcane owl">
<img src="docs/img/units/wyrm_5.png" width="56" alt="Ember wyrm">
<img src="docs/img/units/chainbreaker_5.png" width="56" alt="Chainbreaker">
</p>
> *Fourteen kinds of wonderful creatures, master, and eight kinds of humans in shiny tin. I wrote everything down for
> you - who loves what, who hates whom, who learns what - in* **[Crookfinger's Bestiary](BESTIARY.md)**.
> *The same book is in the game, by the way - on the title screen and in the pause menu.*
## How to play
1. **Download [`play/DungeonCraft.html`](play/DungeonCraft.html)** and open it in your browser. That's all.
It runs offline, on desktop and on phones (touch controls switch on by themselves).
2. Or **run your own server** with Docker - with user accounts, savegames on the server and the game as an
installable app: see **[INSTALL.md](INSTALL.md)**.
**The basics, master:**
| | |
|---|---|
| Esc | Pause menu (save, load, settings) |
| Left click / drag | Use the active tool (dig and rooms: drag a rectangle) |
| Right click | Drop a unit from your hand (prisoners anywhere on your ground; onto a free torture place = locked up) |
| Shift + left click (hand) | Backhand own units (15 s faster, costs a little HP and mood) / punch heroes on your ground (15 damage, 1 s stun) |
| Right drag, WASD, arrows, screen edge | Move the camera |
| Middle drag, Q/E, R/F | Rotate / tilt the camera |
| Mouse wheel | Zoom |
| 1–9, 0, X, V, K, J, B, C, T | Select a tool or spell |
| P / Space | Pause |
| M, +/- | Sound on/off, volume |
| G | Tile outlines on/off |
| H | Help |
| Left click / drag | use the tool - dig, build rooms (drag a rectangle), cast, place towers and traps |
| Right click | drop what your hand holds (creatures, prisoners - or a sacrifice on the altar) |
| Shift + click | slap your own creatures (faster for a while) or punch a hero |
| Right drag, WASD, screen edge | move the camera · middle drag, Q/E, R/F: rotate and tilt · wheel: zoom |
| Esc | pause menu: save, load, settings, bestiary |
All keys except Esc and the arrow keys can be changed in Settings → Keys.
On phones: tap to use a tool, drag to mark or move, two fingers to zoom and turn, long press for information.
The toolbar groups the tools into categories (Dig, Hand, Rooms, Towers, Spells, Orders) plus a menu button; categories with several tools open a row above the toolbar. The keys select tools directly.
## Under the hood
### Touch (phones and tablets)
- **Rust**, compiled to **WebAssembly**, with [macroquad](https://github.com/not-fl3/macroquad) - one small module, no engine, no plugins.
- A real 3D isometric world in pixel art: chunked meshes, flickering point lights, a glow pass, particles,
billboard sprites with eight viewing directions, every frame of it drawn by code - **no image files** except the font.
- Procedural chiptune soundtrack as FastTracker 2 modules with its own XM replayer and synthesised sound effects.
- A tiny Rust savegame server (axum, Argon2 passwords) - optional; without it the game uses savegame files.
- English and German, more languages are one text file away.
The touch interface switches on automatically on touch devices or after the first touch (Settings → Game → Touch interface: Auto / On / Off). It uses a larger interface and these gestures:
## Made with Claude Code
| Gesture | Action |
|---|---|
| Tap | Use the tool (hand: pick up a unit; spells, flag, towers: at that spot) |
| Drag | Dig, rooms and sell: mark a rectangle; other tools: move the map |
| Two fingers | Move the map, pinch to zoom, twist to rotate the camera |
| Long press | Information about the spot; hand: drop the units there |
| Double tap (hand) | Backhand own units / punch heroes on your ground |
The savegame name is entered with the browser's text dialog when the name field is tapped.
## Savegames
When the game is served together with the service (`server/`, started by `docker compose`), it starts with a **login**:
- The very first login creates the **admin account** with the entered name and password - do this right after installing.
- The admin creates further users (title screen -> *Account*); every user can change the password there and log out.
- Every user only sees the **own savegames** on the server: *Save Game* and *Load Game* open the list (save under a new name, overwrite, load, delete).
- *Download Standalone* (title screen) downloads `DungeonCraft.html`: the whole game in one file, without login and without server. Savegame files (*Download as file*) open there as well.
If the service cannot be reached (for example the standalone file, `www/` opened as local files or a plain web server), there is no login and the game uses savegame files: *Save Game* downloads a `.dcsave` file (JSON), *Load Game* opens the file picker; dropping the file onto the game window works too.
The game also autosaves into the browser every two minutes and when the tab is closed; *Continue* on the title screen resumes from there.
### Savegame service
A small Rust service (axum) in `server/` keeps the user accounts (Argon2 password hashes) and the savegames as files in the Docker volume `savegames` (one folder per user). nginx forwards `/api/` to it; the service itself is not published. The standalone file is served under `/standalone/` only for logged-in users (nginx `auth_request`).
| Request | Meaning |
|---|---|
| `GET /api/health` | `ok` when the service runs (`ok setup` while there is no account yet) |
| `POST /api/login` | `{name, password}` -> `{token, name, admin}`; the first login creates the admin |
| `POST /api/logout`, `GET /api/me`, `POST /api/password` | session, own account, change password |
| `GET/POST /api/users`, `DELETE /api/users/{name}` | admin: list, create, delete users |
| `GET /api/saves` | own savegames (id, name, date, map size, wave, play time), newest first |
| `POST /api/saves?name=...` | store a new savegame (body: savegame JSON) |
| `PUT /api/saves/{id}?name=...` | overwrite a savegame |
| `GET /api/saves/{id}` | savegame JSON |
| `DELETE /api/saves/{id}` | delete a savegame |
A forgotten password is set again on the server with `docker compose stop saves && docker compose run --rm --no-deps saves dungeoncraft-saves set-password <name> '<password>' && docker compose up -d saves` (ends the user's sessions). To start over with no accounts at all, delete `users.json` and `sessions.json` in the `savegames` volume.
All requests except health and login need `Authorization: Bearer <token>`. Limits: 1 MB per savegame, 500 MB in total, 30 write requests and 20 login attempts per minute. Settings: `SAVE_DIR` (default `/data`), `LISTEN` (default `0.0.0.0:8080`).
## Adding a language
1. Copy `lang/en.lang` to `lang/<code>.lang` and translate the texts (placeholders `{0}`, `{1}` stay as they are).
2. Add the file to `SOURCES` in `src/i18n.rs`.
3. Note: the DINOBYTE font only covers the CP437 character set.
<p><img src="docs/img/claude-mark.png" width="72" alt="Claude Code mark" align="left"> &nbsp;DungeonCraft was developed with the help of
<a href="https://claude.com/claude-code"><b>Claude Code</b></a>, Anthropic's AI coding assistant - the code, the graphics,
the music, the texts and this page. Claude, Claude Code and Anthropic are trademarks of Anthropic; the pixel mark only
credits the tool.</p>
<br clear="left">
## License
DungeonCraft is released under the [MIT License](LICENSE) – this covers the code, the generated music in `assets/music/` and the procedural graphics.
The DINOBYTE font (`assets/dbyte_1x.png`) by mby is licensed separately under CC BY 4.0, see [CREDITS.md](CREDITS.md).
DungeonCraft is released under the **[MIT License](LICENSE)** - © 2026 Richard Zink. It covers the code, the generated
music in `assets/music/` and the procedural graphics.
The **DINOBYTE** font (`assets/dbyte_1x.png`) by [mby](https://mby.itch.io/dinobyte) is licensed separately under
**CC BY 4.0**. Details in [CREDITS.md](CREDITS.md).
---
<p align="center"><i>"The underworld is yours, master. Now - tea?"</i><br>- Crookfinger</p>
Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff. Show more