Requirements
Requirements are colon-delimited strings that evaluate to true or false for a given player. They appear on both pages (controlling whether the page is selected at all) and responses (controlling whether the button is visible). All requirements in a list must pass.
Page requirements determine whether a page is shown when the player
interacts with an NPC. Response requirements determine whether an
individual button is shown on that page. An empty [] requirements list
always passes.
Arena
Arena progress requirements use named trackers. Set up a tracker with type
arena_complete (counts full completions) or arena_wave
(counts waves cleared), then check with track:id:qty. The arena ID used as the
target matches what was passed to startArena:. Supports * wildcards
(e.g. shadow_* to count all shadow arenas).
Wave arenas are now a Core feature — no optional dependency required.
| Format | Description |
|---|---|
| track:id:qty | True if the named arena tracker has count ≥ qty. Tracker types: arena_complete, arena_wave. |
Example
// Require 3 Shadow Trial completions to unlock the final page
"Actions": ["track:shadow_clears:3:arena_complete:[shadow_trial]"]
"Requirements": ["track:shadow_clears:3"]
Mailbox (Books and Papers optional dependency)
Checks the player's mailbox queue from the Books and Papers plugin. The check is per-world; the result reflects the mailbox in whichever world the player is currently in. Always evaluates false when Books and Papers is not installed.
| Format | Description |
|---|---|
| hasmail | True when the player has at least one queued item in their Books-and-Papers mailbox. |
Example
// Show a "you have mail!" page only when something is waiting
"Requirements": ["hasmail"]
Perfect Parries (Perfect Parries optional dependency)
Counts perfect parries and counterattacks via named trackers. Set up a tracker with
track:id:parry:[targets] or track:id:counter:[targets], then
check with track:id:qty. Targets match the attacker's NPC type ID
(e.g. Zombie), the literal Player for PvP, or * for any.
Both tracker types remain at 0 if Perfect Parries is not installed.
| Format | Description |
|---|---|
| track:id:parry:[targets] | Tracker setup. Increments once per successful perfect parry. Targets match the attacker's entity type. |
| track:id:counter:[targets] | Tracker setup. Increments once per successful counterattack landed after a parry. Targets match the countered entity's type. |
| track:id:qty | True when the named parry/counter tracker has reached qty. |
Example
// Quest start: count any perfect parry, and counterattacks against zombies
"Actions": [
"questStarted:duelist_trial",
"track:duelist_parries:parry:[*]",
"track:zombie_counters:counter:[Zombie]"
]
// Turn-in page
"Requirements": ["track:duelist_parries:25", "track:zombie_counters:10"]
Perfect Dodges (Perfect Dodges optional dependency)
Counts perfect dodges via a named tracker. Set up a tracker with
track:id:dodge:[targets], then check with track:id:qty.
Targets match the attacker's NPC type ID (e.g. Trork),
the literal Player for PvP, or * for any.
The tracker remains at 0 if Perfect Dodges is not installed.
| Format | Description |
|---|---|
| track:id:dodge:[targets] | Tracker setup. Increments once per successful perfect dodge. Targets match the attacker's entity type. |
| track:id:qty | True when the named dodge tracker has reached qty. |
Example
// Quest start: count any perfect dodge, plus dodges against trorks
"Actions": [
"questStarted:nimble_trial",
"track:any_dodges:dodge:[*]",
"track:trork_dodges:dodge:[Trork*]"
]
// Turn-in page
"Requirements": ["track:any_dodges:25", "track:trork_dodges:10"]
Block Breaking
Count block breaks using a named tracker of type block. Set up the tracker
with track:id:block:blockId (typically in the quest-accept response), then
check with track:id:qty. The * wildcard is supported for partial
block ID matching (e.g. track:ore_mined:block:*Ore*).
| Format | Description |
|---|---|
| track:id:qty | True if the named block tracker has count ≥ qty. See Named Tracker. |
Example
// Require mining 20 iron ore
"Requirements": [
"questStarted:miners_task",
"track:ore_mined:20"
]
Block Placing
Count block placements using a named tracker of type place. Set up with
track:id:place:blockId, then check with track:id:qty.
The * wildcard is supported for partial block ID matching
(e.g. track:planks_placed:place:*Planks counts any plank type).
| Format | Description |
|---|---|
| track:id:qty | True if the named place tracker has count ≥ qty. See Named Tracker. |
Example
// Require placing 10 stone bricks
"Requirements": [
"questStarted:build_the_wall",
"track:planks_placed:10"
]
Cooldown
Checks whether enough time has elapsed since a timestamp was recorded via
setTimestamp:key. If the timestamp has never been set for
this player, the requirement returns true.
| Format | Description |
|---|---|
| cooldown:key:seconds | True if ≥ seconds have passed since setTimestamp:key was called, or if it was never called. |
Because cooldown returns true when never set, place the
ready page (with cooldown:key:86400) before
the waiting page (with not:cooldown:key:86400). New players
will always see the ready page first. See
Quest Patterns → Daily Reward.
Example
// Ready page: cooldown has elapsed (or never been set)
"Requirements": ["cooldown:daily_key:86400"]
// Waiting page: cooldown is still active
"Requirements": ["not:cooldown:daily_key:86400"]
Use {cooldown:key:totalSeconds} in dialog text to show the remaining
wait time (e.g. "Come back in 4 hours 23 minutes.").
Daily Reset Gate
Checks whether a timestamp was set before the most recent past occurrence of a
specific clock time (HH:MM, server local time). If the timestamp has never
been set, the requirement returns true.
Pair with setTimestamp:key in a load action to gate once-per-day events.
| Format | Description |
|---|---|
| timedExact:key:hour:minute | True if the key timestamp is unset, OR was set before the most recent past occurrence of HH:MM server local time. |
Place a page with timedExact:daily_key:4:00 (resets at 4 AM) and
a setTimestamp:daily_key load action. When a player visits before
4 AM and the timestamp is already set from the same day, the requirement is false.
After the clock passes 4 AM, the requirement returns true for any player whose
timestamp was recorded before that rollover, allowing the reward to be claimed again.
Example
// Daily reward resets at midnight (00:00)
"Requirements": ["timedExact:daily_reward:0:0"]
"LoadActions": ["setTimestamp:daily_reward"]
Economy & Permissions (VaultUnlocked optional dependency)
These requirements integrate with the optional VaultUnlocked plugin. They return false if VaultUnlocked is not installed or the required provider is not active.
| Format | Description |
|---|---|
| economy:canafford:amount | True if the player's account balance is ≥ amount. Requires VaultUnlocked with a compatible economy provider. |
| permission:has:perm.node | True if the player has the specified permission node. Requires VaultUnlocked with a compatible permissions provider. |
Example
// Gate a response on the player being able to afford the cost
"Requirements": ["economy:canafford:500"]
// Only show VIP dialogue if player has the permission node
"Requirements": ["permission:has:myserver.vip"]
Fishing (HyFishing optional dependency)
Fishing count requirements use named trackers. Set up a tracker with the appropriate
fishing type (catchfish, catchfishbiome, catchfishrarity,
catchfishzone, or releasefish), then check with track:id:qty.
All fishing requirements return false if HyFishing is not installed.
The hasfishinbag check is a live bag inventory check that requires no tracker.
| Format | Description |
|---|---|
| track:id:qty | True if the named fishing tracker has count ≥ qty. Tracker types: catchfish, catchfishbiome, catchfishrarity, catchfishzone, catchfishlegendary, catchfishnew, catchfishperf, releasefish, fishmiss. Supports * wildcard on target. AA-specific types (catchfishlegendary, catchfishnew, catchfishperf, fishmiss) require Anglers' Almanac. |
| hasfishinbag:fishItemId:qty | Live check — true if the player currently has ≥ qty of the fish item in their bag. Use * to match any fish. No tracker needed. |
| fishmiss:fishItemId:qty | True if the player has lost (failed the AA minigame on) ≥ qty matching fish. Supports * wildcard. Requires tracking:fishmiss:fishItemId tag and Anglers' Almanac. |
| catchfishlegendary:qty | True if the player has caught ≥ qty legendary fish (AA isLegendary flag). Requires tracking:catchfishlegendary tag and Anglers' Almanac. |
| catchfishnew:qty | True if the player has caught ≥ qty new-discovery fish (AA isNewDiscovery flag). Requires tracking:catchfishnew tag and Anglers' Almanac. |
| catchfishperf:rating:qty | True if the player has ≥ qty catches at the given AA performance rating (perfect, great, good, fail, nil). Requires tracking:catchfishperf:rating tag and Anglers' Almanac. |
Example
// Require catching 10 fish of any type
"Requirements": [
"questStarted:fishing_quest",
"track:fish_caught:10"
]
// Gate a turn-in on having a specific fish in the bag right now
"Requirements": [
"hasfishinbag:golden_trout:1"
]
Achievements
Gate quests on the player’s achievement state. See the Achievements section in the Core overview for how achievements are defined and unlocked. All four checks return false when the AchievementManager is unavailable (e.g. before player data is loaded).
| Format | Description |
|---|---|
| achievement:id | True once the player has unlocked any stage of the named achievement. |
| achievement:id:minStage | True once the player has unlocked at least the given 1-based stage tier. |
| achievementstages:id:n | True once the player has unlocked at least n stages of the named achievement. |
| achievementcount:n | True once the player has unlocked at least n distinct achievements (any stage). Achievements whose config no longer exists are skipped. |
| achievementpoints:n | True once the player’s total earned achievement points are ≥ n. Points are summed across all unlocked stages of every achievement. |
Example
// Gate a master-fisher quest behind unlocking the legendary catch milestone
"Requirements": [
"achievement:legendary_catch"
]
// Hidden bonus dialog once the player has 50+ achievement points
"Requirements": [
"achievementpoints:50"
]
Global State (server-wide)
Global requirements check server-wide state stored in GlobalData rather
than per-player data. Tags and variables in the global store affect all players equally.
Mutate global state using the corresponding global actions.
| Format | Description |
|---|---|
| global:hastag:tagName | True if the server-wide global tag list contains tagName. |
| global:nottag:tagName | True if the server-wide global tag list does not contain tagName. |
| global:variable:name:equal:value | True if the global variable equals value. |
| global:variable:name:greater:value | True if the global variable is strictly greater than value. |
| global:variable:name:less:value | True if the global variable is strictly less than value. |
| global:variable:name:greaterOrEqual:value | True if the global variable is ≥ value. |
| global:variable:name:lessOrEqual:value | True if the global variable is ≤ value. |
Global tags and variables are shared across all players. Use them for
world events, server-wide flags, or counters that accumulate across players.
Per-player tags and variables (via hasTag and variable:)
remain per-player as before.
Example
// Show a world event page only when the server event is active
"Requirements": ["global:hastag:world_event_active"]
// Gate a reward on a server-wide counter reaching a threshold
"Requirements": ["global:variable:boss_kills:greaterOrEqual:100"]
HyCitizen Kill Count
Count kills of HyCitizens NPCs using a named tracker of type killcitizen
(e.g. track:bandit_kills:killcitizen:Bandit Guard). The name match is
case-sensitive and matches the NPC's exact display name. The *
wildcard is supported in the tracker target — e.g. track:bandit_kills:killcitizen:Bandit*
matches all citizen names beginning with "Bandit".
| Format | Description |
|---|---|
| track:id:qty | True if the named killcitizen tracker has count ≥ qty. See Named Tracker. |
Example
// Require killing 3 "Bandit Guard" citizens
"Requirements": [
"questStarted:bandit_raid",
"track:bandit_kills:3"
]
Inventory
Checks whether the player currently has a sufficient quantity of an item in their
inventory. The optional fourth segment :true marks the item as
consumable — if the response passes and is selected, the items
are removed before actions execute.
| Format | Description |
|---|---|
| item:itemId:qty | True if the player has ≥ qty of the item. Items are not consumed. |
| item:itemId:qty:true | True if the player has ≥ qty of the item. Items are consumed when the response is used. |
When :true is set, items are consumed before the response's
actions execute. This ensures the player cannot back out after the server takes
the items.
Example
// Check without consuming
"Requirements": ["item:gold_ingot:5"]
// Check AND consume on use (item turn-in)
"Requirements": ["item:ancient_blade:1:true"]
Item Group
Matches when the player holds at least qty of any single item
whose id matches one of the target patterns (and does not match any exclude pattern).
Patterns use the same * wildcard semantics as the named tracker system.
The id is a human-readable label used in objective progress text
(e.g. Ores: 15/25).
When used as a response requirement with the trailing :true
flag, the dialogue prompts the player to pick which specific item id to turn in (if more
than one matching stack is present) and consumes qty of the chosen item on click.
| Format | Description |
|---|---|
| itemgroup:id:[patterns]:qty | True if any matching item id has ≥ qty in the player's inventory. Check only. |
| itemgroup:id:[patterns]:qty:true | Same check; on response click, prompts for selection (if needed) and consumes qty of the chosen item. |
| itemgroup:id:[patterns]:exclude:[patterns]:qty[:true] | Same with an exclusion filter — ids matching an exclude pattern are ignored even if they match a target pattern. |
When used in Objectives, the renderer shows progress as the
highest matching stack count (e.g. if the group requires 25 and the player
has 10 Iron_Ore and 18 Copper_Ore, the objective displays 18/25).
Example
// Bring 25 of any ore (but not the rare purple/green variants)
"Requirements": [
"questStarted:ore_donation",
"itemgroup:Ores:[*Ore*,*Crustal*]:exclude:[*Purple*,*Green*]:25"
]
// On the response, prompt and consume 25 of whichever ore the player picks
"Responses": [{
"Text": "Here are the ores.",
"Requirements": ["itemgroup:Ores:[*Ore*,*Crustal*]:exclude:[*Purple*,*Green*]:25:true"],
"Actions": ["completequest:ore_donation"]
}]
Item Harvest
Count block/crop harvests using a named tracker of type harvest. Set up with
track:id:harvest:itemId, then check with track:id:qty.
Fires on InteractivelyPickupItemEvent (block/crop harvest) — does
not fire for picking up dropped item entities from the ground.
The * wildcard is supported for matching multiple item IDs.
| Format | Description |
|---|---|
| track:id:qty | True if the named harvest tracker has count ≥ qty. See Named Tracker. Supports * wildcard on target. |
Example
// Require harvesting 20 of any herb
"Requirements": [
"questStarted:herb_gathering",
"track:herb_harvest:20"
]
Item Pickup
Count item pickups using a named tracker of type pickup. Set up with
track:id:pickup:itemId, then check with track:id:qty.
Fires on the same InteractivelyPickupItemEvent as harvest.
The * wildcard is supported for matching multiple item IDs.
| Format | Description |
|---|---|
| track:id:qty | True if the named pickup tracker has count ≥ qty. See Named Tracker. Supports * wildcard on target. |
Example
// Quest requiring picking up 20 of any herb
"Requirements": [
"questStarted:herb_gathering",
"track:herb_pickup:20"
]
Item Use
Count item activations (mouse clicks while holding the item) using a named tracker
of type use. Set up with track:id:use:itemId, then check
with track:id:qty. Increments once per Pressed click; the
* wildcard is supported on the target. Use cases: counting melee
sword swings, bow shots, wand/spell-tome casts, potion drinks — anything the
client triggers via mouse input on a held item. Hytale has no separate
spell-cast event, so spells are tracked here on the spell-source item ID.
| Format | Description |
|---|---|
| track:id:qty | True if the named use tracker has count ≥ qty. See Named Tracker. Supports * wildcard on target. |
| track:id:use:[items]:button:left|right | Setup-action variant restricting counting to one mouse button (left for melee swings, right for activations like wands and tomes). Omitted = either button. |
Example
// Cast a fire tome 5 times (right-click only)
"Actions": ["questStarted:fire_training", "track:fire_casts:use:Tome_Fire:button:right"]
"Requirements": [
"questStarted:fire_training",
"track:fire_casts:5"
]
Icon State (QuestLines Icons optional dependency)
Checks whether a per-player icon-state override is currently active on a
specific NPC. The override is set with the matching
iconState: action — this
requirement only reads it. Use it to gate dialogue on a state previously
forced by another quest’s action.
Only registered when the QuestLines Icons companion plugin
is active. On installs without Icons the requirement always evaluates to
false.
| Format | Description |
|---|---|
| iconState:npcId:state | True iff the player’s override for npcId is currently set to state. state must be one of available, inprogress, objectives_complete, completed, locked, waiting, repeatable_ready, story, custom, unknown. False if no override is set or the override is a different state — this does not read the auto-computed state shown by Icons. |
Example
// Page only opens once another quest has flagged the smith as ready
"Requirements": ["iconState:smith_id:objectives_complete"]
Kill Counting
Kill counting uses named trackers. Set up a tracker with
track:id:kill:entityTypeId (typically in the quest-accept response),
then check with track:id:qty. Kill counting is not automatic
— counts only accumulate while a tracker is active. Use untrack:id on
quest completion to reset the counter.
| Format | Description |
|---|---|
| track:id:qty | True if the named kill tracker has count ≥ qty. See Named Tracker. |
The tracker target supports * as a wildcard for partial matching,
letting you count multiple enemy variants with a single tracker.
track:goblin_kills:kill:Goblin*— counts Goblin_Duke, Goblin_Miner, Goblin_Scout, etc.track:boss_kills:kill:*Boss*— counts any entity whose ID contains "Boss"
Example
// Require 10 goblin kills (any goblin variant)
"Requirements": [
"questStarted:lost_sword",
"track:goblin_kills:10"
]
Use the variable {track:id} in dialog text to show
the player their current count. See Text Variables.
Logic Operators
Requirements can be composed using any (OR logic), not
(negation), and the ! shorthand. By default, listing multiple
requirements is AND — all must pass. Use any for OR logic.
| Format | Description |
|---|---|
| any:req1|req2|req3 | True if any of the pipe-separated sub-requirements passes (OR logic). |
| not:type:value | Negates any requirement. Prefix any requirement with not: to invert it. |
| !requirement | Shorthand for not:. For example, !hastag:flag is equivalent to not:hastag:flag. |
| macro:name | Evaluates the named requirement macro (stored in QuestLines/macros/<name>.json). By default all requirements must pass (AND). If the macro has "AnyMode": true, at least one must pass (OR). Unknown macro names pass by default. Circular references are detected and pass with a warning log. |
The not: prefix consumes the rest of the string as the inner
requirement. For example, not:questCompleted:my_quest parses as
not( questCompleted:my_quest ). For requirements that are already
negated by name (like questNotStarted), not: is
redundant but valid.
Examples
// Page visible if player has EITHER quest started
"Requirements": [
"any:questStarted:quest_a|questStarted:quest_b"
]
// Negate a cooldown to show a "still waiting" page
"Requirements": [
"not:cooldown:daily_key:86400"
]
// Multiple combined: started AND (has item OR has tag)
"Requirements": [
"questStarted:delivery",
"any:item:package:1|hasTag:already_delivered"
]
MMO Skill Level (MMOSkillTree optional dependency)
Compares the player's current level in a named MMO skill. Requires the MMOSkillTree
plugin to be active; returns false silently if it is not installed.
Skill IDs are case-insensitive (e.g. MINING and mining are
the same).
| Format | Description |
|---|---|
| mmo:skillId:equal:value | True if the player's skill level equals value. |
| mmo:skillId:greater:value | True if the player's skill level is strictly greater than value. |
| mmo:skillId:less:value | True if the player's skill level is strictly less than value. |
| mmo:skillId:greaterOrEqual:value | True if the player's skill level is ≥ value. |
| mmo:skillId:lessOrEqual:value | True if the player's skill level is ≤ value. |
Example
// Require Mining level 10 to access the deep mine quest
"Requirements": ["mmo:MINING:greaterOrEqual:10"]
// Gate a master trainer page on skill level
"Requirements": [
"questStarted:apprentice_path",
"mmo:CRAFTING:greaterOrEqual:25"
]
Moon Phase
| Format | Description |
|---|---|
| moonPhase:full | Moon phase index 0 (full moon). |
| moonPhase:waning | Moon phase indices 1–3. |
| moonPhase:new | Moon phase index 4 (new moon). |
| moonPhase:waxing | Moon phase indices 5–7. |
| moonPhase:3 | Exact numeric phase check (0–7). |
Example
// Werewolf quest only during full moon
"Requirements": ["moonPhase:full"]
Named Trackers
A tracker is created with the track:id:type:[targets] action and checked
here with track:id:qty. The tracker ID ties the setup action, this
requirement, the {track:id} variable, and the objective display together —
no need to repeat the type or target anywhere else.
| Format | Description |
|---|---|
| track:id:qty | True if the named tracker's accumulated count ≥ qty. Returns false if the tracker does not exist (i.e. the track: action has not been run for this player). |
In an Objectives list, use the :: label override as normal:
"track:kills:10::Defeat enemies" renders as Defeat enemies: 3/10.
Named Variables
Named variables are numeric values stored per-player. They are set and mutated via the
variable: action and compared here as a requirement. Values default to
0 if never set. Comparisons use standard numeric rules; fractional
values are supported.
| Format | Description |
|---|---|
| variable:name:equal:value | True if the variable equals value exactly. |
| variable:name:greater:value | True if the variable is strictly greater than value. |
| variable:name:less:value | True if the variable is strictly less than value. |
| variable:name:greaterOrEqual:value | True if the variable is greater than or equal to value. |
| variable:name:lessOrEqual:value | True if the variable is less than or equal to value. |
Put the most-specific variable page first. For example, place the
threshold-met page (using greaterOrEqual) before the
in-progress page (using less) in the quest's
Pages list so the completion page is checked first.
Example
// Complete page — shown once counter reaches 5
"Requirements": [
"questStarted:my_quest",
"variable:counter:greaterOrEqual:5"
]
// Progress page — shown while counter is still below 5
"Requirements": [
"questStarted:my_quest",
"variable:counter:less:5"
]
Use {variable:name} in dialog text to display the current value to the
player. See Text Variables.
NPC Name
Passes when the NPC that opened the current dialogue has a specific display name.
The comparison is case-insensitive. Colons in the name are
supported — everything after npcname: is treated as the name.
This is useful when multiple NPCs share a quest but you want different pages
to show depending on which NPC the player interacted with.
| Format | Description |
|---|---|
| npcname:displayName | True if the NPC that opened the dialogue has the given display name (case-insensitive). |
The NPC name context is set automatically by the interaction listeners when
a player opens dialogue. It is not available in auto-trigger pages
(which fire without NPC interaction) — npcname: will always
return false in that context.
Example
// Show a special page only when talking to "Elder Maren"
"Requirements": [
"questStarted:main_story",
"npcname:Elder Maren"
]
NPC Talk Count
Talk counts are tracked automatically on every NPC interaction — no tracking tag is needed. The counter is incremented before the dialogue page is resolved. Use the NPC's display name (the name shown above their head) as the key.
| Format | Description |
|---|---|
| talk:citizenName:qty | True if the player has talked to this NPC ≥ qty times. Use the NPC's display name (e.g. talk:Elder Maren:3). |
Example
// Unlock dialogue on the 3rd visit to Elder Maren
"Requirements": ["talk:Elder Maren:3"]
// A "returning visitor" page for visits 2+
"Requirements": ["talk:Elder Maren:2"]
Regions (QuestLines Claims or OrbisGuard)
Check whether the player is inside, owns, or is 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. Returns false if neither plugin recognises the region ID.
| Format | Description |
|---|---|
| inregion:regionId | True if the player is currently inside the named region. |
| regionowner:regionId | True if the player is an owner of the named region. For QL Claims this is the current rent tenant (rent plots) or the current buy owner (buy plots); admin regions have no owner. |
| regionmember:regionId | True if the player is a member or owner of the named region. |
Example
// Show dialogue only while inside the city region
"Requirements": ["inregion:city_of_auren"]
// Gate a landowner page on region ownership
"Requirements": ["regionowner:player_homestead"]
Player Stats
Player-kill and travel-distance counts use named trackers, like all other count-based
requirements. Set up with track:id:playerkill:* or track:id:travel:*,
then check with track:id:qty. Travel distance is counted in blocks with no
progress notification.
| Format | Description |
|---|---|
| track:id:qty | True if the named playerkill or travel tracker has count ≥ qty. See Named Tracker. |
Example
// Unlock a bounty page after 5 player kills
"Requirements": [
"questStarted:bounty_hunter",
"track:pkills:5"
]
// Reward the player for exploring 1000 blocks
"Requirements": [
"questStarted:explorer_quest",
"track:distance:1000"
]
Player Tags
Tags are arbitrary string labels attached to a player's data. They are managed via
addTag and removeTag actions. Use them to track custom
state that doesn't map to a full quest.
| Format | Description |
|---|---|
| hasTag:tagName | True if the player currently has the named tag. |
| notTag:tagName | Deprecated. True if the player does not have the named tag. Use not:hastag:tagName or !hastag:tagName instead. |
Example
// Only show dialogue if player has the "vip" tag
"Requirements": ["hasTag:vip"]
// Show a path only if a branching choice has NOT been made
"Requirements": ["notTag:chose_evil_path"]
Position
| Format | Description |
|---|---|
| nearPosition:x:y:z:radius | True if the player is within radius blocks of the coordinate (x, y, z) — sphere check. |
| inArea:x1:y1:z1:x2:y2:z2 | True if the player is inside the axis-aligned box (AABB) defined by the two corner coordinates. Corner order does not matter. |
| areaCheck:[entityA,entityB,...]:x:y:z:radius | True if at least one entity matching one of the bracket-list patterns is found within radius blocks of (x, y, z). Patterns use the same * wildcard rules as the tracker target system; literal Player matches any player entity. A bare single name (no brackets) is also accepted. |
| depth:less:value | True if the player's Y-position is strictly less than value. |
| depth:greater:value | True if the player's Y-position is strictly greater than value. |
| depth:equal:value | True if the player's Y-position is exactly value. |
| distanceFrom:less:x:y:z:distance | True if the player's Euclidean distance to (x, y, z) is strictly less than distance. |
| distanceFrom:greater:x:y:z:distance | True if the player's Euclidean distance to (x, y, z) is strictly greater than distance. |
| distanceFrom:equal:x:y:z:distance | True if the player's Euclidean distance to (x, y, z) is exactly distance. |
Example
// Player must be near the quest location to turn in
"Requirements": ["nearPosition:120:64:-340:10"]
// Page only shows underground (below Y=40)
"Requirements": ["depth:less:40"]
// Page shows when player is far from a dangerous area
"Requirements": ["distanceFrom:greater:500:64:-200:100"]
Quest Items
Checks whether the player's virtual quest inventory contains at
least the given quantity of an item. Quest items are managed by the
givequestitem: and removequestitem: actions and are
never placed in the player's real pack.
| Format | Description |
|---|---|
| questitem:itemId:qty | True if the player's virtual quest inventory contains ≥ qty of the item. |
Example
// Require the medallion to show the turn-in page
"Requirements": [
"questStarted:artifact_hunt",
"questitem:Ancient_Medallion:1"
]
Quest State
| Format | Description |
|---|---|
| questCompleted:questId | True if the player has completed the named quest. |
| questNotCompleted:questId | True if the player has not completed the named quest. |
| questStarted:questId | True if the quest is currently in progress (started but not completed). |
| questNotStarted:questId | True if the quest has not been started (and is also not completed). |
Example
// Show the turn-in page only when quest is in progress
"Requirements": ["questStarted:lost_sword"]
// Show reward page only after completion
"Requirements": ["questCompleted:lost_sword"]
RPG Level & XP (RPGLeveling optional dependency)
Compares the player's global RPG level or total XP. Requires the RPGLeveling plugin to be active; returns false silently if it is not installed.
| Format | Description |
|---|---|
| rpglevel:max | True if the player is at the maximum RPG level. |
| rpglevel:equal:value | True if the player's RPG level equals value. |
| rpglevel:greater:value | True if the player's RPG level is strictly greater than value. |
| rpglevel:less:value | True if the player's RPG level is strictly less than value. |
| rpglevel:greaterOrEqual:value | True if the player's RPG level is ≥ value. |
| rpglevel:lessOrEqual:value | True if the player's RPG level is ≤ value. |
| rpgxp:equal:value | True if the player's total RPG XP equals value. |
| rpgxp:greater:value | True if the player's total RPG XP is strictly greater than value. |
| rpgxp:less:value | True if the player's total RPG XP is strictly less than value. |
| rpgxp:greaterOrEqual:value | True if the player's total RPG XP is ≥ value. |
| rpgxp:lessOrEqual:value | True if the player's total RPG XP is ≤ value. |
Example
// Require RPG level 10 to start a veteran quest
"Requirements": ["rpglevel:greaterOrEqual:10"]
// Show a special page only at max level
"Requirements": ["rpglevel:max"]
Endless Leveling (EndlessLeveling optional dependency)
Compares the player's Endless Leveling level, XP, skill attributes, race, or class.
Requires the EndlessLeveling plugin to be active; returns false silently
if it is not installed. The level cap is per-player (prestige-aware), so ellevel:max
uses getLevelCap(uuid).
| Format | Description |
|---|---|
| ellevel:max | True if the player is at their EL level cap. |
| ellevel:equal:value | True if the player's EL level equals value. |
| ellevel:greater:value | True if the player's EL level is strictly greater than value. |
| ellevel:less:value | True if the player's EL level is strictly less than value. |
| ellevel:greaterOrEqual:value | True if the player's EL level is ≥ value. |
| ellevel:lessOrEqual:value | True if the player's EL level is ≤ value. |
| elxp:equal:value | True if the player's EL XP equals value. |
| elxp:greater|less|greaterOrEqual|lessOrEqual:value | EL XP comparison operators. |
| elskill:SKILL:equal:value | True if the player's SKILL level equals value. |
| elskill:SKILL:greater|less|greaterOrEqual|lessOrEqual:value | Skill attribute level comparison. Skill IDs (case-insensitive): STRENGTH, HASTE, DEFENSE, LIFE_FORCE, STAMINA, FLOW, SORCERY, DISCIPLINE, PRECISION, FEROCITY. |
| elrace:raceId | True if the player's race ID matches (case-insensitive). |
| elclass:classId | True if the player's primary OR secondary class matches (case-insensitive). |
| elclass:primary:classId | True if the player's primary class matches. |
| elclass:secondary:classId | True if the player's secondary class matches. |
Example
// Require level 20 and some strength to start a raid
"Requirements": ["ellevel:greaterOrEqual:20", "elskill:STRENGTH:greater:5"]
// Warrior-only dialogue branch
"Requirements": ["elclass:primary:warrior"]
Timed Quest
Timed requirements check whether an active countdown is still running or has expired.
The timer is started via the timedStart:key action. Both requirements
return false if the timer was never started.
| Format | Description |
|---|---|
| timedActive:key:seconds | True if timedStart:key was called AND fewer than seconds have elapsed. Timer is still running. |
| timedExpired:key:seconds | True if timedStart:key was called AND ≥ seconds have elapsed. Time is up. |
Place the expired page before the active page.
Both return false when never started, so the intro page (with
questNotStarted) catches those players. Once started, the expired
page is checked first — if time is up it shows the failure; otherwise the active
page shows. See Quest Patterns → Timed Quest.
Example
// Expire page — must come BEFORE active page in Pages list
"Requirements": [
"questStarted:courier_run",
"timedExpired:courier_timer:300"
]
// Active page — shown while timer is running
"Requirements": [
"questStarted:courier_run",
"timedActive:courier_timer:300"
]
Use {timeleft:key:totalSeconds} in dialog text to show the remaining
time (e.g. "You have 2 minutes 45 seconds left.").
World
| Format | Description |
|---|---|
| world:worldName | True if the player is currently in the named world. |
Example
"Requirements": ["world:overworld"]
World Time
| Format | Description |
|---|---|
| timeOfDay:morning | In-game hours 6–11. |
| timeOfDay:afternoon | In-game hours 12–16. |
| timeOfDay:evening | In-game hours 17–20. |
| timeOfDay:night | In-game hours 21–5. |
Example
// NPC only talks at night
"Requirements": ["timeOfDay:night"]
Zone & Biome
Check the player's current world zone or biome. Zone names correspond to named
map regions (e.g. emerald_grove); biome names correspond to biome
identifiers (e.g. temperate_forest). No tracking tag required.
| Format | Description |
|---|---|
| zone:regionName | True if the player is currently in the named world zone. |
| biome:biomeName | True if the player is currently in the named biome. |
Example
// Show dialogue only in the Emerald Grove zone
"Requirements": ["zone:emerald_grove"]
// Show a page only in the temperate forest biome
"Requirements": ["biome:temperate_forest"]
Quick Reference
| Format | Category | Description |
|---|---|---|
| questCompleted:id | Quest State | Quest is completed |
| questNotCompleted:id | Quest State | Quest is not completed |
| questStarted:id | Quest State | Quest is in progress |
| questNotStarted:id | Quest State | Quest not started |
| hasTag:tag | Tags | Player has tag |
| notTag:tag | Tags | Player does not have tag — deprecated, use !hastag:tag |
| track:id:qty | Tracking | Named tracker count ≥ qty (set up with track:id:type:target action; all count-based tracking uses this format) |
| item:itemId:qty | Inventory | Has item (no consume) |
| item:itemId:qty:true | Inventory | Has item (consume on use) |
| talk:citizenName:qty | NPC Talk | Talk count threshold (auto-tracked; use the NPC's display name) |
| npcname:displayName | NPC Name | True if the NPC that opened the dialogue has this name (case-insensitive; always false in auto-trigger) |
| cooldown:key:seconds | Cooldown | Elapsed since setTimestamp (true if never set) |
| timedActive:key:seconds | Timer | Timer running and not yet expired |
| timedExpired:key:seconds | Timer | Timer started and expired |
| timedExact:key:hour:minute | Timer | Daily reset gate; true if timestamp unset or before last HH:MM |
| nearPosition:x:y:z:r | Position | Within radius blocks (sphere) |
| inArea:x1:y1:z1:x2:y2:z2 | Position | Inside axis-aligned box defined by two corners |
| areaCheck:[entityA,entityB]:x:y:z:r | Position | Any listed entity exists within radius of point (* wildcards; Player = any player) |
| depth:less|greater|equal:value | Position | Player Y-position comparison |
| distanceFrom:less|greater|equal:x:y:z:dist | Position | Distance from fixed world point |
| timeOfDay:morning|afternoon|evening|night | World Time | Time of day |
| moonPhase:full|waning|new|waxing|<int> | Moon Phase | Moon phase check |
| world:worldName | World | Current world |
| zone:regionName | Zone/Biome | Player is in named world zone |
| biome:biomeName | Zone/Biome | Player is in named biome |
| any:req1|req2|... | Logic | OR across sub-requirements |
| not:requirement | Logic | Negate any requirement |
| !requirement | Logic | Shorthand negation — equivalent to not:requirement |
| macro:name | Logic | All requirements in named macro must pass (AND group); defined in macros.json |
| variable:name:equal:value | Variable | Named variable equals value |
| variable:name:greater:value | Variable | Named variable > value |
| variable:name:less:value | Variable | Named variable < value |
| variable:name:greaterOrEqual:value | Variable | Named variable ≥ value |
| variable:name:lessOrEqual:value | Variable | Named variable ≤ value |
| economy:canafford:amount | Economy | Player balance ≥ amount (requires VaultUnlocked) |
| permission:has:perm.node | Permissions | Player has permission node (requires VaultUnlocked) |
| mmo:skillId:equal:value | MMO Skill | Skill level equals value (requires MMOSkillTree) |
| mmo:skillId:greater|less|greaterOrEqual|lessOrEqual:value | MMO Skill | Skill level comparison (requires MMOSkillTree) |
| rpglevel:max | RPG Level | Player is at max RPG level (requires RPGLeveling) |
| rpglevel:equal:value | RPG Level | RPG level equals value (requires RPGLeveling) |
| rpglevel:greater|less|greaterOrEqual|lessOrEqual:value | RPG Level | RPG level comparison (requires RPGLeveling) |
| rpgxp:greater|less|equal|greaterOrEqual|lessOrEqual:value | RPG XP | Total RPG XP comparison (requires RPGLeveling) |
| ellevel:max | EL Level | Player at EL level cap (requires EndlessLeveling) |
| ellevel:equal|greater|less|greaterOrEqual|lessOrEqual:value | EL Level | EL level comparison (requires EndlessLeveling) |
| elxp:equal|greater|less|greaterOrEqual|lessOrEqual:value | EL XP | EL XP comparison (requires EndlessLeveling) |
| elskill:SKILL:equal|greater|less|greaterOrEqual|lessOrEqual:value | EL Skill | Skill attribute level (requires EndlessLeveling) |
| elrace:raceId | EL Race | Player's race matches (requires EndlessLeveling) |
| elclass:classId, elclass:primary|secondary:classId | EL Class | Primary/secondary class match (requires EndlessLeveling) |
| hasfishinbag:fishItemId:qty | Fishing | Live bag check; * for any fish; no tracker needed (requires HyFishing) |
| inregion:regionId | Regions | Player is inside named region (QL Claims first, then OrbisGuard) |
| regionowner:regionId | Regions | Player owns named region (QL Claims first, then OrbisGuard) |
| regionmember:regionId | Regions | Player is member or owner of named region (QL Claims first, then OrbisGuard) |
| global:hastag:tagName | Global State | Server-wide tag list contains the tag |
| global:nottag:tagName | Global State | Server-wide tag list does not contain the tag |
| global:variable:name:op:value | Global State | Server-wide variable comparison (ops: greater, less, equal, greaterOrEqual, lessOrEqual) |
| hasmail | Mailbox | Player has at least one queued item in their Books-and-Papers mailbox (requires Books and Papers) |
| iconState:npcId:state | Icon State | Player’s per-NPC icon-state override is currently set to state (one of available, inprogress, objectives_complete, completed, locked, waiting, repeatable_ready, story, custom, unknown) (requires QuestLines Icons) |