Actions
Actions are colon-delimited strings in a response's Actions list.
They execute in order when the player clicks that response button.
Actions modify quest state, player data, inventory, the world, or navigate dialogue.
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:hastagworld flag is set. - Location-based triggers — combine with
nearPositionorzonerequirements to fire when a player enters an area.
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"
]
}
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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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. |
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.
| Format | Description |
|---|---|
| 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
| Format | Description |
|---|---|
| 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. |
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.
| Format | Description |
|---|---|
| page:pageId | Navigates the player's open dialogue to the specified page. Place this last in the actions list. |
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.
| Format | Description |
|---|---|
| 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. |
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.
| Format | Description |
|---|---|
| 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
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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. |
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.
| Format | Description |
|---|---|
| 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.
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": [{ "..." }]
}
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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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. |
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.
{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.
| Format | Description |
|---|---|
| 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 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.
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).
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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).
| Format | Description |
|---|---|
| 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
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| random:[action1,action2,action3] | Picks one of the listed actions at random and executes it. Enclose the list in [ and ], comma-separated. |
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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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).
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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
| Format | Description |
|---|---|
| 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. |
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"
]
Default icons available out of the box:
Home, Spawn, Portal, Warp,
Death, Campfire, Coordinate,
Temple_Gateway, PortalInvasion,
UserA–UserF.
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.
| Format | Description |
|---|---|
| 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). |
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.
| Format | Description |
|---|---|
| delay:N | Wait N seconds before continuing. N may be a decimal (e.g. 0.5, 2.5). |
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.
| Format | Description |
|---|---|
| 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.
| Format | Description |
|---|---|
| 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
| Format | Category | Description |
|---|---|---|
| startquest:id | Quest State | Add to started quests + notification |
| completequest:id | Quest State | Move to completed + notification |
| removequest:id | Quest State | Remove from both lists |
| addTag:tagName | Tags | Add a tag (idempotent) |
| removeTag:tagName | Tags | Remove a tag |
| track:id:type:[targets] | Tracking | Start a named tracker (see Named Trackers section) |
| track:id:type:target:loc:x:y:z:radius | Tracking | Start a location-gated named tracker |
| track:id:type:target:world:worldName | Tracking | Start a world-gated named tracker |
| untrack:id | Tracking | Remove named tracker and reset its count to zero |
| untrack:[id1,id2] | Tracking | Remove multiple named trackers at once |
| setTimestamp:key | Cooldown | Record time for cooldown gate |
| clearTimestamp:key | Cooldown | Remove a stored timestamp; resets the gate |
| timedStart:key | Timer | Record time for countdown timer |
| timedStart:key:durationSeconds | Timer | Record time + store duration (allows timedActive/timedExpired without repeating seconds) |
| item:itemId:qty | Items | Give item + notification |
| command:text:server | Commands | Run as server console |
| command:text:player[:elevated] | Commands | Run as player (add :elevated to grant temp wildcard perm) |
| page:pageId | Navigation | Navigate to another page |
| spawnCitizen:name:x:y:z[:delay[:qty]] | Spawning | Clone HyCitizens NPC at position (coords may use ~) |
| spawn:npcRoleId:x:y:z[:delay[:qty]] | Spawning | Spawn Hytale NPC by role ID (coords may use ~) |
| tp:x:y:z[:world] | Teleport | Teleport player to coords (supports ~ relative); optional loaded world name (defaults to current) |
| variable:name:set:value | Variable | Set named variable to value |
| variable:name:add:value | Variable | Add to named variable |
| variable:name:subtract:value | Variable | Subtract from named variable |
| variable:name:multiply:value | Variable | Multiply named variable |
| variable:name:divide:value | Variable | Divide named variable |
| economy:give:amount | Economy | Deposit amount into player account (requires VaultUnlocked) |
| economy:take:amount | Economy | Withdraw amount from player account (requires VaultUnlocked) |
| sound:soundName | Sound | Play 2D sound to player |
| sound:soundName:true | Sound | Broadcast 2D sound to all players |
| sound:soundName:x:y:z | Sound | Play 3D positional sound at coordinates (~ relative ok); broadcasts to nearby players |
| sound:soundName:x:y:z:player | Sound | Same as above but only to the triggering player |
| title:primary[:secondary[:server|player[:isMajor]]] | Title | Show 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 Marker | Place a marker on the player's world map. Repeating the same id updates it. |
| removeMarker:id | Map Marker | Remove the map marker with the given id (clears on reconnect) |
| hideBlock:x:y:z[:world] | Block Visibility | Hide a single block from this player only (sends Air); persists across reconnect |
| hideBlock:x1:y1:z1:x2:y2:z2[:world] | Block Visibility | Hide 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 Visibility | Clear a hideBlock override and re-send the real block(s) to the player |
| macro:name | Macro | Execute a named action macro from macros/<name>.json in order |
| delay:N | Flow | Pause for N seconds (decimals ok, e.g. 0.5) then continue remaining actions |
| mmo:xp:skillId:give:amount | MMO Skill | Give skill XP to player (requires MMOSkillTree) |
| rpgxp:give:amount | RPG Leveling | Give RPG XP to player (requires RPGLeveling) |
| elxp:give:amount | Endless Leveling | Grant EL XP to player (requires EndlessLeveling) |
| startArena:arenaId[:x:y:z] | Arena | Start a Core wave arena at player pos or given coords (supports ~ relative) |
| failArena | Arena | Abort the player's current arena session (runs the arena's FailActions) |
| mail:itemId:qty | Books and Papers | Push an item stack to the player's mailbox (requires Books and Papers) |
| givebook:title:author:body | Books and Papers | Give the player a signed book; \f = page break, \n = line break (requires Books and Papers) |
| regionaddmember:regionId | Regions | Add player as member of named region (QL Claims first, then OrbisGuard) |
| regionremovemember:regionId | Regions | Remove player from named region's member list (QL Claims first, then OrbisGuard) |
| opengui:guiId | GUI | Open a custom GUI screen for the player (requires QuestLines GUI plugin) |
| iconState:npcId:state | Icon State | Set 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:clear | Icon State | Guarded clear — removes the override only if it currently equals state (requires QuestLines Icons) |
| global:addtag:tagName | Global State | Add tag to server-wide global tag list |
| global:removetag:tagName | Global State | Remove tag from server-wide global tag list |
| global:variable:name:op:value | Global State | Mutate server-wide variable (ops: add, subtract, multiply, divide, set) |
| random:[action1,action2,...] | Random | Pick 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:pageId | System (auto) | Last-seen page tag — written automatically by the engine; powers the Quest Journal. Do not write manually. |