Execution Order

If a response has an item:...:true requirement (consume on use), items are removed before actions execute. Actions themselves run in the order they appear in the list. A page:pageId action navigates immediately — place it last in the list.

Auto-Triggered Pages

Every page has an optional AutoTrigger boolean field (default false). When set to true, QuestLines checks the page's requirements on every HUD tick for all online players. As soon as requirements pass, the page's LoadActions are executed automatically — no NPC interaction required. This is useful for world events, passive rewards, or background state changes.

Prevent Re-Firing Every Tick

An auto-triggered page fires every tick while its requirements pass. Always pair it with a notTag:someflag requirement and an addTag:someflag load action so it fires only once per player.

JSON field

{
  "Id":           "auto_welcome",
  "AutoTrigger": true,
  "Requirements": ["questNotStarted:intro_quest", "notTag:auto_welcome_fired"],
  "LoadActions":  ["addTag:auto_welcome_fired", "questStarted:intro_quest"],
  "Name":         "System",
  "Dialog":      "Welcome, adventurer!"
}
Tick Rate

Auto-trigger checks run at the same interval as the Quest Goal HUD refresh (configurable via hud_refresh_rate in QuestLines.conf, default 500 ms). The check is independent of quest tracking — it starts when the player connects and stops when they disconnect.

Commands

Runs a server command either from the console or as the interacting player. This allows bridging QuestLines with any other plugin that supports commands.

