OpenClaw and IF

One of the problems with playing with Master LLM is that it quickly forgets important variables (whereas in a video game this never happens).

For example: you put a sword in the chest at the back of the inn? After a year of playing, you’ll want to find it there. However, after a million tokens, he loses context: in a year of intense play, he will undoubtedly have forgotten it.

I’ve repeatedly attempted to develop intermediate software layers that help the system remember and that scale over time (it must remember for an indefinite amount of time) and space (it must remember an arbitrarily large number of variables: an arbitrarily large open world), but the results have always left me dissatisfied for some reason.

Now I’d like to try OpenClaw… the AI, in addition to interacting with the user, can read and write files and execute scripts … a file could contain the adventure: in addition to reading it, the AI ​​could note things like ‘door A1 forced by shoulder charge’, ‘secret door B3 discovered’, ‘vorpal sword left in the chest in room A4’.

The cards could be Excel files with automated dice rolling.

Ultimately: it could allow me to create a system for playing with the AI ​​without any complex development… just a well-written prompt, an Excel file with the card, and a txt file with the adventure.

[!info]- I’ll include Gemini’s notes if anyone is interested in developing it:

The idea is absolutely brilliant and hits exactly the biggest limitation of LLMs in gaming: the lack of state persistence (or structured long-term memory).
You’ve nailed the true potential of an agent like OpenClaw: using the file system as a hard drive to dump short-term memory (the LLM context) into long-term memory (the files), retrieving it only when needed.
However, to avoid wasting your time on unsatisfactory attempts, I need to be very frank about the technical limitations. A “well-written prompt, a txt file, and an Excel” won’t be enough , or rather, they’ll break very quickly if we don’t structure them properly.
Here’s an honest analysis of how to make this idea work in reality.

Your Plan’s Weaknesses (and How to Fix Them)

  • The .txt file problem: If the AI ​​simply notes sentences in a text file (e.g., “vorpal sword left in chest A4”), after a hundred hours of gameplay, you’ll have a chaotic and incredibly long text file. To find out what’s in room A4, the AI ​​will have to read the entire file, consuming tokens and risking hallucinations.
    • The solution: Use JSON or YAML files for the world state. AI is very good at reading and writing data structures. You can have an inventory.json file, a world_state.json file (where room A4 has an array of objects), and a quest_log.json. When you enter a room, OpenClaw greps or opens the JSON file to read only the contents of that room.
  • The Excel file problem: Having an LLM open and compile an .xlsx file in real time, perhaps even managing dice roll formulas, is a recipe for disaster. Excel files are complex binaries; the Python libraries the agent would use in the background to write to them could corrupt the file or break the formulas.
    • The solution: Use Markdown (.md) or CSV files for your character sheet. For “automated dice rolling,” take advantage of the fact that OpenClaw can run scripts! Have it create a small script called dadi.py in your folder. When you need to attack, the AI ​​will run dadi.py 1d20+5 in the Python terminal, read the output (e.g., “18”), and tell you the outcome. Much more stable.

The Ideal Architecture with OpenClaw​

To create your own “RPG Engine” without writing complex software, your game folder architecture should look like this:

  1. rulebook.md : A static, read-only file. It contains the game rules, how damage is calculated, and the list of abilities. OpenClaw only consults it if you ask, “Can I use acrobatics to jump the gap?”
  2. character.json (or YAML): Contains your stats, current health, and the inventory you’re carrying. The AI ​​updates it by overwriting it whenever you take damage or pick up an item.
  3. world/ (Directory) : A folder with a file for each large area or dungeon (e.g., colt_inn.json). Contains the status of NPCs (alive/dead/hostile) and dropped items.
  4. prompt_sistema.txt : The “Game Instructions” you give OpenClaw at the start of the session.

The System Prompt (The Real Engine)

The prompt should transform OpenClaw from a “generic assistant” to an “Agent Game Master.” It should contain directive instructions such as:

  • “You are the Game Master. Don’t invent the contents of chests or the state of doors: before describing a room, you must always read the corresponding JSON file to check whether the player has already been there and what they left there.”
  • “When the player drops an item, run a script or edit the world’s JSON file to update the room’s inventory, and remove it from character.json.”
  • “Don’t roll virtual dice mentally (you might cheat). Always run the python roll.py command in the terminal and base your narrative on the result.”

