Skip to Content

Rules

Gnosis Rules provide a powerful, data-driven system for reacting to engine events and modifying the engine’s behavior. Rules are typically defined in JSON and loaded into the Persistent.configuration.rules domain.

How Rules Work with Events

Rules do not exist in isolation; they are built to react to events. Every rule definition contains a trigger field that matches a specific Event ID.

  1. The Event Fires: A system publishes an event (e.g., "combat.deal_damage") to the GnosisEventBus.
  2. The Match: The Engine looks for all rules where trigger == "combat.deal_damage".
  3. The Evaluation: For every matching rule, the engine builds an Evaluation Context (using the event’s payload) and checks the rule’s Conditions.
  4. The Outcome: If conditions pass, the rule’s Outcomes are applied.

Example Rule Definition

Below is a simplified JSON representation of a rule that “intercepts” a combat event to increase damage:

{ "id": "iron_sword_boost", "category": "INTERCEPTOR", "trigger": "COMBAT_DEAL_DAMAGE", "conditions": [ { "path": "payload.damageType", "op": "EQUALS", "value": "PHYSICAL" } ], "outcomes": [ { "type": "MATH_ADD", "target": "amount", "value": 5 } ] }

In this case:

  • trigger: The rule “wakes up” only when the engine publishes the COMBAT_DEAL_DAMAGE event.
  • category: Being an INTERCEPTOR, it will modify the amount value inside the event payload before other systems see it.
  • conditions: It only adds the damage if the damageType is "PHYSICAL".

Note: For the rest of this document, we assume an event has been fired and a rule has been triggered. The examples will focus on how that rule then processes data and performs actions.

Rule Categories

Every rule belongs to one of three categories, which determines how its outcomes are applied:

CategoryDescriptionCommon Use Case
InterceptorDirectly modifies the event’s payload before it is dispatched to subscribers.Buffs/debuffs that change damage or cost.
ObserverListens for an event and potentially fires a new event in response.Unlocking achievements or granting bonuses.
TriggerExecutes a specific engine action by calling a registered service or function.Playing sound effects, spawning VFX, or triggering UI.

Category Examples

Interceptor: Damage Multiplier

Trigger: combat.deal_damage

{ "category": "INTERCEPTOR", "outcomes": [ { "type": "MATH_MULTIPLY", "target": "amount", "value": 1.5 } ] }

Observer: Achievement Unlock

Trigger: combat.enemy_killed

{ "category": "OBSERVER", "conditions": [ { "path": "persistent.stats.kills", "op": "EQUALS", "value": 100 } ], "outcomes": [ { "type": "FIRE_EVENT", "target": "achievement.unlock", "value": { "id": "centurion" } } ] }

Trigger: Play Sound Effect

Trigger: ui.button_click

{ "category": "TRIGGER", "outcomes": [ { "type": "CALL_SERVICE", "target": "Audio.PlaySfx", "value": { "clipId": "click_01" } } ] }

Evaluation Context

When a rule is evaluated, it has access to a context containing:

  • payload: The data associated with the triggering event.
  • ephemeral: Current session-only state.
  • persistent: Permanent state (save-data).
  • parameters: Local parameters defined within the rule itself.
  • calculations: Results from math steps defined in the rule.

Conditions

Conditions determine if a rule’s outcomes should be executed. A rule passes if all its conditions are met.

Path-Based Conditions

Each condition typically consists of a path, an operator, and a value.

OperatorDescription
EQUALSChecks if the value at the path equals the specified literal or context-bound value.
NOT_EQUALS, NEInequality check.
EXISTSChecks if the path points to a valid GnosisNode.
NOT_EXISTSChecks if the path is invalid.
CONTAINSFor strings, check for substring. For lists, check if the list contains the value.
GT, LT, GTE, LTENumeric comparisons (Greater Than, Less Than, etc.).

Random Gate

The CHANCE_PERCENT operator provides a built-in random gate. It ignores the path and uses the specified value as a percentage (0-100).

  • Example: { "op": "CHANCE_PERCENT", "value": 15 } will pass 15% of the time.

Calculations

Rules can define a list of ordered math steps in a calculations array. Each calculation’s result is stored in the context under calculations.<name> for use in conditions or outcomes.

Math Operators

Available operators for calculations:

  • + (or ADD)
  • - (or SUB)
  • * (or MUL, ×)
  • / (or DIV)
  • % (or MOD, REM)
  • RANDOM_INT (or RNG_INT, DICE): Returns a random integer between two inclusive bounds.

Example: Complex Calculation

"calculations": [ { "name": "base_damage", "fromContext": "payload.amount" }, { "name": "total_bonus", "op": "*", "args": [ { "fromContext": "persistent.player.strength" }, 0.5 ] }, { "name": "final_damage", "op": "+", "args": [ { "fromContext": "calculations.base_damage" }, { "fromContext": "calculations.total_bonus" } ] } ]

Outcomes

Outcomes are the actions taken when all conditions pass. Their behavior depends on the rule’s Category.

Interceptor Outcomes

Modify the current event payload.

  • SET: Overwrite a value at a target path.
  • MATH_ADD: Add a value to a numeric target.
  • MATH_MULTIPLY: Multiply a numeric target by a factor.

Observer Outcomes

  • FIRE_EVENT: Publishes a new event to the bus.
    • target: The new event ID.
    • value: An optional object payload.

Trigger Outcomes

  • CALL_FUNCTION (or CALL_SERVICE): Executes an engine function.
    • target: ServiceId.FunctionName (e.g., "Audio.PlaySound").
    • value: Parameters passed to the function.

Binding Values (fromContext)

Rather than hard-coding literal values, you can bind values from the evaluation context using the fromContext key.

Outcome Binding Features

  • fromContext: The path to resolve.
  • default: A literal value or another binding to use if the path is invalid.
  • scale: A numeric factor to multiply the resolved value by.
  • round: One of floor, ceil, or round (default) for integer coercion.

Example: Advanced Outcome Binding

{ "type": "SET", "target": "modifiedAmount", "value": { "fromContext": "calculations.final_damage", "scale": 1.1, "round": "ceil" } }

Rule Ordering

The execution order of rules is determined by:

  1. ExecutionOrder: An optional integer (lower values run first).
  2. Registration Order: Ties are broken by the order in which rules were added to the engine.

If you don’t specify an executionOrder, the engine automatically assigns it in increments of 10.

Last updated on