VNLEWIKIBuild stories. Connect worlds.Deutsch
VNLE Handbook

Flow rules & game actions

← Learning path and engines

These rules explain the export for your own scripts and community integrations. JSON contains data, not source code to execute. Editor layout, selection and internal screen arrangement do not control the game.

Before the first step

Check format = vnle.story, containerVersion = 1 and supported execution.contractVersion plus requiredCapabilities. The example uses 1.1 with core.v1 and substory.call-return.v1. JSON Schema checks structure; execution also requires reference, type and supported-command checks. Never silently skip an unknown node kind.

Field reference · JSON Schema

What each node means

kindBehavior
dialoguePresent text and wait for Continue; then follow continuation.
choiceFollow optionOrder, filter option.condition, apply selected effects once and follow continuation. Honor whenEmpty.
branchEvaluate condition; follow whenTrue or whenFalse.
actionEvaluate effects in order, then continuation.
jumpResolve the target entry without pushing a new return point.
callRemember continuation, then enter the target entry.
returnResume the most recently saved continuation. Returning without a Call is an error.
commandEvaluate arguments, request a game action, wait for its result and follow the matching outcome.
endFinish the entire current conversation, including when inside a substory. Return resumes the caller.

Conditions and typed values

An operand is either literal with a typed value, or variable with variableRef. compare supports eq, ne, lt, lte, gt, gte. all requires every child, any at least one, and not negates a child. Do not confuse booleans with the strings "true"/"false".

Variable effects are set, add and subtract. add/subtract use integers in −2147483648 to 2147483647. Evaluate effects in order, but do not commit a partial update when an effect is invalid. Do not overwrite saved values with initial defaultValue data on every conversation.

Jump · Go to the warehouse after buying

Jump

The harbour key was already bought: jump directly to the Warehouse entry. target contains flowRef and entryRef; execution.entries[entryRef].nodeRef gives the next node. A Jump does not automatically open an engine scene or map. That requires an appropriate game action.

{
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "2e1710dc-c32f-418f-abca-e055d6fd28f3",
  "kind": "jump",
  "target": {
    "entryRef": "a4dd48dc-b0e2-47fc-b102-def52583a8fe",
    "flowRef": "2a0a259c-b76a-4de0-8cee-567713c2d320"
  }
}
Call Substory / Return · Mira explains the warehouse

Call Substory / Return

Yes, this flow is described by JSON. After the greeting, the story calls the explanation of the locked warehouse. The game pushes continuation onto a stack and enters target. After Mira’s explanation it reaches return, pops the most recent continuation and performs “Already have the key?”. Nested calls use the same rule.

{
  "continuation": {
    "kind": "node",
    "nodeRef": "95e9a55f-f6b1-4696-9c52-6ea3ea2ac7e5"
  },
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "85b3aadf-2b95-4150-bc44-c0c796337874",
  "kind": "call",
  "target": {
    "entryRef": "2bac8555-3cae-4a21-9b8c-b410ab207ebb",
    "flowRef": "ea13b52e-092a-44ff-b087-0ce65d040c9a"
  }
}
{
  "flowRef": "ea13b52e-092a-44ff-b087-0ce65d040c9a",
  "id": "f8b8673a-cb86-40ae-a4f6-d37a5c1563c3",
  "kind": "return"
}

All involved nodes are in the story.json export. Call does not mean loading another JSON file or script. maxCallDepth limits nesting. Return on an empty stack is an error. End instead finishes the entire conversation and clears the stack.

Command · Open a shop or give a key

Command

“Open shop” is a suitable example: the game opens its own shop and pauses the conversation. Only after the shop closes does it report an agreed outcome, such as purchased or cancelled. Define these names and arguments as a game action in that VNLE project; open_shop is not a built-in VNLE command.

The real Harbour export uses give_item: the command refers to a definition, supplies the item ID and offers two outcomes. The game adds the key to its inventory or declines the transfer. The author has already defined the next path for both results.