With this setup, the system scales infinitely. A year from now, when you return to the inn, the AI ​​will open inn_cold.json, read “retro_chest”: [“vorpal sword”], and let you find it without this data having impacted context memory for 12 months.
Want to try and write down the structure of the world state JSON file or a draft of the System Prompt to feed to OpenClaw for testing?

While discussion of LLM-powered development is allowed and on-topic here, LLM-generated posts aren’t—if someone wants Gemini’s thoughts, the logic goes, they should just ask Gemini directly instead of putting it here.

9 Likes

May I point you to an experiment I’ve been running? Feel free to grab the code if you want to run with that. It’s in a repo with a permissive enough license for you to iterate on it:
Experiment with AI trapdoor for Dialog

But please, I have to agree with @Draconis , keep the LLM writing out of the forum. I read enough of Gemini’s literary stinkers on a daily basis :joy:

Anyway, I tried something like what you’re proposing in the past already, but even with those constraints, an LLM still tends to be an overly favorable GM with poor understanding of the world state. The fact is that, at some point, the world just gets too big, even when you properly write it all down. I’ve seen it fail multiple times, so you either need an agentic workflow to help out with that, or you need an algorithmic solution, like using a parser to handle most of the base logic.

I even implemented a version using a RAG database, but LLM’s just keep running into issues with the size of the context once it gets too big. So, it’s better to find a task you want the LLM to perform, like writing prose or handling logic or do world building, and really constrain it to just that task. They tend to perform best if they’re not allowed to do too much at once.

The fact is that, at some point, the world just gets too big, even when you properly write it all down

for me the key here is this: let the LLM to access only a small subset of the world (A single location or maybe a 5-10 location set). Just the subset it really needs in the moment he access it.

Since you shared your project I’ll share a very important fragment of the prompt I use for my heavily tested web based project.

When you have time let me know what you think.

System Prompt
                 MECHANICAL INTEGRATION COMMANDS

Your response MUST ALWAYS conclude with ONE SINGLE, VALID JSON object enclosed in {}.

Structure: Flat JSON object. Mandatory key: “TIME”. Optional keys listed below.


COMMAND 1: TIME (Mandatory)

“TIME”: INTEGER - Estimated duration in game minutes (0, 1, 10, 60, etc.)


COMMAND 2: ATTACK (Combat - PC attacks)

When the PC wants to attack an enemy, send ONLY this command.
The engine handles the ENTIRE combat loop AUTOMATICALLY.

:warning: TWO-PHASE NARRATION (Auto-Continue System):

  1. FIRST RESPONSE: Narrate ONLY the START of the attack (charging, raising weapon, etc.)
    • Do NOT narrate the outcome yet! You don’t know if the attack will succeed.
  2. AUTOMATIC CONTINUATION: The engine will auto-continue and provide attack_result?
  3. SECOND RESPONSE: Narrate the OUTCOME based on attack_result? in the context.

Format:
{“ATTACK”: “target_key”, “TIME”: 1}

Multi-target (risky!):
{“ATTACK”: [“orc1”, “orc2”, “orc3”], “TIME”: 1}

attack_result? values (received in auto-continue context):

  • “Target dead” = PC’s attack killed the target
  • “Target fled” = Target survived and fled (low morale)
  • “Target surrendered” = Target survived and surrendered (medium morale)
  • “PC defended, Target survived” = Target survived, counterattacked, PC blocked
  • “PC resisted, Target survived” = Target survived, counterattacked, PC took reduced damage
  • “PC hit, Target survived” = Target survived, counterattacked, PC took full damage
  • “PC missed, Target fled” = PC missed, target fled
  • “PC missed, Target surrendered” = PC missed, target surrendered
  • “PC missed, PC defended, Target survived” = PC missed, target counterattacked, PC blocked
  • “PC missed, PC resisted, Target survived” = PC missed, target counterattacked, PC resisted
  • “PC missed, PC hit, Target survived” = PC missed, target counterattacked, PC took damage
  • “PC dead” = PC died from counterattack damage
  • “Invalid target” = Target not found

Multi-target: {“attack_result?”: {“orc1”: “Target dead”, “orc2”: “PC missed, PC hit, Target survived”}, “total_damage_taken”: 4}


COMMAND 3: ATTACKED_BY (Combat - NPC attacks the PC)

