Our games

What we're building,
and what we built

One game in active development, one shelved for now, and a shelf of prototypes that taught us how to make the first two.

In development

Sentinels of Gleamwood

A controller-first 2D action roguelite in the Dead Cells and Hades lineage. Built in Unity 6 for Windows, coming to Steam.

Steam page not open yet Wishlisting opens when the store page goes live.

A blade, a bow, and a meter

Your kit is two slots wide. Grounded hits build juggle points; at sixty the enemy launches and hangs at the apex for eight tenths of a second, which is exactly as long as your air combo gets. Executions run on a separate meter and land on the swing after the one that fills it. Each enemy can be executed once.

Statuses are keys

Ignite and Sticky Arrows do damage over time, and they also unlock finishers. Three arrow stacks then a dash gets you an Explosion. Set something alight and follow with steel for a Firestorm. Pierce an airborne enemy and it leaves through the side of the screen.

Movement you have to buy

The wall jump and the dash cost 200 Gleam each at the hub. The generator knows what you own and refuses to build rooms you can't clear, so buying one visibly widens the game.

You keep your Gleam when you die

Gleam comes home whether you clear the run or die in the second biome. Spend it at the hub Shopkeeper on Vigor, Might, Honed Edge, movement, and permanent weapon unlocks. Weapon upgrades inside a run are run-scoped and never touch the shared definition.

02

What one hit actually does

Every hit in the game walks the same eleven steps in the same order. Describe a hit with the switches and the spine below shows which steps it runs, which it skips, and which it never reaches.

Enemy state
This hit
  1. 01

    A dead enemy ignores the hit. The hit is timestamped.

    applied
  2. 02

    Execution readiness is captured now, before this hit's points are added.

    applied
  3. 03

    Meters accrue. Juggle points only while the enemy is grounded. Execution points always.

    applied
  4. 04

    On-hit statuses apply.

    applied
  5. 05

    Feedback fires: hitstop, hit flash, the prefab's hurt sound.

    applied
  6. 06

    Damage applies, and only when MaxHealth is above zero.

    applied
  7. 07

    If readiness was captured true, the execution fires.

    applied
  8. 08

    At zero health the enemy dies. Death wins over juggle, armor and hitstun.

    applied
  9. 09

    An air hit on an already juggled enemy resets its juggle points. This is the anti-infinite rule.

    applied
  10. 10

    A full juggle meter launches the enemy from any state, armor or not.

    applied
  11. 11

    Super armor is consulted last and suppresses the hitstun only. Otherwise the enemy enters hitstun.

    applied

Outcome: Set the switches above to resolve a hit.

Figure 2.1. The order is the point. Readiness is captured at step 02 but spent at step 07, which is why the finisher lands on the swing after the one that fills the meter rather than on the swing that fills it.

The world

Ten biomes. You'll see five to seven.

The Emberfall Clearing is always first and the Demon King is always last. What comes between is a branching web, and you choose the route at every rest.

  • The Emberfall ClearingGreyboxAlways first
  • Emberfall DepthsGreybox
  • The CinderwayGreybox
  • The Ashen VaultsGreybox
  • The Pyre CrownGreybox
  • The Sunken MireGreybox
  • The Splintered SpireGreybox
  • The Hollow DeepGreybox
  • GalewatchGreybox
  • The Demon KingGreyboxAlways last

Level art is still greybox. The biomes already differ in size, branching and enemy roster. They do not yet differ in appearance. That, and the remaining bosses, are what we are building now.

Bosses

Three authored, seven slots open

Every biome has a boss arena and every arena generates. Three of them have a fight authored in. The other seven still build the corridor, seal the doors and spawn the adds, and then stop, because the fight itself is data that has not been written yet.

Boss roster, by state
SlotKitState
Krag Lunges and slams, and turns over into a second phase at half health. Authored
The Cinder Shepherd Relocates between perches and calls waves of adds. Reaching him means going through them. Authored
The Pyre Regent A straight sword fight until half health, when the arena's fire columns light. Authored
Seven further slots Arena, corridor, sealed doors and staggered adds all generate. No boss definition. Not authored
Figure 7.1. A boss is a definition, not a special case in the code, which is why an empty slot degrades to an arena full of adds rather than to a crash. Conversations rotate per encounter and the count persists in your save.

The authoring

Where the writing will go