{
  "arguments": {
    "3cac6185-4404-4a2a-be5f-14afa0f97137": {
      "kind": "literal",
      "value": {
        "entityType": "item",
        "type": "reference",
        "value": "2d71e57e-9b58-4fdb-a33f-42b89bd8bf04"
      }
    }
  },
  "commandRef": "0723beb0-daba-4602-a665-4efc27511bc3",
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "500c28a1-ed85-4c36-9a38-bedacce1c6b7",
  "kind": "command",
  "outcomes": {
    "12bc86a9-b2eb-4eb4-836c-a9efd936bf1b": {
      "continuation": {
        "kind": "node",
        "nodeRef": "382640aa-5b50-4715-9148-b470bc2f7cd6"
      },
      "resultBindings": {}
    },
    "1c527580-69a6-4e99-8503-1b0de3fc8e78": {
      "continuation": {
        "kind": "node",
        "nodeRef": "221d6b0c-99e1-45a8-bc73-768c4ee679eb"
      },
      "resultBindings": {}
    }
  }
}
{
  "id": "0723beb0-daba-4602-a665-4efc27511bc3",
  "key": "give_item",
  "name": "Give item",
  "category": "custom",
  "inputs": [
    {
      "id": "3cac6185-4404-4a2a-be5f-14afa0f97137",
      "name": "Item",
      "valueType": {
        "entityType": "item",
        "kind": "reference"
      }
    }
  ],
  "outcomes": [
    {
      "id": "12bc86a9-b2eb-4eb4-836c-a9efd936bf1b",
      "name": "Received",
      "results": []
    },
    {
      "id": "1c527580-69a6-4e99-8503-1b0de3fc8e78",
      "name": "Declined",
      "results": []
    }
  ]
}
  1. Look up commandRef in execution.commands (signature) and content.commandDefinitions (readable key).
  2. Evaluate arguments using input IDs; missing optional arguments use their declared defaultValue.
  3. Request the explicitly supported game action once. Do not allow Continue or a second execution while waiting.
  4. Validate the outcome ID and typed result values. outcome.resultBindings assigns result values to story variables; then follow outcome.continuation.
  5. A technical failure is not automatically the author’s cancellation outcome. Show the error and provide a controlled retry/cancel path in the game.

In this project, declining the key transfer leads to a refund of five gold. The exported continuation does this; it is not automatic for every Command. Inventory, quest journal and engine maps are your game’s systems; JSON catalog records alone do not create them.

Placeholders · “You have 5 gold left”

Text and placeholders

{
  "continuation": {
    "kind": "node",
    "nodeRef": "2e1710dc-c32f-418f-abca-e055d6fd28f3"
  },
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "0d7f959e-8fba-4882-ae98-b66d8416d4d2",
  "kind": "dialogue",
  "speakerRef": "807b2957-7bab-4a28-83a4-1f3d1589bbc2",
  "text": {
    "bindings": {
      "1084cf8e-515e-4b33-92f8-b5c260e88a9e": {
        "kind": "variable",
        "variableRef": "a547610e-661f-4b8d-a25b-21d042f1a2d5"
      }
    },
    "textRef": "fcc775ff-558e-4a41-823d-183aaff03e4b"
  }
}

Concatenate kind: text segments directly. For kind: placeholder, use placeholderRef to find its definition in the text record and binding in node.text.bindings. The binding provides a literal or variable value. Capture it when presenting the line and format it according to its type. Types and format options are in the field reference.

PlaceholderDefinition · Message

Progress, limits and errors

Save position, story variables, return stack and any pending game action with the game state. Language is a separate game setting. storyBuildId helps associate a save with its execution version; do not guess a continuation for an incompatible story.

maxAutomaticTransitions, maxConditionDepth, maxConditionTerms and maxCallDepth bound automatic or deeply nested flow. Missing references, no available answers with whenEmpty: fault, unknown commands and invalid types need clear errors. Redrawing the UI must never repeat purchases or variable changes.

Hand off to a developer or an AI

Provide the real export, engine version, target platform, these flow rules and the engine page. Describe your UI elements and existing inventory/shop functions. Ask for actual JSON fields and documented engine APIs, and require the cases below to be tested.

  1. Ten gold: receive key, five gold remain; two gold: no key and no deduction.
  2. Inventory declines: refund according to the story; no invented success.
  3. Call/Return resumes at the intended node; End inside a Call finishes the conversation.
  4. Language switching preserves progress; missing translations and intentionally empty text work.
  5. Shipped JSON/images/fonts work outside the development folder; missing images do not leave the old portrait visible.