Year one retrospective dev blog cover showing studio development timeline
Back to Dev Blog
studio retrospective engineering

Year One Review: What We Built, Broke, and Kept

Lightfury Games started in 2024. One year in, we have three things to account for: what we built and still use, what we built and threw away, and what is still in the codebase because nobody has been brave enough to delete it yet. This is the honest version of that accounting.

What We Kept: The Spatial Grammar System

The terrain generation approach that ships in Lumenfall today is based on a spatial grammar engine that we built in the first six months and have been evolving ever since. The core of it has not been rewritten, which after a year of adding features and changing requirements is a sign that the original design was sound.

The spatial grammar works by applying production rules to a grid of zone seeds. Each rule fires conditionally based on the current state of neighboring cells. The rules encode geological logic: what terrain types can adjoin, what minimum distances are required between incompatible formations, how elevation propagates. The grammar produces something that looks like landscape because it is shaped by the same relational constraints that produce real landscape, not because anyone drew it.

The original grammar had 18 rules and took about an hour to get running the first time. The current grammar has over 120 rules and resolves in under two seconds for a full world map. The performance improvement came incrementally as we profiled and optimized, but the rule structure it is built on was stable enough to absorb those changes without a rewrite. That stability was not obvious when we chose the approach. We were gambling on an architecture we thought was right. We were lucky that the bet paid off.

What We Threw Away: The Authored Zone Library

The first approach we tried for terrain diversity was a library of hand-authored zone templates. The idea was reasonable: procedural generation can produce structurally varied terrain, but curated templates ensure that every zone has a specific quality. You do not just get a mountain; you get a mountain with interesting things to find.

We built 34 zone templates over about three months. It was slow, careful work. The result was a world that looked like it had been designed, which was the problem. Players in early internal testing noted that different regions felt like variations on a small number of themes. They were. The template library had 34 entries but most of the world was covered by about 8 high-frequency templates. The long tail of rarely-used templates was technically available but practically invisible in play.

The template library also constrained the spatial grammar. Grammar rules had to accommodate template requirements, which made the rule set harder to evolve. When we added a new geological feature type, we had to update both the grammar and the template library, and they sometimes wanted incompatible things.

We scrapped the template library after three months of trying to make it work alongside the grammar. The right way to think about it is that we paid three months to learn that the template approach was pulling at the wrong point. The grammar encodes what is possible. Templates limit what you get. For an infinite-world game, limiting what you get is exactly wrong.

What Is Still in the Codebase: The Faction Scaffolding

Early in development we had a different vision for how NPCs would be organized in the world. Rather than the trait-based individual synthesis system we use now, we planned a faction model: named groups with explicit territories, relationships, and ideologies. Characters would derive their personalities from their faction membership. The world map would show contested borders between factions, and events would shift those borders over time.

We built significant scaffolding for this system before we started questioning the premise. The faction model required an authored faction set, which meant some number of factions would appear in every world. That authoring load was manageable. The problem was territory. For factions to have meaningful territories, territories had to persist across the generative run. Persistent features in a runtime-generated world require special handling at every stage of the generation pipeline. The complexity was real and growing.

The faction scaffolding is still in the codebase. Some of it is still used: faction-like groupings appear in NPC social clusters, and the relationship graph machinery from the faction prototype handles NPC social proximity. But the authored faction names, the territory contest system, the cross-session faction state, all of that is commented out. We have not deleted it because there is a version of Lumenfall that revisits the faction model at a smaller scope, maybe as an optional layer rather than a core system. We have not decided. The code is there as a record of what we built and why we stopped.

The Technical Debt We Accumulated and Why

Three specific areas accumulated debt that we are still paying down.

The world seed architecture was designed before we understood how save state would work in a game where the world generates at runtime. The seed is now doing more work than it should: it encodes the initial generation state, the player's position within that generation, and a delta of any runtime changes to the world. Those should be separate systems. Separating them is a refactor that will happen before launch but has not happened yet because it requires touching every part of the generation stack.

The NPC dialogue cache is a second debt area. The synthesis system generates dialogue nodes as needed, which means dialogue generation can spike at moments of high NPC density. We cache generated dialogue aggressively, but the cache invalidation logic was written quickly and is incorrect in edge cases. Players have occasionally reported NPCs saying things that belong to a different interaction context. The root cause is a stale cache entry being returned for a context it was not generated for. Fixing it correctly means rethinking the cache keying scheme. It is on the roadmap.

The third debt area is asset management in the generation pipeline. When the terrain grammar generates a new zone, it pulls from an asset pool of visual elements. The pool management code was written for a smaller pool than we have now, and the lookup performance degrades at scale. The degradation is not yet player-visible, but it will be if we continue expanding the visual vocabulary without refactoring the lookup.

What Year Two Looks Like

The primary technical goal for year two is stability at scale. The generation systems work well in the range we have tested. The question we do not yet have a confident answer to is how they behave at the extremes: very long sessions, very dense zones, world seeds that hit edge cases in the grammar rule set. That answer requires more playtesting at larger scale than we have run so far.

The second goal is the interaction depth problem the closed alpha surfaced. Individual systems are coherent. The connections between systems need work. Terrain shapes character populations, which shapes NPC interactions, which shapes how the narrative layer has material to work with. That chain should feel tight and continuous from a player standpoint. Right now it has gaps. Closing those gaps is design work as much as engineering work, and it is the hardest kind because the failures are felt rather than measured.

More from the Dev Blog

All posts
Alpha 04 terrain synthesis blog cover

Alpha 04: Terrain Synthesis Gets Smarter

Read post
Angel funding blog cover

Angel-Backed: What Changes and What Stays the Same

Read post
Studio founding story blog cover

Why We Started Building AI-Generated Open Worlds

Read post