FormatDescription
command:commandText:server Runs commandText as the server console (op-level permissions).
command:commandText:player Runs commandText as the interacting player (player's permission level).
command:commandText:player:elevated Runs commandText as the player with a transient wildcard permission grant. Requires VaultUnlocked; falls back to normal player execution if unavailable.
No Colons in Command Text

The command text is parsed by splitting on :. If your command contains a colon (e.g. a plugin command like myplugin:subcmd), the parser will break. Use an alias or a wrapper command instead.

Example

// Teleport the player via console command
"Actions": ["command:tp Steve 100 64 -200:server"]

// Player runs a command (e.g. to join a minigame)
"Actions": ["command:joinqueue arena:player"]

Cooldown & Timer

FormatDescription
setTimestamp:key Records the current server time under key in the player's timestamps. Used with the cooldown:key:seconds requirement.
timedStart:key Records the current server time under key. Use with timedActive:key:seconds / timedExpired:key:seconds.
timedStart:key:durationSeconds Same as above, but also stores the duration so the requirements can be written as just timedActive:key / timedExpired:key (without repeating the seconds). The stored duration is used when no seconds argument is given to those requirements.
setTimestamp vs timedStart

Both actions write to the same timestamps map. The names differ only for readability: use setTimestamp for cooldown gates (reward timers, NPC chat limits) and timedStart for countdown timers (timed quests, races). Do not reuse the same key for both purposes.

Example

// Grant daily reward and reset the cooldown clock
"Actions": [
  "item:daily_chest:1",
  "setTimestamp:daily_reward"
]

// Start a 5-minute countdown timer
"Actions": [
  "questStarted:courier_run",
  "timedStart:courier_timer"
]

Dialogue Navigation

Immediately navigates the player to a different dialogue page without closing the UI. This enables branching conversations, sub-menus, and multi-step dialogue trees that don't require NPC re-interaction.

FormatDescription
page:pageId Navigates the player's open dialogue to the specified page. Place this last in the actions list.
💡
Use for Branching Dialogue

Hub pages with no quest logic can use page:pageId exclusively to route players to topic-specific sub-pages. The sub-pages can themselves have actions or further navigation. See Quest Patterns → Branching Dialogue.

Example

// Hub page response routes to a lore sub-page
"Responses": [{ "Text": "Tell me about the ancient war.", "Actions": ["page:elder_lore_war"] }]

Economy (VaultUnlocked optional dependency)

Give or take currency from the player's economy account. Requires VaultUnlocked with a compatible economy provider. Actions are silently ignored if VaultUnlocked is not installed.

FormatDescription
economy:give:amount Deposits amount into the player's economy account.
economy:take:amount Withdraws amount from the player's economy account. Use economy:canafford:amount as a requirement to guard this.
💡
Always Guard economy:take with a Requirement

Pair economy:take with an economy:canafford:amount requirement on the same response. This prevents the action from firing if the player's balance is insufficient.

Example

// Quest reward — deposit gold into the player's account
"Actions": [
  "questCompleted:merchant_errand",
  "economy:give:250"
]

// Purchase response — require funds, then deduct them
"Responses": [{
  "Text":         "I'll buy the map for 100 gold.",
  "Requirements": ["economy:canafford:100"],
  "Actions":      ["economy:take:100", "item:treasure_map:1"]
}]

Global State (server-wide)

Manage server-wide tags and variables stored in GlobalData. Unlike per-player tags and variables, these affect and are visible to all players. Changes are persisted to QuestLines/global.json immediately.

FormatDescription
global:addtag:tagName Adds tagName to the server-wide global tag list. No effect if already present.
global:removetag:tagName Removes tagName from the server-wide global tag list.
global:variable:name:set:value Sets the global variable to value exactly.
global:variable:name:add:value Adds value to the global variable.
global:variable:name:subtract:value Subtracts value from the global variable.
global:variable:name:multiply:value Multiplies the global variable by value.
global:variable:name:divide:value Divides the global variable by value. Division by zero is ignored.

Example

// Start a world event by setting a global tag
"Actions": ["global:addtag:world_event_active"]

// Increment a server-wide boss kill counter
"Actions": ["global:variable:boss_kills:add:1"]

Items

FormatDescription
item:itemId:qty Gives qty of the specified item to the player and shows an item-received notification.

Example

// Reward the player for completing a quest
"Actions": [
  "questCompleted:bounty_hunter",
  "item:gold_coin:50",
  "item:health_potion:3"
]

Journal Log Tag

QuestLines automatically maintains a log:questId:pageId tag for every quest a player has interacted with. It records the last dialogue page the player actually saw for that quest and is the data source that powers the Quest Journal (/journal).

You never write this tag yourself. The engine writes it in two places:

  • When a player opens NPC dialogue — the resolved page is logged immediately.
  • When a page:pageId response navigates to a new page — the target page is logged before its load actions run.

At most one log:questId:* tag exists per quest per player at any time; the old entry is replaced automatically. The tag persists after quest completion so the Journal can show the final dialogue for completed quests.

No Action Required

Do not add addTag:log:… to your LoadActions or response Actions. The engine handles this automatically. If you see a stale log: tag in player data during debugging, it is always safe to remove it manually via /ql player <name> in the admin UI.

Load Actions

In addition to response Actions, every Page has a LoadActions field — a list of action strings that run automatically whenever that page is displayed. This happens both on the initial NPC interaction and whenever a page:pageId response navigates to the page. Load actions fire before the player sees any response buttons and regardless of which button they eventually click.

Use LoadActions for side-effects that should happen once per page view, not once per response click — for example, setting a tag, recording a timestamp, or running a server command that logs the visit.

Old Configs

LoadActions defaults to an empty list if omitted. Existing page configs that predate this field work without any changes.

JSON field

{
  "Id":           "my_quest_hub",
  "Requirements": ["questStarted:my_quest"],
  "LoadActions":  ["setTimestamp:last_visit_hub"],
  "Dialog":      "Welcome back, {username}.",
  "Responses": [{ "..." }]
}
💡
Supported Actions

Any action string that works in a response Actions list also works in LoadActions. The same execution rules apply: actions run in order, and page:pageId navigation is supported but should be placed last.

Goal Text

Every Page has an optional GoalText string. When a player is tracking a quest, the Quest Goal HUD finds their current page and displays this text. Supports all TextFormatter variables and inline formatting tags.

Superseded by Objectives — if the page has a non-empty Objectives list, GoalText is ignored in the HUD and journal. Old configs that only use GoalText continue to work without any changes.

JSON field

{
  "Id":        "collect_wood",
  "GoalText": "Collect 10 oak logs from the forest.",
  "Dialog":   "Come back when you have the wood."
}

Objectives

Every Page has an optional Objectives list — an array of requirement strings (same format as Requirements). When non-empty, the Quest Goal HUD and Journal replace the static GoalText display with an auto-generated pass/fail objectives list.

Each objective is evaluated live and rendered as a labelled line with a progress counter where applicable:

// HUD / journal output
✓ Kill zombie: 5/5
· Break oak_log: 2/10
· Have iron_sword x1

The pass indicator is (green) when the requirement passes and · (grey) when it does not. Requirements that have a tracked progress counter (kills, breaks, places, talk, travel, fish, etc.) show current/required alongside the label. Requirements with no counter (e.g. item:, questCompleted:) show only the label.

Contextual Requirements Skipped

Contextual requirements (e.g. npcname:) are skipped during objective evaluation because they depend on the active NPC interaction, which is not available in the HUD or journal context.

Backwards Compatibility

Objectives defaults to an empty list if omitted. Pages that only define GoalText continue to work without any changes — the two fields are mutually exclusive at render time, not in the data model.

JSON field

{
  "Id":           "collect_wood",
  "Requirements": ["questStarted:lumber_run"],
  "Objectives":   [
    "break:oak_log:10",
    "item:iron_axe:1"
  ],
  "Dialog":      "Come back when you have the wood."
}
💡
Objectives vs Requirements

Objectives are purely display — they do not gate the page. If you also want to block navigation until the objectives are complete, add the same strings to the page Requirements or to the relevant response's Requirements.

MMO Skill XP (MMOSkillTree optional dependency)

Gives XP to a player's MMO skill. Requires the MMOSkillTree plugin to be active; silently ignored if not installed. Skill IDs are case-insensitive.

FormatDescription
mmo:xp:skillId:give:amount Awards amount XP to the player in the specified skill (e.g. MINING, CRAFTING).

Example

// Give 500 Mining XP as a quest reward
"Actions": [
  "questCompleted:miners_task",
  "mmo:xp:MINING:give:500"
]

Named Variables

Named variables are numeric values stored per-player. They can be used to track arbitrary numeric state — scores, choices, counters, or anything that doesn't fit a kill/break counter. Variables default to 0 if never set. Fractional values are supported.

FormatDescription
variable:name:set:value Sets the variable to value exactly. Use this to initialise a variable at quest start.
variable:name:add:value Adds value to the variable.
variable:name:subtract:value Subtracts value from the variable.
variable:name:multiply:value Multiplies the variable by value.
variable:name:divide:value Divides the variable by value. Division by zero is ignored.
Initialise Before Use

Always use variable:name:set:0 (or another initial value) in your quest-start actions before any add, subtract, or multiply operations. While variables default to 0, being explicit avoids surprises if a player repeats a quest.

💡
Variables in Action Strings

{variable:name} substitution works inside action strings, not just dialog text. This lets named variables drive action parameters at runtime.

// Spawn count and delay are taken from named variables
"spawn:Zombie:~0:~0:~0:{variable:delay}:{variable:amount}"

Example

// Quest start — initialise a score variable
"Actions": [
  "questStarted:my_quest",
  "variable:score:set:0"
]

// Response that increments the counter
"Actions": ["variable:score:add:1"]

// Response that doubles it
"Actions": ["variable:score:multiply:2"]

Use {variable:name} in dialog text to show the current value to the player. Use variable:name:comparison:value requirements to gate pages or responses on the variable's value. See Requirements → Named Variables and Text Variables.

NPC Spawning

Spawn NPCs in the world as a result of player actions — useful for scripted events, quest escorts, ambushes, and environmental storytelling. Spawned NPCs are not persisted to disk; they exist only until the server restarts or they are killed.

FormatDescription
spawnCitizen:citizenName:x:y:z Spawns a visual clone of the HyCitizens NPC with that display name at the given world coordinates. Copies model, skin, scale, and appearance. Spawns in the player's current world.
spawnCitizen:citizenName:x:y:z:delaySeconds Same as above, but waits delaySeconds (integer) before spawning. Useful for dramatic timing.
spawnCitizen:citizenName:x:y:z:delaySeconds:quantity Spawns quantity clones simultaneously (minimum 1). Combine with a delay as needed.
spawn:npcRoleId:x:y:z Spawns a standard Hytale NPC by role ID at the given coordinates. Default rotation (0, 0, 0).
spawn:npcRoleId:x:y:z:delaySeconds Same as above with an optional spawn delay in seconds.
spawn:npcRoleId:x:y:z:delaySeconds:quantity Spawns quantity NPCs simultaneously. Combine with a delay as needed.
spawnCitizen Lookup

spawnCitizen matches by the display name of an existing HyCitizens NPC. If multiple NPCs share the same display name, the first match is used. The spawned copy has no quest linkage — it is purely visual.

💡
Relative Coordinates

Coordinates for both spawnCitizen and spawn support a ~ prefix to specify a position relative to the player. For example, ~5 means 5 units ahead on that axis, and ~ or ~0 means exactly at the player's position on that axis.

// Spawn 3 units in front of and 1 unit above the player
"spawnCitizen:Bandit Guard:~3:~1:~0"

Example

// Spawn a bandit immediately at a fixed position
"Actions": [
  "questStarted:ambush",
  "spawnCitizen:Bandit Guard:120:64:-340"
]

// Spawn after a 3-second dramatic pause
"Actions": [
  "questStarted:ambush",
  "track:bandit_kills:killcitizen:Bandit Guard",
  "spawnCitizen:Bandit Guard:120:64:-340:3"
]

// Spawn a standard Hytale NPC by role
"Actions": ["spawn:goblin_warrior:200:70:100"]

OrbisGuard Regions (OrbisGuard optional dependency)

Add or remove the interacting player as a member of an OrbisGuard region. Requires OrbisGuard to be active; silently ignored if not installed.

FormatDescription
regionaddmember:regionId Adds the player as a member of the named OrbisGuard region.
regionremovemember:regionId Removes the player from the named OrbisGuard region's member list.

Example

// Grant access to the guild hall region on quest complete
"Actions": [
  "questCompleted:guild_initiation",
  "regionaddmember:guild_hall"
]

Player Tags

Tags are free-form string labels stored on a player's data. They can represent any custom boolean state — visited locations, made choices, unlocked content, etc. Adding a tag that already exists is a no-op (idempotent).

FormatDescription
addTag:tagName Adds the tag to the player's tag list. No effect if already present.
removeTag:tagName Removes the tag. If the tag is a tracking flag, also resets the associated counter (see Tracking Flags below).

Example

// Record that the player chose a path in branching dialogue
"Actions": ["addTag:chose_merchant_path"]

// Remove a tag when a condition is no longer relevant
"Actions": ["removeTag:pending_escort"]

Quest State

FormatDescription
questStarted:questId Adds the quest to the player's started quests list and displays a "Quest Started" notification on screen.
questCompleted:questId Adds the quest to completed quests, removes it from started quests, and shows a "Quest Completed" notification.
questRemoved:questId Removes the quest from both started and completed lists. Useful for resettable or repeatable quests.

Example

// Accept the quest and begin tracking kills (Goblin* matches Goblin_Duke, Goblin_Miner, etc.)
"Actions": [
  "questStarted:lost_sword",
  "track:goblin_kills:kill:Goblin*"
]

// Complete the quest and tear down tracking
"Actions": [
  "questCompleted:lost_sword",
  "untrack:goblin_kills"
]

Random

Picks one action at random from a bracket-delimited comma-separated list and executes it. Each option in the list is an action string. Only one option is chosen and run per execution. The selection is uniformly random.

FormatDescription
random:[action1,action2,action3] Picks one of the listed actions at random and executes it. Enclose the list in [ and ], comma-separated.
page: not supported inside random

The page:pageId navigation action cannot be used inside a random: bracket list. Use separate responses with different page actions if you need random navigation.

Example

// Give one of three random item rewards
"Actions": [
  "questCompleted:loot_chest",
  "random:[item:gold_coin:10,item:health_potion:3,item:rare_gem:1]"
]

RPG Leveling XP (RPGLeveling optional dependency)

Gives XP to a player's global RPG level. Requires the RPGLeveling plugin to be active; silently ignored if not installed.

FormatDescription
rpgxp:give:amount Awards amount XP to the player's global RPG level.

Example

// Give 200 RPG XP as a quest reward
"Actions": [
  "questCompleted:training_quest",
  "rpgxp:give:200"
]

Sound

Plays a 2D sound event to the interacting player. Use /sound in-game to browse all available sound event names. The optional :true flag broadcasts the sound to all online players instead of just the interacting player.

FormatDescription
sound:soundName Plays the named sound event to the interacting player only.
sound:soundName:true Plays the named sound event to all online players.

Example

// Play a quest-complete fanfare for the player
"Actions": [
  "questCompleted:lost_sword",
  "sound:ui.quest_complete"
]

// Announce a world event to all players
"Actions": ["sound:world.boss_roar:true"]

Named Trackers

Named trackers are the preferred way to set up progress counting. Instead of adding a tag, writing a separate requirement, and using a separate variable, a single ID ties everything together. Old tracking flags continue to work.

FormatDescription
track:id:type:[target1,target2] Creates (or resets) a named tracker. id is a unique name you choose. type is one of: kill, killcitizen, block, place, playerkill, travel, craft, harvest, pickup, catchfish, catchfishbiome, catchfishrarity, catchfishzone, releasefish. Targets use a comma-separated bracket list or a bare single value; * matches any.
track:id:type:[targets]:exclude:[excl1,excl2] Same as above with an exclusion filter. Events whose value matches an exclude pattern are ignored even if they matched a target.
track:[id1,id2]:type:[targets] Bracket list on the ID creates multiple trackers with the same config in one action.
untrack:id / untrack:[id1,id2] Removes the named tracker(s) and resets the count to zero.

Example — kill 10 undead:

// Setup (on quest-start response)
"track:undead:kill:[zombie,skeleton,phantom]"

// Requirement (on completion page)
"track:undead:10"

// Variable in dialogue
"You have slain {track:undead} undead so far."

// Objective entry
"track:undead:10::Kill Undead"

// Reset (on quest-complete response)
"untrack:undead"

Quick Reference

FormatCategoryDescription
questStarted:idQuest StateAdd to started quests + notification
questCompleted:idQuest StateMove to completed + notification
questRemoved:idQuest StateRemove from both lists
addTag:tagNameTagsAdd a tag (idempotent)
removeTag:tagNameTagsRemove a tag
track:id:type:[targets]TrackingStart a named tracker (see Named Trackers section)
track:id:type:target:loc:x:y:z:radiusTrackingStart a location-gated named tracker
track:id:type:target:world:worldNameTrackingStart a world-gated named tracker
untrack:idTrackingRemove named tracker and reset its count to zero
untrack:[id1,id2]TrackingRemove multiple named trackers at once
setTimestamp:keyCooldownRecord time for cooldown gate
timedStart:keyTimerRecord time for countdown timer
timedStart:key:durationSecondsTimerRecord time + store duration (allows timedActive/timedExpired without repeating seconds)
item:itemId:qtyItemsGive item + notification
command:text:serverCommandsRun as server console
command:text:player[:elevated]CommandsRun as player (add :elevated to grant temp wildcard perm)
page:pageIdNavigationNavigate to another page
spawnCitizen:name:x:y:z[:delay[:qty]]SpawningClone HyCitizens NPC at position (coords may use ~)
spawn:npcRoleId:x:y:z[:delay[:qty]]SpawningSpawn Hytale NPC by role ID (coords may use ~)
variable:name:set:valueVariableSet named variable to value
variable:name:add:valueVariableAdd to named variable
variable:name:subtract:valueVariableSubtract from named variable
variable:name:multiply:valueVariableMultiply named variable
variable:name:divide:valueVariableDivide named variable
economy:give:amountEconomyDeposit amount into player account (requires VaultUnlocked)
economy:take:amountEconomyWithdraw amount from player account (requires VaultUnlocked)
sound:soundName[:true]SoundPlay sound to player (:true = broadcast to all)
mmo:xp:skillId:give:amountMMO SkillGive skill XP to player (requires MMOSkillTree)
rpgxp:give:amountRPG LevelingGive RPG XP to player (requires RPGLeveling)
regionaddmember:regionIdOrbisGuardAdd player as member of OrbisGuard region (requires OrbisGuard)
regionremovemember:regionIdOrbisGuardRemove player from OrbisGuard region's member list (requires OrbisGuard)
global:addtag:tagNameGlobal StateAdd tag to server-wide global tag list
global:removetag:tagNameGlobal StateRemove tag from server-wide global tag list
global:variable:name:op:valueGlobal StateMutate server-wide variable (ops: add, subtract, multiply, divide, set)
random:[action1,action2,...]RandomPick one action at random from bracket list (page: not supported inside)
The actions above work in both Response.Actions and Page.LoadActions. Load actions fire automatically when the page is shown, before the player clicks any button.
log:questId:pageIdSystem (auto)Last-seen page tag — written automatically by the engine; powers the Quest Journal. Do not write manually.