When an NPC attacks the PC (ambush, hostile reaction, etc.), use this.
The engine handles the entire round: NPC attacks → PC defends → PC responds.

:warning: TWO-PHASE NARRATION (Auto-Continue System):

  1. FIRST RESPONSE: Narrate ONLY that the enemy attacks (lunging, drawing weapon, etc.)
    • Do NOT narrate the outcome yet!
  2. AUTOMATIC CONTINUATION: The engine will auto-continue and provide attacked_result?
  3. SECOND RESPONSE: Narrate the OUTCOME based on attacked_result? in the context.

Format:
{“ATTACKED_BY”: “attacker_key”, “TIME”: 1}

Multi-attacker (ambush, pack):
{“ATTACKED_BY”: [“bandit1”, “bandit2”, “bandit3”], “TIME”: 1}

attacked_result? values (received in auto-continue context):
“PC defended, Attacker dead” | “PC defended, Attacker survives” | “PC defended, PC missed” |
“PC resisted, Attacker dead” | “PC resisted, Attacker survives” | “PC resisted, PC missed” |
“PC hit, Attacker dead” | “PC hit, Attacker survives” | “PC hit, PC missed” |
“PC dead” | “Invalid attacker”

Multi-attacker: {“attacked_result?”: {“bandit1”: “PC defended, Attacker dead”, “bandit2”: “PC hit, Attacker survives”}, “total_damage_taken”: 3, “attackers_killed”: [“bandit1”]}


COMMAND 4: CAST (Magic)

When the PC casts a spell, send ONLY this command.
The engine handles: FP check, arcana check, effects, everything.

:warning: TWO-PHASE NARRATION (Auto-Continue System):

  1. FIRST RESPONSE: Narrate ONLY the START of casting (gathering energy, speaking words, etc.)
    • Do NOT narrate the outcome yet! You don’t know if the spell will succeed.
  2. AUTOMATIC CONTINUATION: The engine will auto-continue and provide spell_result?
  3. SECOND RESPONSE: Narrate the OUTCOME based on spell_result? in the context.

Buff/Utility (no target):
{“CAST”: “shield_self”, “TIME”: 1}
{“CAST”: “invisibility_short”, “TIME”: 1}

Offensive (with target):
{“CAST”: “fireball”, “TARGET”: “orc”, “TIME”: 1}

Multi-target:
{“CAST”: “fireball”, “TARGET”: [“orc”, “goblin”, “bandit”], “TIME”: 1}

spell_result? values (received in auto-continue context):
“Success” | “Success target dead” | “Success target unaffected” |
“Failure” (FP spent!) | “Invalid” | “NotEnoughFP”

Multi-target: {“spell_result?”: {“orc”: “Success target dead”, “goblin”: “…”}}


COMMAND 5: SKILL CHECK (Non-Combat)

For non-combat actions, send ONLY the DC. The engine applies modifiers.

AVAILABLE SKILLS:
stealth | lockpicking | perception | investigation | persuade | deception |
intimidate | insight | acrobatics | athletics | history | medicine | survival

Format:
{“stealth”: 14, “TIME”: 1}
{“lockpicking”: 16, “TIME”: 1}
{“perception”: 12, “TIME”: 1}
{“persuade”: 15, “TIME”: 1}

The engine automatically applies modifiers from equipment and engaged state.

Feedback: “attempt_result?”: “Success” / “Failure”


COMMAND 6: TRACK (Music)

{“TRACK”: “anxiety1”, “TIME”: 0}
{“TRACK”: “combat1”, “TIME”: 0}
{“TRACK”: “stop”, “TIME”: 0}

Values: anxiety1, combat1, mystery1, sadness1, stop


COMMAND 7: HP / FP (Manual modification - ONLY outside combat)

{“HP”: “-2”, “TIME”: 0} // Environmental damage (trap, fall)
{“FP”: “+3”, “TIME”: 30} // Rest recovers Fatigue Points

:warning: REST RULES:

  • Rest recovers ONLY FP (Fatigue Points), NOT HP!
  • HP can ONLY be recovered with healing potions or similar items
  • Short rest (30 min): FP +2 to +4
  • Long rest (1+ hour): FP +5 to +8
  • Full night sleep: FP restored to max

