Lanorim

A single-player RPG that plays like a real table. Grid map, minis, a tray of physics dice, and a GM behind a screen running a branching campaign. One hero, no party to manage.

SRD spells
123 slots or points
Classes
7
Test methods
1,125 xUnit
Godot references in core
0
Role
Sole developer and designer
Status
Pre-v1, playable end to end
Stack
C#, .NET, Godot 4.7, xUnit, YarnSpinner
Source
Public on GitHub

Why it exists

I spent a long time looking for a single-player RPG that felt like a tabletop campaign without making me run a party of six or lean on companion AI I didn't enjoy. While I was rebuilding the localization engine for Portalborn I liked the game enough to start writing campaign content for it, and that clarified that the thing I actually wanted was something else.

Then the obvious caught up with me. I write software for a living.

Lanorim is that game. You play one hero. A referee runs a branching campaign from behind a GM screen, on a grid map with minis and a tray of dice. There's a companion, but it sits off the map and talks. It doesn't fight, so there's nothing to babysit.

Starting over on the SRD

The first version ran on a homebrew dice-pool system of my own. Lanorim is the rebuild: the rules are now the System Reference Document 5.2.1, used under CC BY 4.0, and the useful parts of the old project were harvested into the new one.

The rule I work to is that anything I don't deliberately change stays SRD. Every change is something I then have to build, balance, and document, so a decisions file tracks each one. The ones that made the cut are there because one hero against a group needs them:

  • The hero gets two actions, a bonus action and a reaction. Monsters don't. They play their statblock as written.
  • At 0 HP it's a single death save rather than three.
  • Casters get one extra 1st-level slot, and every hero carries a keepsake that stands them back up the first time they drop each long rest.

Each of these sits behind its own switch, so the balance simulator can play every class with the changes on and off and I can see what each one is actually worth.

The rule the architecture is built on

core/ and content/ never reference Godot. Dice, the grid and line of sight, checks and saves, the combat loop, spells, inventory, dialogue and saves are all plain C#.

That's what lets the rules run headless. The test suites exercise them without an editor, the simulator plays thousands of fights overnight, and a sample campaign plays from its first line to its last with nobody at the keyboard. The Godot project consumes the rules and adds the table, the dice bodies, audio and presentation. Nothing goes the other way.

ProjectWhat lives there
coreThe rules: dice, grid, resolution, characters, combat, magic, tables. No Godot.
contentThe SRD data and everything built on it: sheets, creation, inventory, maps, campaigns, dialogue, saves. No Godot.
core.tests, content.testsxUnit
simHeadless balance harness and dev tools
gameThe Godot project, and the only thing that touches Godot

Localization from the first commit

The engine emits localization keys and never player-facing strings. English is a locale file exactly like every other language, so adding one is a content task. A check script and a test both fail if the engine emits a key that has no English.

None of that is normal for a hobby project at this stage. I did it because I'd just spent weeks retrofitting internationalization into a shipped codebase that grew without it, and I wasn't going to do that twice.

Are the dice actually fair

The dice are physics objects, thrown into a tray and read off their resting orientation. That's more fun than a random number with an animation over it. It's also a way to introduce bias you never notice.

So the faces get tallied and run through a chi-squared test, swept per die and per tray skin, because each skin has its own physics material and a handful of dice colliding is a different system from one die alone.

Physics bit me in a smaller way too. At Godot's default 60 ticks a second, a d4 or d6 lying on a face rocks on its corners forever. The game runs physics at 120.

Rules in code, world in data

The rules are code. Campaigns are packages of data that sit loose on disk: maps, encounters, loot tables, merchants and branching dialogue, so an author can edit one without touching the engine. There's an in-game map builder for the maps, and a campaign check that warns when a fight is over budget for one hero or a story gate has only one way through. It warns and never refuses.

Where it stands

Pre-v1. The design is settled, the rules engine and data are in and tested, and every screen is wired, from the title and character creation through the table, combat, level-up, death and the end. What's left is mostly how it looks, Workshop support, Steam, and writing the real campaigns.

Read the source All projects