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 and no dialogue UI is shown.

Typical use cases:

  • Auto-start intro quests — trigger a quest start the first time a new player connects.
  • Passive milestones — detect when a player reaches a kill count or level and award a bonus quest.
  • World-event gates — fire effects when a global:hastag world flag is set.
  • Location-based triggers — combine with nearPosition or zone requirements to fire when a player enters an area.
Prevent Re-Firing Every Tick

An auto-triggered page fires every tick while its requirements pass. You must prevent this from looping. The standard pattern is to add a !hastag:someflag (or notTag:someflag) requirement and an addTag:someflag load action so it fires exactly once per player. You can also use setTimestamp: + cooldown: for time-gated repeatable triggers.

Example — auto-start an intro quest on first login

{
  "Id":           "auto_welcome",
  "AutoTrigger": true,
  "Requirements": ["questNotStarted:intro_quest", "!hastag:intro_fired"],
  "LoadActions":  ["addTag:intro_fired", "questStarted:intro_quest", "chat:Welcome to the server, {username}!"]
}

Example — location-based trigger (fires once when player enters area)

{
  "Id":           "dungeon_entrance_trigger",
  "AutoTrigger": true,
  "Requirements": [
    "questStarted:dungeon_quest",
    "nearPosition:450:64:-210:20",
    "!hastag:dungeon_entered"
  ],
  "LoadActions": [
    "addTag:dungeon_entered",
    "setMarker:dungeon_exit:Exit:460:64:-250:Portal",
    "sound:world.dungeon_ambience"
  ]
}
Tick Rate & Scope

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 runs independently for every online player — it starts when a player connects and stops when they disconnect. Auto-trigger pages do not open a dialogue UI and are never reachable via NPC interaction.

Achievements

Drive achievement state directly from quests or response actions. See the Achievements section in the Core overview for how achievements unlock automatically when their Criteria pass — these actions are for explicit grant/revoke and for opening the UI.

FormatDescription
unlockachievement:id Force-unlock the next still-locked stage of the named achievement, firing that stage’s UnlockActions. No-op when every stage is already unlocked. Unknown IDs log a warning.
unlockachievement:id:tier Force-unlock the specific 1-based stage tier (and any earlier locked stages it cascades through). Each granted stage’s UnlockActions fire in order.
revokeachievement:id Remove every stage of the named achievement from the player and clear its unlock timestamps. Destructive; intended for admin / scripted use only.
openachievements Open the player-facing Achievements UI. Equivalent to the player running /achievements; useful as a response action that pairs with a NPC who awards milestones.

Example

// Award a milestone tier on quest completion and pop the UI to show it
"Actions": [
  "questCompleted:dragon_slayer",
  "unlockachievement:legendary_hunter:2",
  "openachievements"
]

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"]

Chat

Sends a plain chat message directly to the interacting player. Supports all TextFormatter variables (e.g. {username}, {variable:name}). Everything after chat: is the message — colons in the message text are allowed. message: is an alias and behaves identically.

FormatDescription
chat:message text here Sends the message to the triggering player's chat. Colons in the message are preserved.
message:message text here Alias of chat: — identical behavior, same parsing and variable substitution.

Example

// Simple confirmation message
"Actions": ["chat:Your quest has been recorded, {username}."]

// Alias form — identical effect
"Actions": ["message:Welcome back, {username}!"]

// Used in an if: branch to inform the player of a condition
"Actions": [
  "if:hasTag:quest_started::chat:You already have this quest!::startquest:my_quest"
]

Cooldown & Timer