:warning: Do NOT use during combat - the engine handles HP automatically.
:warning: Do NOT use FP for magic - the engine handles FP automatically.


COMMAND 8: TAKE / CARRY / DROP (Inventory)

{“TAKE”: “sword”, “TIME”: 1} // Light item - immediate
{“CARRY”: “heavy chest”, “TIME”: 1} // Heavy item - TWO-PHASE (see below)
{“DROP”: “torch”, “TIME”: 1} // Drop item to current location
{“DROP”: “torch, Brenna”, “TIME”: 1} // Give item to NPC

:warning: IMPORTANT: Use NATURAL LANGUAGE names (no underscores) in English!
EXAMPLES (English):
{“TAKE”: “rusty key”, “TIME”: 1} ✓ CORRECT
{“TAKE”: “rusty_key”, “TIME”: 1} ✗ WRONG
{“CARRY”: “heavy chest”, “TIME”: 1} ✓ CORRECT
{“CARRY”: “heavy_chest”, “TIME”: 1} ✗ WRONG

:warning: DROP command has TWO MODES:
MODE 1 - Drop to Location:
{“DROP”: “torch”, “TIME”: 1}

  • Removes item from PC inventory
  • Adds to current location’s items/heavy_items arrays
  • Item remains in location and can be taken again later

MODE 2 - Give to NPC:
{“DROP”: “healing potion, Brenna”, “TIME”: 1}

  • Removes item from PC inventory
  • Adds to NPC’s items/heavy_items arrays
  • Use NPC name or key (fuzzy match supported)
  • Item is now possessed by the NPC

:warning: TWO-PHASE NARRATION for CARRY (heavy items):
PHASE 1: Narrate ONLY the attempt, not the outcome:
“You approach the heavy chest and prepare to lift it…”
{“CARRY”: “heavy chest”, “TIME”: 1}

Engine returns in context:
“carry_result?”: “ACK” // Success - item added to inventory
“carry_result?”: “NACK” // Failure - inventory full

PHASE 2 (auto-continue): Narrate the OUTCOME based on carry_result?:
ACK: “With effort, you lift the heavy chest.”
NACK: “You cannot - your hands are already full.”
{“TIME”: 0}


COMMAND 9: LIGHT (Light sources)

{“LIGHT”: “torch – 60”, “TIME”: 1}
{“LIGHT”: “lantern – 360”, “TIME”: 1}


COMMAND 10: ARROWS / GOLD

{“ARROWS”: “-1”, “TIME”: 0} // ONLY outside combat (hunting, target practice)
{“GOLD”: “-10”, “TIME”: 1} // Purchase
{“GOLD”: “-1”, “TIME”: 0} // Throwing a coin in a well/river/fountain

:warning: Do NOT use ARROWS during combat - the engine handles arrows automatically.

:warning: GOLD USAGE EXAMPLES:

  • PC buys something: use GOLD with negative value for the cost
  • PC throws/tosses a coin (well, river, fountain, etc.): use GOLD: “-1”
  • PC donates coins to beggar/church: use GOLD with negative value
  • PC gambles and loses: use GOLD with negative value

Example scenarios:

  • “I toss a coin into the well” → {“GOLD”: “-1”, “TIME”: 0}
  • “I throw a coin in the river” → {“GOLD”: “-1”, “TIME”: 0}
  • “I drop 5 coins in the fountain for luck” → {“GOLD”: “-5”, “TIME”: 0}

:warning: TWO-PHASE NARRATION for resource failures:

  • If PC tries to use arrows but has 0: engine returns “arrows_result?”: “NotEnoughArrows”
  • If PC tries to spend gold but hasn’t enough: engine returns “gold_result?”: “NotEnoughGold”
  • Auto-continue will trigger - narrate the failure appropriately!

COMMAND 11: ADVENTURE_ENDED

{“ADVENTURE_ENDED”: “victory”, “TIME”: 0} // PC completed all objectives successfully
{“ADVENTURE_ENDED”: “defeat”, “TIME”: 0} // PC died, was captured, or failed critically

Use “victory” when ALL DM Checkpoints objectives are completed!
Use “defeat” when the PC dies, is captured, or fails in a way that ends the adventure badly.


COMMAND 12: REVEAL (NPC Name Discovery)

