An adventure constantly remembers what has already happened: the door is open, the talk with the innkeeper is done, chapter two has begun. This information is called game state. While a project is small, it hardly matters how you store it. Once you have a few scenes, the way your states are organised decides whether you still understand your own game. This page shows how to set up states from the start so they stay clear.
How conditions and values work in general is explained on the page Conditions and Values.
State, not sequence
Actions describe what is happening right now. States describe what has already happened. Good game logic therefore never asks "did the player do this and then that earlier?", only "which state applies now?".
Example: to know whether the innkeeper recognises the player, you don't check whether the dialog has run before. At the end of the first conversation you set a condition innkeeper_knows_player to true and later only query that.
Condition or value?
| Question | Use | Example |
|---|---|---|
| Are there exactly two possibilities? | Condition | door open or closed, conversation done or not |
| Are there several steps in a row? | Value | chapter 1 to 4, a puzzle in three steps |
| Is something being counted? | Value | collected coins, failed attempts |
| Should an object appear or disappear? | Condition | An object has a "Condition" field: it is only active while that condition is true (or, with "Negate condition", while it is false). |
One value instead of many conditions
A common mistake is a chain of single conditions such as chapter1_done, chapter2_done, chapter3_done. This allows states that must never exist, for example chapter 3 done but chapter 2 not. A single value prevents that:
story_stage 0 game start 1 left the cellar 2 innkeeper gave the task 3 key found 4 finale
Write down what each number means, ideally right in a "Comment" action part or in your project notes. Queries are built with the "If value" action part, for example If value 'story_stage' >= 2. Counting up is done with "Set value".
The same applies to puzzles with several steps: a value safe_step with 0, 1, 2 and 3 is clearer than three conditions that have to match.
Combined conditions
Sometimes something depends on several states at once. You don't have to rebuild that query in every action. Set the "Condition type" of a condition to Combined condition and link other conditions with AND or OR. The result can also be negated.
Example: door_can_open is true when key_found AND guard_asleep are true. You can then use this one condition everywhere, including the "Condition" field of an object.
Where states belong
- Local states only concern one place, such as a puzzle in one scene or a single object. Create them where they are needed: in the conditions and values of that object or scene. That way you find them again when you work on that spot.
- Global states concern the whole game, such as story progress or important player decisions. Keep them in one fixed place that everyone on the team knows. Many people create a separate scene for this that is never entered in the game and only holds conditions and values.
Names you understand
- Lower case, underscores, no spaces and no special characters:
kitchen_puzzle_solvedinstead ofPuzzle solved?. - Put the place or topic first:
cellar_grate_open,cellar_light_on. Related states then sit next to each other in lists. - Phrase conditions as a statement that can be true or false:
door_open, notdoor. - Keep names unique across the project. In Lua you can then address a condition simply with
Conditions["door_open"]. If a name exists more than once, you need the full object path.
Reading and setting states in Lua
Conditions and values can also be reached from scripts. The shorthand notation comes from the reference Lua in Visionaire:
-- read and set a condition
if Conditions["door_open"].Value then
print("The door is open")
end
Conditions["door_open"].Value = true
-- read and increase a value
local stage = Values["story_stage"].Int
Values["story_stage"].Int = stage + 1
-- duplicate names: use the object path
local coins = Characters["hero"].Values["coins"].Int
Simple checks don't need a script. The action parts "If condition" and "If value" are almost always enough. Lua pays off when the same evaluation is needed in many places. Then you write it once as a function in a definition script and call it everywhere, for example with the "If lua result" action part.
Common mistakes
- Too many single conditions for a sequence that is really a series of stages. Use a value instead.
- State set in the wrong place: if a condition is set in three different actions, nobody knows later which one applies. Set a state in exactly one place whenever possible.
- Meaning only in your head: in three months you won't remember what
story_stage = 3means. Write it down. - Presentation in scripts: scripts should evaluate states. Animations, sounds and texts are better kept in the editor's actions, where you can see them.
This article builds on ideas from an earlier draft of the Visionaire scripting guide. All labels are those of the English interface of Visionaire Studio 5.