FormatDescription
setTimestamp:key Records the current server time under key in the player's timestamps. Used with the cooldown:key:seconds requirement.
clearTimestamp:key Removes the stored timestamp under key. Resets any cooldown:, timedActive:, timedExpired:, or timedExact: gate that reads it.
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": [
  "startquest: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": [
  "completequest: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": [
  "completequest:bounty_hunter",
  "item:gold_coin:50",
  "item:health_potion:3"
]

If Action

Executes actions conditionally based on whether a set of requirements passes. Uses :: as a delimiter between the requirements, the then-branch actions, and an optional else-branch. Each section is a comma-separated list. Brackets [] around lists are accepted but optional.

FormatDescription
if:req::thenAction Runs thenAction when the requirement passes. No else branch.
if:req::thenAction::elseAction Runs thenAction when the requirement passes; elseAction when it fails.
if:req1,req2::[thenA,thenB]::[elseA,elseB] All requirements must pass (AND logic). Brackets optional around comma-separated action lists.
page: not supported inside if:

The page:pageId navigation action cannot be used in the then or else branches of an if: action.

Example

// Flag the player only if they haven't been flagged before
"Actions": ["if:notTag:visited::addTag:visited"]

// Grant a bonus quest if a prerequisite is complete, otherwise inform the player
"Actions": [
  "if:questCompleted:main_quest::startquest:bonus_quest::chat:Complete the main quest first!"
]

// Multiple requirements and multiple actions in each branch
"Actions": [
  "if:hasTag:vip,questCompleted:tutorial::[startquest:vip_quest,chat:Welcome VIP!]::[chat:You are not eligible yet.]"
]

Quest Items

Quest items live in the player's virtual quest inventory — a separate storage tracked by QuestLines, not the player's real item pack. They cannot be dropped, lost, or consumed by normal item: requirements. Use questitem:itemId:qty in Requirements to check them, and removequestitem: to take them back. Quest items are visible to the player in the Quest Items tab of the journal.

FormatDescription
givequestitem:itemId:qty Adds qty of the item to the player's virtual quest inventory. The item is not placed in the real pack.
removequestitem:itemId:qty Removes up to qty of the item from the player's virtual quest inventory. Has no effect on the real pack.

Example

// Give the player a quest item on quest start
"Actions": [
  "startquest:artifact_hunt",
  "givequestitem:Ancient_Medallion:1"
]

// Turn in — require the item in Requirements, then remove it in Actions
"Requirements": ["questitem:Ancient_Medallion:1"]
"Actions": [
  "removequestitem:Ancient_Medallion:1",
  "completequest:artifact_hunt"
]

Journal Page Resolution

The Quest Journal (/journal) resolves the current page for each quest the player has started or completed using live page resolution — the same algorithm the HUD uses. The quest's pages are walked in order and the first page whose requirements pass is the one shown in the journal.

No tags or actions are needed to drive the journal. Older versions of QuestLines maintained a hidden log:questId:pageId tag for this; that mechanic was removed in 1.9.4. Any leftover log: tags in player data are harmless and ignored at runtime.

JournalText on a page overrides the Dialog field for the journal entry only — useful when the in-world dialogue is spoken by an NPC but you want the journal written from the player's perspective.

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.

Objectives

Page Objectives drive the live HUD and Journal goal display. They are built from requirement strings (same format as the Requirements list) plus a special text: prefix for plain-text lines, and they support a ::label override for friendly labels.

Full reference, including the text: special case, the ::label override, contextual-requirement handling, and a complete JSON example, is on the Objectives page.

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": [
  "completequest: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": [
  "startquest: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": [
  "startquest:ambush",
  "spawnCitizen:Bandit Guard:120:64:-340"
]

// Spawn after a 3-second dramatic pause
"Actions": [
  "startquest: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"]

Teleport

Move the interacting player to a fixed or player-relative location, optionally in a different loaded world. Body and head rotation are preserved. The action is a no-op if the named world is not loaded; coordinates outside the loaded chunks are still accepted (the engine streams the destination on arrival).

FormatDescription
tp:x:y:z Teleport the player to the given coordinates in their current world. Coordinates support ~ prefix for player-relative values (e.g. ~5 = 5 units ahead on that axis).
tp:x:y:z:world Teleport to a named world (case-insensitive, must be loaded). Omit the segment to stay in the current world.

Example

// Drop the player into a hidden chamber on quest start
"Actions": [
  "questStarted:hidden_temple",
  "tp:120:64:-340"
]

// Cross-world teleport using a player-relative Y offset
"Actions": [
  "tp:0:~10:0:nether_realm"
]

Regions (QuestLines Claims or OrbisGuard)

Add or remove the interacting player as a member of a named region. QuestLines Claims is checked first when installed; OrbisGuard is used as a fallback for any region ID Claims does not recognise. When adding via QL Claims, the member is granted a standard trusted flag set (build, break, interact, container, door, entry, sit, portal). Silently ignored if neither plugin recognises the region.

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

Example

// Grant access to the guild hall region on quest complete
"Actions": [
  "completequest: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
startquest:questId Adds the quest to the player's started quests list and displays a "Quest Started" notification on screen.
completequest:questId Adds the quest to completed quests, removes it from started quests, and shows a "Quest Completed" notification.
removequest: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": [
  "startquest:lost_sword",
  "track:goblin_kills:kill:Goblin*"
]

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

Icon State (QuestLines Icons optional dependency)

Sets a per-player icon-state override on a specific NPC, bypassing the auto-computed quest state for that player only. Useful for narrative beats — e.g. force the unknown icon on a fallen NPC the player just discovered, or hold an NPC at objectives_complete after a scripted scene. Pairs with the matching iconState: requirement (see Requirements » Icon State).

Only registered when the QuestLines Icons companion plugin is active — the action silently no-ops on installs without Icons. See the Icons wiki » Per-Player Overrides for the full state list and resolution priority.

FormatDescription
iconState:npcId:state Set the player’s icon-state override for npcId. state must be one of available, inprogress, objectives_complete, completed, locked, waiting, repeatable_ready, story, custom, unknown. Invalid state names silently no-op. story and custom are override-only states (not auto-resolved) intended for main-quest beats and admin-defined visuals.
iconState:npcId:state:clear Guarded clear — removes the override only if it currently equals state. Prevents one quest’s cleanup from stomping another quest’s override.

Example

// Force the smith's icon to the "turn in" cue while a follow-up
// scene plays out on a different NPC.
"Actions": ["iconState:smith_id:objectives_complete"]

// On turn-in, only clear if our quest set it -- another quest may have
// switched the icon to its own override since.
"Actions": [
  "completequest:trial",
  "iconState:smith_id:objectives_complete:clear"
]

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": [
  "completequest: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": [
  "completequest:training_quest",
  "rpgxp:give:200"
]

Endless Leveling XP (EndlessLeveling optional dependency)

Grants XP to a player's Endless Leveling level. Requires the EndlessLeveling plugin to be active; silently ignored if not installed. Respects the plugin's own bonuses (Discipline multiplier etc.), world XP blacklists, and the player's level cap.

FormatDescription
elxp:give:amount Grants amount EL XP to the player.

Example

// Give 1500 EL XP on dungeon completion
"Actions": [
  "completequest:dungeon_raid",
  "elxp:give:1500"
]

Wave Arena

Triggers Core-driven wave arenas from quest actions. Arenas are authored via /ql waveeditor and stored under QuestLines/wavearenas/<id>.json. No optional dependency. Pair with track:id:arena_complete:[arenaId] or track:id:arena_wave:[arenaId] to count arena progress for quests. Rewards are configured per-arena via CompletionActions / FailActions (core action strings; see the Wave Arenas page).

FormatDescription
startArena:arenaId Starts the named wave arena centered on the player's current position.
startArena:arenaId:x:y:z Starts the named wave arena at the given coordinates. Coordinates support ~ for player-relative values.
failArena Aborts the player's current arena session (fail reason MANUAL). Runs the arena's FailActions. No-op if the player is not in an arena.

Example

// Start the Shadow Trial arena on quest accept; track 3 clears to complete
"Actions": [
  "track:shadow_clears:3:arena_complete:[shadow_trial]",
  "startArena:shadow_trial"
]

Books and Papers (Books and Papers optional dependency)

Integrates with the Books and Papers plugin for delivering items to player mailboxes and giving custom signed books or letters. Both actions are no-ops when Books and Papers is not installed. Pair with the hasmail requirement to gate dialogue on whether the player has unread mail.

FormatDescription
mail:itemId:qty Pushes the given item stack into the player's mailbox in the world they are currently in. The player collects the item by interacting with their mailbox block.
givebook:title:author:body Gives the player a signed Books_And_Papers_Book with the supplied title, author, and pages. Use \f in body to break pages and \n for line breaks within a page. title and author cannot contain a literal colon; body captures everything after the third colon, so colons inside page text are preserved.
givebook:itemId:title:author:body Same as above with an explicit item id (must start with Books_And_Papers_). Use Books_And_Papers_Letter for a single-page letter.

Example

// Mail a stack of gold coins to the player on quest completion
"Actions": [
  "questCompleted:lost_letter",
  "mail:Items_Coin_Gold:100"
]

// Hand the player a signed two-page book
"Actions": [
  "givebook:The Sealed Tome:Master Aldwin:Chapter One...\fChapter Two..."
]

Sound

Plays a sound event. Use /sound in-game to browse all available sound event names. Supports 2D (player-relative) and 3D (world-positioned) playback. Coordinates support ~ prefix for player-relative values.

FormatDescription
sound:soundName Plays the named sound event to the interacting player only (2D).
sound:soundName:true Plays the named sound event to all online players (2D broadcast).
sound:soundName:x:y:z Plays a 3D positional sound at the given world coordinates. Broadcasts to all nearby players within the sound's natural max distance. Coordinates support ~ prefix (e.g. ~0:~5:~0).
sound:soundName:x:y:z:player Same as above but only plays to the triggering player (still distance-checked from the specified position).

Example

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

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

Title

Shows a large event title on screen. Supports all TextFormatter variables in both the primary and secondary text.

FormatDescription
title:primary Shows the primary message to the triggering player.
title:primary:secondary Primary message with a subtitle below it.
title:primary:secondary:server Broadcasts to all players in the world instead of just the triggering player.
title:primary:secondary:player Explicit player-only (default). Same as omitting the third segment.
title:primary:secondary:server:true Server broadcast with major (large) title style.

Example

// Show a dramatic boss-intro title to all players
"Actions": [
  "title:The Lich Awakens:Prepare for battle:server:true"
]

// Personal quest-complete message
"Actions": [
  "completequest:main_quest",
  "title:Quest Complete!:Well done, {username}"
]

World Map Markers

Place and remove markers on a player's world map. Markers persist across sessions until explicitly removed. Sending the same id again replaces the existing marker.

FormatDescription
setMarker:id:label:x:y:z Place a map marker. id uniquely identifies this marker (used for updates/removal). label is the display text. x:y:z are world coordinates (support ~ relative prefix).
setMarker:id:label:x:y:z:image Same with a custom icon. image is the marker image name — .png is appended automatically. Marker images live in Common/UI/WorldMap/MapMarkers/.
setMarker:id:label:x:y:z:image:RRGGBB Same with a hex tint color applied to the icon (e.g. FF0000 for red).
removeMarker:id Remove the marker with the given id. The marker clears from the client on reconnect.

Per-player block visibility

FormatDescription
hideBlock:x:y:z[:world] Hide a single block from this player only — they see Air where the rest of the server still sees the real block. World defaults to the player's current world. Coordinates support ~-relative syntax.
hideBlock:x1:y1:z1:x2:y2:z2[:world] Hide an inclusive 3D box. Coordinates may be supplied in any order — they are normalised to min/max on insert. Volume is capped via block_override_max_region_volume in QuestLines.conf (default 32768).
showBlock:x:y:z[:world]
showBlock:x1:y1:z1:x2:y2:z2[:world]
Clear a previously stored override and re-send the real block(s). The coordinate set must match the originally hidden region (any ordering); partial overlaps are not removed.
Why a periodic resync is needed

The Hytale plugin API does not expose a per-player chunk-load event, so every time the client reloads a chunk (typical as the player moves around) the server re-sends the real block data and our override is briefly visible. QuestLines re-applies overrides on login and on a configurable periodic tick (default every 5 seconds, controlled by block_override_tick_seconds) to compensate. Lower the interval for tighter sync at the cost of more packets; set to 0 to disable the tick entirely.

Example

// Hide a wall of blocks until the player completes the quest
"Actions": [
  "questStarted:secret_passage",
  "hideBlock:120:64:-30:120:67:-30"
]

// Reveal the wall again on completion
"Actions": [
  "questCompleted:secret_passage",
  "showBlock:120:64:-30:120:67:-30"
]
Built-in marker icons

Default icons available out of the box: Home, Spawn, Portal, Warp, Death, Campfire, Coordinate, Temple_Gateway, PortalInvasion, UserAUserF. Add custom icons to Common/UI/WorldMap/MapMarkers/ in your resource pack.

Example

// Mark an objective location when a quest starts
"Actions": [
  "questStarted:lost_sword",
  "setMarker:goblin_cave:Goblin Cave:450:64:-210:Coordinate:FF6600"
]

// Remove the marker on quest completion
"Actions": [
  "questCompleted:lost_sword",
  "removeMarker:goblin_cave"
]

Macro

Executes a named macro defined in QuestLines/macros/. Each macro is stored as its own <name>.json file. Action macros reuse common action sequences; requirement macros group reusable conditions. Circular references are detected and logged as a warning.

FormatDescription
macro:name Runs every action in the named action macro in order. If used in a requirement list, evaluates the named requirement macro. Unknown macro names are silently ignored (actions) or pass by default (requirements).
Macro file format (QuestLines/macros/<name>.json)

Each macro is a separate JSON file. "Type" is "action" or "requirement". Requirement macros AND all entries by default; add "AnyMode": true to use OR logic (at least one must pass).

// macros/give_rewards.json — action macro
{
  "Type": "action",
  "Entries": ["economy:give:500", "item:Gold_Coin:5", "addTag:rewarded"]
}

// macros/is_vip.json — requirement macro with AnyMode (OR)
{
  "Type": "requirement",
  "AnyMode": true,
  "Entries": ["hasTag:vip", "permission:has:server.vip"]
}

Macros can be created and edited from the in-game editor under the Macros tab in /ql quest. Legacy macros.json files are automatically migrated to per-file format on load.

Example

// Complete the quest and give rewards using a macro
"Actions": [
  "completequest:main_quest",
  "macro:give_rewards"
]

// Gate a response on a VIP requirement macro (OR logic)
"Requirements": ["macro:is_vip"]

Delay

Pauses execution of the remaining actions for a given number of seconds, then resumes. Player data is re-fetched after the delay so variable checks stay current. Decimal values are supported for sub-second precision.

FormatDescription
delay:N Wait N seconds before continuing. N may be a decimal (e.g. 0.5, 2.5).
Not supported inside random:

delay: cannot be used inside a random:[...] bracket list.

Example

// Give a reward then show a title 1.5 seconds later
"Actions": [
  "item:Health_Potion:3",
  "delay:1.5",
  "title:Potions received!"
]

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, use, launch, portal, catchfish, catchfishbiome, catchfishrarity, catchfishzone, catchfishlegendary, catchfishnew, catchfishperf, releasefish, fishmiss, parry, counter, dodge, arena_complete, arena_wave (arena types require EndgameQoL; AA types require Anglers' Almanac; parry/counter require Perfect Parries; dodge requires Perfect Dodges). Targets use a comma-separated bracket list or a bare single value; * matches any. For use, the targets are item IDs the player must be holding when clicking. For launch/portal, the targets are block IDs (Launchpad, Portal_Device, Hub_Portal_*, etc.). For parry/counter, the targets match the attacker's (parry) or countered entity's (counter) NPC type ID, or Player. For dodge, the targets match the attacker's NPC type ID, or Player.
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:id:type:[targets]:loc:x:y:z:radius Location-gated. Only counts events while the player is within radius blocks of (x, y, z). Coords are absolute world coordinates (raw doubles — ~-relative is not supported here). The check uses the player's position at the moment the event fires.
track:id:type:[targets]:world:worldName World-gated. Only counts events while the player is in the named world. Supports * wildcard (e.g. over*) and bracket lists ([overworld,nether]).
track:id:use:[items]:button:left|right use-only modifier. Counts only the named mouse button (left for melee swings, right for wand/tome casts and similar activations). Omitted = either button counts.
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.

Modifiers (exclude:, loc:, world:, button:) can appear in any order after the targets and combine with AND semantics — e.g. a tracker with both loc: and world: only counts events that satisfy both gates.

Example — location-gated kills (clear a bandit camp):

// Setup — only kills within 30 blocks of (1024, 64, -512) count
"track:bandit_camp:kill:[Bandit,Marauder]:loc:1024:64:-512:30"

// Combine gates: bandits in that radius AND only in the overworld
"track:bandit_camp:kill:[Bandit,Marauder]:loc:1024:64:-512:30:world:overworld"

// Requirement
"track:bandit_camp:10"

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"

Open GUI (QuestLines GUI companion plugin)

Opens a custom GUI screen for the player. Requires the QuestLines GUI companion plugin. The screen is defined by a .gui.json file in mods/QuestLinesGUI/. When QuestLines GUI is present, it registers this action with Core automatically at startup.

FormatDescription
opengui:guiId Open the screen with the given ID for the triggering player.
// In a response — open a shop when the player clicks
"opengui:blacksmith_shop"

// In LoadActions — auto-open a welcome screen once per player
// (requires: notTag:seen_welcome)
"addTag:seen_welcome"
"opengui:welcome_screen"

Quick Reference

FormatCategoryDescription
startquest:idQuest StateAdd to started quests + notification
completequest:idQuest StateMove to completed + notification
removequest: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
clearTimestamp:keyCooldownRemove a stored timestamp; resets the 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 ~)
tp:x:y:z[:world]TeleportTeleport player to coords (supports ~ relative); optional loaded world name (defaults to current)
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:soundNameSoundPlay 2D sound to player
sound:soundName:trueSoundBroadcast 2D sound to all players
sound:soundName:x:y:zSoundPlay 3D positional sound at coordinates (~ relative ok); broadcasts to nearby players
sound:soundName:x:y:z:playerSoundSame as above but only to the triggering player
title:primary[:secondary[:server|player[:isMajor]]]TitleShow an event title screen; server=broadcast to world, player=self only (default); isMajor=true for large format
setMarker:id:label:x:y:z[:image[:RRGGBB]]Map MarkerPlace a marker on the player's world map. Repeating the same id updates it.
removeMarker:idMap MarkerRemove the map marker with the given id (clears on reconnect)
hideBlock:x:y:z[:world]Block VisibilityHide a single block from this player only (sends Air); persists across reconnect
hideBlock:x1:y1:z1:x2:y2:z2[:world]Block VisibilityHide an inclusive 3D box from this player only; volume capped via block_override_max_region_volume
showBlock:x:y:z[:world]
showBlock:x1:y1:z1:x2:y2:z2[:world]
Block VisibilityClear a hideBlock override and re-send the real block(s) to the player
macro:nameMacroExecute a named action macro from macros/<name>.json in order
delay:NFlowPause for N seconds (decimals ok, e.g. 0.5) then continue remaining actions
mmo:xp:skillId:give:amountMMO SkillGive skill XP to player (requires MMOSkillTree)
rpgxp:give:amountRPG LevelingGive RPG XP to player (requires RPGLeveling)
elxp:give:amountEndless LevelingGrant EL XP to player (requires EndlessLeveling)
startArena:arenaId[:x:y:z]ArenaStart a Core wave arena at player pos or given coords (supports ~ relative)
failArenaArenaAbort the player's current arena session (runs the arena's FailActions)
mail:itemId:qtyBooks and PapersPush an item stack to the player's mailbox (requires Books and Papers)
givebook:title:author:bodyBooks and PapersGive the player a signed book; \f = page break, \n = line break (requires Books and Papers)
regionaddmember:regionIdRegionsAdd player as member of named region (QL Claims first, then OrbisGuard)
regionremovemember:regionIdRegionsRemove player from named region's member list (QL Claims first, then OrbisGuard)
opengui:guiIdGUIOpen a custom GUI screen for the player (requires QuestLines GUI plugin)
iconState:npcId:stateIcon StateSet per-player icon-state override on the named NPC. state: available, inprogress, objectives_complete, completed, locked, waiting, repeatable_ready, story, custom, unknown (requires QuestLines Icons)
iconState:npcId:state:clearIcon StateGuarded clear — removes the override only if it currently equals state (requires QuestLines Icons)
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.