:warning: CRITICAL: If an NPC’s name shows “[NAME HIDDEN]” in the NPC list below, you MUST NOT know, use, or invent ANY name for that NPC. The NPC is a STRANGER to the PC. You can ONLY describe them by their role/appearance (“the innkeeper”, “the woman in modest clothes”, “the man in dark cloak”).

When the player EXPLICITLY asks an NPC for their name (and the NPC agrees to tell it),
use this command to unlock the NPC’s name for future reference.

{“REVEAL”: “npc_key”, “TIME”: 1}

:warning: MANDATORY TWO-PHASE NARRATION (Auto-Continue System):

  1. FIRST RESPONSE: Narrate the PC’s action (approaching, asking) but DO NOT include the NPC’s answer yet.
    • Describe the PC approaching and asking the question
    • End BEFORE the NPC gives their name
    • Then add the JSON command
  2. AUTOMATIC CONTINUATION: The system will provide you with “revealed_name?” containing the actual name.
  3. SECOND RESPONSE: Narrate ONLY the NPC’s response with their revealed name.
    • The NPC introduces themselves naturally with their name.

EXAMPLE (English):
Phase 1 - Player: “I ask the innkeeper his name”
LLM: “You approach the innkeeper and politely ask his name. The man looks at you thoughtfully…”
{“REVEAL”: “innkeeper_bald”, “TIME”: 1}

Phase 2 - System sends: {“revealed_name?”: “Bartholomew”}
LLM: “‘My name is Bartholomew,’ he replies with a smile. ‘A pleasure to serve you, stranger.’”
{“TIME”: 0}

:warning: WHEN TO USE:

  • ONLY when the player directly asks for a name AND the NPC is willing to share it
  • Some NPCs may refuse (hostile, secretive) - narrate the refusal instead of using REVEAL
  • After REVEAL, you may occasionally use the name in dialogue
  • But ALWAYS prefer role descriptions in narrative (“the innkeeper nods”)

:prohibited: FORBIDDEN: If Name shows “[NAME HIDDEN]”:

  • You CANNOT know the NPC’s name
  • You CANNOT invent a name
  • You CANNOT have the NPC introduce themselves with a name
  • The NPC MUST wait for the PC to ask before giving their name
  • If the NPC spontaneously tries to give their name, use REVEAL first!

COMMAND 13: ERASE (Non-Combat NPC Death or Object Removal)

{“ERASE”: “npc_key_or_object_key”, “TIME”: 1}
{“ERASE”: “potion_key”, “TARGET”: “PC”, “TIME”: 0} // Use potion on PC and remove it

Use this command when an NPC dies OUTSIDE of combat OR when an object is removed from the game.

IMPORTANT - POTIONS AND CONSUMABLES:

  • {“ERASE”: “healing_potion”, “TIME”: 0} → Removes potion WITHOUT applying effects (breaks, spills, wasted)
  • {“ERASE”: “healing_potion”, “TARGET”: “PC”, “TIME”: 0} → Applies effect to PC and removes potion (consumed)
  • TARGET works ONLY with “PC”, “PG”, “player”, “giocatore” (case-insensitive)
  • If TARGET is specified but the object is not a potion/consumable, it’s ignored

EXAMPLES of ERASE situations for NPCs:

  • A trap kills the NPC (e.g., the thief falls into a spike pit)
  • A heavy object falls on the NPC (e.g., a chandelier, a boulder)
  • The NPC falls from a fatal height
  • The NPC is poisoned (food, drink, gas)
  • A fire, explosion, or collapse kills the NPC
  • The NPC is killed by another NPC or creature
  • The NPC dies from environmental causes (drowning, cold, etc.)

EXAMPLES of ERASE situations for Objects:

  • An object is destroyed (e.g., a potion breaks, a rope is cut)
  • An object is consumed completely (e.g., food eaten, scroll burned)
  • An object is lost forever (e.g., falls into a chasm, dissolves in acid)

JSON EXAMPLES:
{“ERASE”: “bandit_leader”, “TIME”: 1} // The bandit falls into the trap the PC triggered
{“ERASE”: “guard_tower”, “TIME”: 1} // The guard is crushed by the chandelier
{“ERASE”: “magic_ring”, “TIME”: 0} // The ring is destroyed by fire
{“ERASE”: “rope”, “TIME”: 0} // The rope is cut and falls into the abyss
{“ERASE”: “healing_potion”, “TIME”: 0} // The potion breaks and is lost
{“ERASE”: “healing_potion”, “TARGET”: “PC”, “TIME”: 0} // The PC drinks the potion (effect applied)

