posted an update —


title: "Heroes Required: How We're Building a World That Runs Without Us" date: 2026-06-25

tags: [devlog, gamedev, csharp, procedural-generation]

Heroes Required: How We're Building a World That Runs Without Us

A dev blog — the game where your guild matters more than your hero


The Problem We're Solving

Most RPGs have a content problem: the world is pre-scripted. Every NPC says the same thing on Tuesday as they did on Monday. Quests don't change. Relationships don't decay. The game world is a stage set, not a living place.

Heroes Required is built around a different premise: the world keeps changing whether the player is looking at it or not, and the content engine — not a game designer — drives that change every week.

This post explains how the engine works under the hood, without touching any story details.


What We've Actually Built

The core system is a C# .NET 9 backend: Domain → Application → Infrastructure → API. Clean architecture, nothing exotic. The interesting decisions live in the design, not the stack.

Vassals Own Everything

The foundational mechanic: knowledge, abilities, and relationships never belong to the hero. They belong to hired specialists called vassals.

A Scout named Mossbridge knows where the dragon cave is. Not you. Mossbridge.

If Mossbridge leaves your guild — unhappy, underpaid, overshadowed by a more famous colleague — that knowledge walks out with her. The "Find the Dragon" quest that was available last week is now locked. Not because a designer flipped a switch, but because your team no longer has the expertise to pursue it.

guild.RemoveVassal("Mossbridge");
Assert.False(guild.Knows("DragonLocation")); // the most important unit test

The same principle applies to combat approaches, diplomatic paths, trade routes, and ancient languages. Your guild's composition at expedition time is the thing that determines what options exist — not a skill tree, not a level cap.

The Anti-Metagaming Engine

The same in-world obstacle gives different players completely different options depending on what vassals they brought.

  • Guild A (Sage + Trader): unlocks diplomatic paths via historical knowledge and faction contacts
  • Guild B (Defenders only): unlocks combat paths

Neither is correct. Neither is playing wrong. The system breaks the ability for online guides to publish "the answer" — because the answer depends on who you hired.

Assert.NotEqual(
    guildA.AvailableActions.Count(a => a.IsAvailable),
    guildB.AvailableActions.Count(a => a.IsAvailable)
);

Cultural Intelligence — Every Conflict Has Two Correct Sides

One rule governs all generated conflict: the world is not ridiculous to the people living in it.

When an NPC does something that seems strange, it's because their culture has a completely valid internal logic that outsiders misread. We've built cultural profiles for eight species — each one has a core belief, rules for how they interpret behavior, and documented historical misunderstandings with every other group.

None of this backstory is exposition-dumped on the player. It's the engine that generates the reasons NPCs give for conflict. A generated dispute between a dwarf and elf vassal reads differently from a dispute between an orc and a goblin vassal, because the rules engine draws from different cultural templates.

The profiles load from JSON at startup:

"coreIdentity": {
  "primaryBelief": "Anything worth doing should last longer than the person who created it."
}

That single line drives how every dwarf NPC behaves in conflict scenarios — patient, literal, and genuinely baffled by anyone who signs a contract without waiting two years.

The Weekly Mutation Engine

Every week, 10–15% of the world's mutable content changes. Not randomly — the mutation selector uses narrative pressure: NPCs who have been overused get a rest, NPCs who've been dormant get pushed forward, story tension accumulates and discharges.

public WorldState RunWeek(WorldState state)
{
    state.Week++;
    var targets = _selector.SelectTargets(state); // 10-15% of mutable NPCs
    foreach (var npc in targets)
        ApplyMutation(npc, state.Week);    // AI generates the content here in Phase 2
    _drama.Generate(guild, relationships); // jealousy, rivalry, recognition
    return state;
}

Right now the mutation content is a placeholder — the AI slot is intentionally empty. The philosophy: don't let the model touch anything until the rules engine can reject bad ideas. The validator that sits in front of the AI is the thing that keeps the world coherent.

The Canon Firewall

Some things about this world can never change, no matter what the AI generates. A layer called WorldTruthValidator sits in front of every mutation. It maintains a set of immutable facts. Any proposed change that targets one of those facts is rejected before it reaches the content generator.

var result = validator.Validate(
    new Mutation { Target = "GreatForgetting", Change = "Spell worked correctly" },
    canon);

Assert.False(result.Accepted); // the engine enforces this, not discipline

The backstory of this world has specific historical events that must remain true regardless of what the content engine generates week to week. The AI is allowed to be weird — to produce NPCs with unexpected motivations, alliances that make no surface sense, disputes that seem absurd. It's not allowed to break the foundation.

The 52-Week Test

The validation suite includes a full-year simulation. It runs 52 weekly ticks, applies all mutation, drama, and relationship logic, then checks:

  • The world's core truths are still intact
  • NPCs have actually changed (the engine ran)
  • No locked NPC was ever touched
  • Every guild still has at least one viable expedition path — no dominant strategy locked everything out

This is what "game design survives automation" looks like as a test suite. 124 passing tests, 0 failures.


What's Next

Phase 2: plug in the language model. The WeeklySimulationRunner has an empty slot where IContentGenerator will go — a FakeContentGenerator for tests, an OpenAIContentGenerator for production. Once the deterministic engine has sufficient test coverage, we'll trust it to validate AI output rather than just running it unchecked.

Phase 3: Unity integration. The backend will export a JSON payload per NPC that Unity consumes to drive dialogue mood, quest availability, and faction reputation modifiers. The engine tells Unity what an NPC is feeling and why. Unity just renders it.

The goal is a game that genuinely earns the phrase "living world" — not as marketing copy, but as something verifiable in a test run.


Next post: wiring the language model and building the validation layer that keeps it honest.

Log in or sign up for Devpost to join the conversation.