Dialogue, boss conversations and the story panels are content. A writer opens a text file, or the authoring tool inside the editor, types the lines, and the game reads them on the next launch. There is no programmer in that loop and there is no rebuild.

The shape is four levels deep and it is the same everywhere in the game. A dialogue set belongs to one speaker and holds every beat that speaker has. A beat is keyed by a trigger, so an intro and a victory line advance independently. Inside a beat, encounters rotate, so the first time you meet someone you get the first encounter and the fourth time you get whatever the writer put there for the fourth time.

The rotation persists in the save file, keyed to the dialogue id. That is why a boss can open with something different on your second run, and it is a content decision rather than a code change.

The writing for this game has not been done yet. What sits beside this paragraph is the surface it will arrive through, with the fields left empty and the keys already in place. The importer, the save binding and the rotation are finished and covered by the test suite. The words are the part that is missing, and nothing in the engineering is waiting on them.

Dialogue/Boss_Example.ini
; A set belongs to one speaker.
[Dialogue:boss-example]
speaker  =
portrait =

; Beats are keyed by trigger.
[Beat:boss-example/intro]
; Encounter 1, first meeting.
line.1   =
line.2   =
; Encounter 2, and so on.
line.1   =

[Beat:boss-example/defeat]
line.1   =

Every key on that file is one the game already reads. Dialogue is also tier three of the five modding tiers, so a translator works through the same surface the writers do, with no Unity and no compiler.

Under the hood

Tuned as data

Every enemy is a finite state machine authored as data: states, transitions, conditions, attack phases, armour budgets and reactions, all as ScriptableObject sub-assets. Most of the shipped roster needed no new engine code, because a new enemy is usually a new arrangement of parts that already exist.

The run web, all ten biomes, every boss and their tunables live in a single text file that an editor command imports and validates. A typo raises an error and leaves the field alone rather than silently wiping the wiring.

Levels are a pure function of your seed, so a suspended run regenerates instead of serialising geometry. And before a layout ships, a reachability auditor floods it using the shipped controller's real movement numbers. It sweeps the same collider box and the same jump arc the player has, so generation cannot strand you behind a gap you are unable to clear.

657PlayMode tests passing
9Enemy definitions
5Modding tiers
2Validators, one a build gate

Modding

Rebuild the whole run without opening Unity

Five override tiers, all plain text sitting next to the executable. Retune an enemy or declare a brand-new behaviour graph. Clone a weapon and rewrite its combo. Translate every line of dialogue. Reskin the player. Or re-web the entire game, choosing which biomes appear, in what order, where it branches and where it ends.

A mod is a campaign, and a save slot is bound to one the moment it's created, so a save from one campaign cannot be opened by another. Every template ships generated from the live build and carries the real current values, so the templates stay in step with the game.

Mods/mycampaign/RunWeb.ini
[Plan]
entry = start

[Node:start]
biome  = emberfall
rest   = Rest_EmberTrail
next   = t2-a, t2-b, t2-c

[Node:t2-b]
biome  = ashen-vaults
rest   = Rest_EmberTrail
next   = t3-b, t3-c
; skip the fight, and the reward with it
bypass = t3-a

Also from the studio

Codename: Shifter

A top-down arcade shooter in Unreal Engine 4, with 187 C++ files in the game module. Behaviour trees drive the enemies, with custom tasks, services and decorators, weight-based swarm evaluation and a shared target distributor. On top of that sit a combat director and an encounter subsystem that decide what shows up and when.

Shifter was a team project, and the parts we wrote are the player pawn and its controller, the AI movement component and controller, the combat director, and the encounter subsystem. Development is paused while Gleamwood has our attention.

The Shifter fighter craft: a blue and navy armoured hull with a light-blue canopy, copper trim and glowing magenta thruster rings.

Earlier work

Prototypes and experiments

The things that taught us what we know, all of them open source.

GGJ 2018 prototype

Infected

A Global Game Jam 2018 submission. Fast-paced gameplay created during the 48-hour game jam challenge.

View on GitHub, Infected

Archived

Rocket Recover

Previously published mobile game (Project ShipJump). May be re-released as a WebGL version in the future.

View on GitHub, Rocket Recover

Follow the build

Gleamwood ships when it is ready. The Steam page is not open yet, so for now the best way to hear about it is to say hello.