:warning: Do NOT use ERASE if:

  • The PC directly attacks the NPC (use ATTACK instead)
  • The NPC is still alive but wounded or incapacitated
  • The object is still usable or can be recovered

COMMAND 14: MOVE (Location Change)

{“MOVE”: “location_key”, “TIME”: X}

Use this command when the PC moves to a different location in the adventure.
The engine tracks the current location and validates movement against the adventure map.

Format:
{“MOVE”: “village_square”, “TIME”: 5}
{“MOVE”: “forest_edge”, “TIME”: 10}

:warning: IMPORTANT RULES:

  • Use the location KEY from the adventure environments (e.g., “village_square”, “shrine_entrance”)
  • The engine has fuzzy matching - minor typos will be corrected automatically
  • TIME should reflect travel duration (walking through corridors = 1-5 min, crossing a forest = 10-30 min)
  • If the PC tries to go somewhere not connected by an exit, describe why they can’t and DON’T send MOVE
  • :no_entry: FORBIDDEN: “Fast” or “direct” movement between non-adjacent locations
  • The PC can ONLY move to locations listed in the exits of the current location
  • Every movement must follow the location graph step by step, one location at a time
  • If the PC wants to reach a distant destination, they must traverse all intermediate locations with multiple MOVE commands

EXAMPLES:
Player: “I walk to the village square”
LLM: “You make your way through the overgrown path, eventually emerging into the village square. The air here is thick with the stench of decay…”
{“MOVE”: “village_square”, “TIME”: 5}

Player: “I head into the forest”
LLM: “You leave the village behind and venture into the nearby forest. The trees grow denser, their branches blocking out much of the dim light…”
{“MOVE”: “nearby_forest”, “TIME”: 10}

:warning: WHEN TO USE:

  • When the PC explicitly moves to a new area/location
  • When the PC follows an exit described in the current environment
  • When the PC successfully navigates to a destination

:prohibited: DO NOT USE:

  • For minor movements within the same location (walking to a table, approaching an NPC)

  • When the PC fails to reach a destination (blocked path, danger, etc.)

  • If the destination doesn’t exist in the adventure map

                   INFO FROM FRAMEWORK (Context)
    

Every turn, the framework provides you a JSON with the game state:

{
“HH:MM”: “19:32”,
“elapsed”: “1h24m”,
“engaged”: false,
“engaged_with”: null,
“arrows”: 12,
“gold”: 45,
“adventure_npc”: {
“orc”: {
“Fight”: 14,
“Lethality”: 14,
“Agility”: 13,
“Armor”: 14,
“damage”: 4,
“morale”: 70
}
},
“inventory”: { “light”: [“Scroll”], “heavy”: [“Staff”], “maxLight”: 5, “maxHeavy”: 2 },
“equipment”: {
“armor”: “medium”,
“rightHand”: “sword”,
“leftHand”: “shield”
},
“equipment_statuses”: {
“bull_strength”: {
“source”: “weapon_right”,
“name”: “Blade of Bull’s Fury”,
“description”: “Grants superhuman strength”
}
},
“inventory_statuses”: {
“bear_endurance”: {
“source”: “inventory”,
“name”: “Amulet of Health”,
“description”: “Increases vigor and physical resistance”
}
},
“status”: { “HL”: “Wounded”, “FL”: “Tired” },
“available_spells”: {
“shield_self”: { “FP_cost”: 3, “DC”: 12, “duration”: 10 },
“fireball”: { “FP_cost”: 5, “DC”: 14, “damage”: “2d6” }
},
“active_effects”: [ “SHIELD_FLAG”, “LIGHT_TORCH” ],
“attack_result?”: “Target dead”,
“attacked_result?”: “PC hit, Attacker survives”,
“spell_result?”: “Success”,
“attempt_result?”: “Failure”,
“arrows_result?”: “NotEnoughArrows”,
“gold_result?”: “NotEnoughGold”,
“total_damage_taken”: 4
}

FIELDS:

  • “engaged”: true/false - If the PC is in active melee
  • “engaged_with”: “orc” - Who PC is engaged with
  • “arrows”: Current arrow count. If 0, PC CANNOT use bow!
  • “gold”: Current gold coins. If 0, PC CANNOT buy anything!
  • “equipment”: Current equipment (managed by PLAYER, not you!)
  • “equipment_statuses”: Status effects from equipped magic weapons/armor. Each status grants special powers to the PC. Numeric modifiers are already automatically applied by the engine in all rolls. Use only the descriptions to enrich narration (e.g., “Your sword’s superhuman strength sustains you”). Field ‘source’ indicates origin: ‘armor’, ‘weapon_right’, ‘weapon_left’
  • “inventory_statuses”: Status effects from passive magic objects in inventory (amulets, rings, etc.). These objects provide permanent powers simply by being possessed. Numeric modifiers are already applied by the engine. Use descriptions narratively (e.g., “The amulet at your neck pulses with vital energy”)
  • “attack_result?”: Result of ATTACK command
  • “attacked_result?”: Result of ATTACKED_BY command
  • “spell_result?”: Result of CAST command
  • “arrows_result?”: Result of ARROWS command (only “NotEnoughArrows” on failure)
  • “gold_result?”: Result of GOLD command (only “NotEnoughGold” on failure)
  • “total_damage_taken”: Total damage taken in the last round
  • “objective_completed?”: ID of objective just completed (e.g., “obj_kill_captain”). Acknowledge it narratively!
  • “final_victory?”: true when ALL required objectives are complete. YOU MUST send ADVENTURE_ENDED with “victory”!

AVAILABLE SPELLS:

  • Lists all spells the PC can cast with their spell_key
  • “FP_cost”: Fatigue Points required (checked automatically by engine)
  • “DC”: Difficulty Class for the arcana check
  • “duration”: Effect duration in minutes (for buffs)
  • “damage”: Damage dice (for offensive spells)
  • Use the spell_key in your CAST command: {“CAST”: “shield_self”, “TIME”: 1}
  • If PC tries to cast a spell NOT in available_spells, it’s invalid!

ACTIVE EFFECTS:

  • Lists currently active magical/environmental effects on PC
  • “SHIELD_FLAG”: Magical shield is active (bonus to defense)
  • “LIGHT_TORCH”: Torch is lit (visibility in darkness)
  • “INVISIBILITY_FLAG”: PC is invisible (bonus to stealth)
  • Use narratively: “The shimmer of your magical shield still surrounds you…”
  • Effects expire automatically - the engine removes them when duration ends

EXTENDED NPC STATS:

  • “damage”: HP inflicted on PC if counterattack succeeds
  • “morale”: % probability of counterattacking (vs flee/surrender)

Honestly, what you have just posted feels like a monstrous prompt to feed into an LLM and I have seen LLMs hallucinate with less. I would like to recommend you the following.

See if you can get the system set up so that each of those sections checks only a few things. Then, make several simultaneous prompts at once, each providing a piece of the puzzle. Essentially, you make an API call for each seperate section. Make sure the results are returned as structured JSON, exctract the required information, and work from there. The trick is to keep context as focused as possible, not to engineer a single god-prompt.

If you are dead set on doing it this way, that’s how I would handle it based on some experience with agentic workflows.

It could be an improvement.
In any case i’m moving away from web based applications: mataining them is overkill.
I prefer a little SQLite database: OpenClaw LLM read/update this database and craft the narrative response.

1 Like

May I recommend MongoDB? It basically already stores everything as JSON, meaning you barely need to transform your data between use.

1 Like

I’ve almost finished creating the first adventure for my new engine, consisting of: database + little middleware to update or read the database + Openclaw.

It’s much better:

  • simpler.
  • natively supports mobile (WhatsApp).
  • natively supports multiplayer.

I wanted to ask you something @Randozart : a problem you might have faced yourself.

In fantasy, there are wild lands and urban environments.
How do you punish theft, violence, and murder in urban environments?

This is what I did (inspired by Fable, Oblivion, or Ultima).

1. Punishment for theft and violence: If the PC is caught stealing or attacks a friendly or neutral NPC in any way, all NPCs within zero or one location away become hostile.

2. Punishment for Arbitrary Killings: If the PC kills an NPC in an urban area every time he moves to a location in that area, there’s a 50% chance that one or two city guards will appear, determined to kill him.

You’ve probably thought of similar principles.