Quests
Configure the quest system
Overview
The quest system lets you create tasks for players to complete - gathering resources, reaching skill levels, killing entities, and more. Quests are defined as JSON files in mods/mmoskilltree/quests/ and loaded automatically on startup.
Quests are organized into categories (e.g., main, daily, misc) and can have prerequisites, level requirements, and permission gates. Players interact with quests via the Quests UI tab or /quest, and admins manage them with /mmoquestadmin.
Want to ship quests as an installable add-on? Bundle them in a content pack - see the quest pack authoring reference for the full field set, the extends/params template DSL, and worked bounty-template examples, and templates for repeatable quest families in general.
mods/mmoskilltree/quests/*.jsonContent packs ship quests under Server/MMOSkillTree/Quests/*.json (and QuestTemplates/*.json for the extends DSL); see the quest pack authoring reference.
Default Quests & Customization
On every server start, default quests are written to mods/mmoskilltree/quests/_defaults/default-quests.json. This file is always overwritten, so defaults stay up-to-date with new mod versions automatically.
To customize quests, create your own .json files anywhere in the quests/ folder (or subdirectories). If any user files exist outside of _defaults/ and backups/, the default quests are ignored entirely. The generated default file remains as a reference you can copy and customize.
Servers upgrading from older versions will have their example-quests.json automatically moved to quests/backups/.
After editing quest files, run /mmoquestadmin reload to apply changes without restarting the server.
File Structure
Each JSON file in the quests/ directory contains a schemaVersion and a quests array. You can split quests across multiple files (e.g., one per category). The directory is scanned recursively.
{
"schemaVersion": 1,
"quests": [
{
"id": "mining_beginner",
"titleKey": "quest.mining_beginner.title",
"flavorKey": "quest.mining_beginner.flavor",
"category": "main",
"tags": ["gathering", "beginner"],
"enabled": true,
"sortOrder": 1,
"autoAccept": true,
"sequential": false,
"repeatable": false,
"cooldownSeconds": 0,
"autoClaimRewards": true,
"permission": null,
"prerequisites": [],
"requirements": { "minLevel": 0, "minSkill": null, "minSkillLevel": 0 },
"objectives": [
{
"id": "mine_stone",
"type": "BREAK_BLOCK",
"target": "Stone",
"amount": 150,
"displayText": "Mine 150 Stone blocks",
"matchMode": "CONTAINS"
}
],
"rewards": [
{
"type": "COMMAND",
"command": "/give {player} Tool_Pickaxe_Iron --quantity=1",
"displayName": "Iron Pickaxe",
"description": "A sturdy pickaxe for your efforts"
},
{
"type": "XP",
"skill": "MINING",
"amount": 200,
"displayName": "+200 Mining XP"
}
]
}
]
}This example authors autoClaimRewards: true explicitly to opt into auto-claim. Omitting the field (or setting it false) is the current default and parks the quest at COMPLETED_UNCLAIMED until the player runs /quest claim. It also uses titleKey/flavorKey localization keys rather than the raw displayName/description literals - see Localization.
Quest Fields
Basic Fields
| Field | Type | Default | Description |
|---|---|---|---|
id | String | required | Unique quest identifier. Cannot contain | = : , characters |
titleKey | String | null | Localization key for the quest title. Convention: quest.<id>.title. Preferred over displayName |
flavorKey | String | null | Localization key for markdown flavor/lore text. Convention: quest.<id>.flavor. Preferred over description |
titleArgs / flavorArgs | String[] | [] | Ordered template args substituted into titleKey/flavorKey (e.g. @amount, @skill, or a literal) |
displayName | String | id | Deprecated raw fallback - prefer titleKey. Skips localization entirely; never author on new content |
description | String | "" | Deprecated raw fallback - prefer flavorKey |
category | String | "misc" | Category for grouping (e.g., main, daily, misc) |
enabled | Boolean | true | Whether the quest is active. Disabled quests are not indexed or offered |
sortOrder | Integer | 0 | Sort position within category (lower = first) |
tags | String[] | [] | Tags for filtering and organization (e.g., ["epic", "combat", "chapter-2"]) |
variables | Object | {} | Key-value metadata for custom data (e.g., { "difficulty": "hard", "region": "mountains" }) |
Behavior Fields
| Field | Type | Default | Description |
|---|---|---|---|
autoAccept | Boolean | false | Automatically start the quest when a matching event fires (no manual accept needed) |
sequential | Boolean | false | Objectives must be completed in order (top to bottom) |
repeatable | Boolean | false | Quest can be completed multiple times |
cooldownSeconds | Long | 0 | Seconds before a repeatable quest can be accepted again (e.g., 86400 for daily) |
autoClaimRewards | Boolean | false (1.4.3+) | An omitted or false flag parks the quest at COMPLETED_UNCLAIMEDon finishing its objectives; the player must run /quest claim. Only an explicit true auto-grants rewards on completion. A Bounty Board quest always behaves as false regardless of this flag - bounty rewards are only ever collected at the board |
requiresFeatures | String or String[] | [] | Gates the whole quest on one or more server feature ids (e.g. mastery, abilities, currency, xp_tokens, command_rewards, item_requirements, action_requirements). Hides the quest entirely when any listed feature is off |
requiredAchievements | String[] | [] | Shorthand array of achievement ids, folded into the same prerequisite gate as prerequisites.achievements - each must be UNLOCKED |
requiresSkills | Object | {} | A {SKILL: level} map, an alias form of the array requirements shorthand below - a skill-level gate expressed as a map instead of an array of objects |
turnInNpcId | String | null | The literal value "giver" auto-appends a synthetic report-back TURN_IN objective at the quest's own npcViewId (warned and skipped if npcViewId is unset). Any OTHER value routes the turn-in to a different NPC than the giver, auto-labeled "Go to {NPC}" instead of "Return to {NPC}". Skipped if you already hand-authored a TURN_IN objective |
completionDialogue | String | null | A dialogue id the NPC page hands off to on quest completion, instead of reopening the quest list |
autoTrack | Boolean | false | Auto-pins the quest to the on-screen tracker the moment it is accepted |
resetsOnComplete | Array of Strings | [] | Quest IDs to fully reset (state, progress, cooldown) when this quest is claimed. Targets become re-acceptable immediately. Fires after rewards execute; for manual-claim quests, only on the actual claim. Unknown IDs and self-references are ignored with a warning. Chains do not cascade - targets are reset, not re-completed. Example: ["daily_mining", "daily_combat"] |
permission | String | null | Permission node required to accept the quest (e.g., mmoskilltree.quest.vip) |
npcViewId | String | null | NPC identifier that surfaces this quest. When set, the quest is hidden from the quest log until accepted and only appears in the NPC quest UI |
npcDisplayName | String | null | Display name for the NPC shown in the NPC quest UI header |
npcRequiredToAccept | Boolean | false | When true, the quest can only be accepted through the NPC quest page, not from the quest log |
incompleteMarkdown | String | null | NPC dialogue shown before the quest is accepted. Supports # headers, **bold**, and *italic* |
activeMarkdown | String | null | NPC dialogue shown while the quest is in progress |
completeMarkdown | String | null | NPC dialogue shown when the quest is complete |
Requirements
The requirements field gates who can accept the quest. It accepts either a single object or an array of objects. All conditions must be met before the quest becomes available.
Legacy shorthand vs. the unified gate
requirements object below (minLevel/minSkill/minSkillLevel) is a legacy shorthand kept for backward compatibility. New content that needs the fuller gate vocabulary (total level, achievements, currency balance, playtime, mastery-node ownership, and more) should author the prerequisites object form instead - see the full leaf-field list under Branching Format below. Both forms are read together and AND-combined when both are present on the same quest.Single Object (one skill)
The simplest format - a single object with an optional total level gate and one skill requirement:
"requirements": {
"minLevel": 25,
"minSkill": "MINING",
"minSkillLevel": 15
}| Field | Type | Default | Description |
|---|---|---|---|
minLevel | Integer | 0 | Minimum total level across all skills |
minSkill | String | null | Skill name for the skill-level requirement (e.g., MINING) |
minSkillLevel | Integer | 0 | Minimum level in the specified skill |
Array Format (multiple skills)
To require multiple skills, use an array of requirement objects. Each entry can specify a minLevel, a minSkill/minSkillLevel pair, or both. The highest minLevel across all entries is used as the total level gate, and every skill requirement must be met.
"requirements": [
{ "minLevel": 25 },
{ "minSkill": "MINING", "minSkillLevel": 15 },
{ "minSkill": "CRAFTING", "minSkillLevel": 20 }
]In this example, the player needs total level 25, Mining level 15, and Crafting level 20 to accept the quest. All skill requirements are displayed in the quest log's “Requires:” line.
Both formats are fully supported. Use the single object when you only need one skill gate, and the array when you need multiple.
Prerequisites
Prerequisites control which quests must be completed before this quest becomes available. There are two formats: a simple array (AND-all) and a structured object (AND/OR grouping).
Simple Format (AND)
A plain array of quest IDs - all must be completed:
"prerequisites": ["mining_beginner", "combat_initiate"]Branching Format (AND/OR)
For branching storylines, use an object with mode and nested groups. This lets you create quest paths where players can choose between different branches:
"prerequisites": {
"mode": "AND",
"quests": ["quest_intro"],
"groups": [
{
"mode": "OR",
"quests": ["quest_path_warrior", "quest_path_mage"]
}
]
}In this example, the player must complete quest_intro AND either quest_path_warrior OR quest_path_mage.
| Field | Type | Description |
|---|---|---|
mode | String | "AND" (all must pass) or "OR" (any one must pass) |
quests | String[] | Quest IDs evaluated directly with this group's mode |
permissions | String[] | Permission nodes evaluated with this group's mode. In AND mode all must be held; in OR mode any one suffices |
achievements | String[] | Achievement ids that must be UNLOCKED |
features | String[] | Server feature ids that must be enabled |
minCombatLevel | Integer | Minimum combat level (any one COMBAT-category skill at or above this) |
skillLevels | Array | Per-skill level gates: [{ "skill": "MINING", "level": 20 }] |
totalLevel | Integer | Minimum total (all-skills-summed) level |
playtimeSeconds | Long | Minimum lifetime playtime |
kills | Array | Kill-count gates: [{ "mobPattern": "Trork", "min": 50 }]. An empty/"*" mobPattern is any-mob total kills |
deaths | Object | A death-count range gate: { "min": 0, "max": 5 } (a negative/omitted max means no upper bound) |
achievementPoints | Integer | Minimum lifetime achievement point total |
masteryNodes | Array | Must OWN the named node: [{ "trackId": "acrobatics_mastery", "nodeId": "acr_t1" }] |
currencyBalances | Array | Current-balance gates: [{ "currencyId": "bounty_token", "min": 100, "max": -1 }] (a negative max is no cap) |
lifetimeCurrencySpent | Array | Lifetime-spent gates (no upper bound): [{ "currencyId": "mastery_point", "min": 50 }] |
groups | Array | Nested prerequisite groups, each with their own full field set (recursive) |
Groups can be nested to any depth. The simple array format is fully backward compatible and equivalent to { "mode": "AND", "quests": [...] }. This is the same PrerequisiteGroup schema shared by Mastery track/node requirements, ShopEntry/ ShopPool/BountyBoard gates, and Currency visibility - see the content-pack schemas reference if it documents the shared shape.
The quest UI displays branching prerequisites with "OR" labels so players can see which paths are available. Permission requirements display as "Permission: node.name".
Permission Prerequisites
Add a permissions array to any prerequisite group to require permission nodes alongside quest completions. Permissions and quests can be freely mixed in AND/OR logic:
"prerequisites": {
"mode": "AND",
"quests": ["first_blood"],
"permissions": ["rank.vip", "server.quest.unlock"]
}In this example, the player must have completed first_blood AND hold both rank.vip and server.quest.unlock permissions. In OR mode, completing any quest or holding any permission in the group would be enough.
Permissions work at any nesting level, so you can create complex gates like "complete quest_a AND (have rank.vip OR complete quest_b)".
Visibility
The optional visibility object controls whether a quest appears in the quest log. By default, all quests are visible. Hidden quests remain invisible until their conditions are met - but once a quest is started (active, completed, or on cooldown), it is always visible regardless of visibility settings.
| Field | Type | Default | Description |
|---|---|---|---|
hidden | Boolean | false | Master switch. When true, the quest is hidden until conditions below are met |
permission | String | null | Quest only visible to players with this permission |
requirePrerequisites | Boolean | false | Quest only visible after all prerequisite quests are completed |
requireLevel | Boolean | false | Quest only visible when the player meets the level/skill requirements |
When multiple conditions are set, all must pass for the quest to be visible. If hidden is true with no other conditions, the quest falls back to the standard acceptance check - it becomes visible when the player meets all requirements and prerequisites.
"visibility": {
"hidden": true,
"requirePrerequisites": true
}This is useful for quest chains where you don't want players to see future quests until they've completed the current step.
Objectives
Each quest has one or more objectives. Objective ordering can be controlled in two ways:
sequential: true- All objectives must be completed top-to-bottom (legacy behavior, still supported)- Per-objective
orderfield - Group objectives into stages for mixed sequential/parallel completion
Objective text is auto-generated and localized from the objective type, target, and amount, so each player sees it in their own client language. The displayText field is an optional override; leave it out to use the localized default.
Objective Types
| Type | Triggered By | Target Example |
|---|---|---|
BREAK_BLOCK | Breaking a block | Stone, Ore_Iron |
PLACE_BLOCK | Placing a block | Plank |
CRAFT_ITEM | Crafting an item | Plank |
KILL_ENTITY | Killing an entity | Trork |
DEAL_DAMAGE | Dealing damage to an entity | Trork |
PICKUP_ITEM | Picking up an item | Coin |
CATCH_FISH | Catching a fish (via fishing mod or fish mob kill) | Salmon, Lobster, or "" (any fish) |
REACH_LEVEL | Reaching a skill level | MINING (skill name) or TOTAL |
TALK_TO_NPC | Interacting with a quest-giver NPC (native dialogue engine; HyCitizens optional) | NPC view ID, or """" (any NPC) |
TURN_IN | Player clicks "Turn In" button in quest UI | Ore_Iron (item ID to remove from inventory) |
REACH_LOCATION | Entering a target zone or region | Zone or region name (matched against the objective zone field) |
CONSUME_ITEM | Eating or consuming an item | Potion_Healing, Apple |
SPEND_CURRENCY | Spending a currency (e.g., at a shop) | bounty_token (currency ID), or "" (any) |
COMPLETE_QUEST | Completing another quest | Quest id, or "" (any) |
TAKE_FALL_DAMAGE | Taking fall damage | "" (target unused) |
PLAYER_DEATH | Dying | "" (target unused) |
GAIN_XP | Earning skill XP - the standard "train a skill" objective | MINING (skill id) |
SPRINT_DISTANCE | Sprinting a distance | "" (target unused; amount is distance) |
SWIM_DISTANCE | Swimming a distance | "" (target unused; amount is distance) |
ABILITY_CAST | Casting an active ability | Ability id, or "" (any) |
EARN_CURRENCY | Earning (gaining) a currency, any source | Currency id, or "" (any) |
BREED_ANIMAL | Breeding a tamed animal (Taming pack content) | Animal/species id, or "" (any) |
FEED_ANIMAL | Feeding a tamed animal (Taming pack content) | Animal/species id, or "" (any) |
HARVEST_ANIMAL | Harvesting a resource from a tamed animal (Taming pack content) | Animal/species id, or "" (any) |
COMPANION_COMBAT | A tamed companion dealing or securing a kill (Taming pack content) | Mob id, or "" (any) |
COMPLETE_BOUNTY | Completing a Bounty Board contract - for meta-quests tracking bounty activity | Board id, or "" (any board) |
BOUNTY_STREAK | Reaching a daily bounty-completion streak (value-based: tracks a high-water mark, not an accumulating delta) | Board id, or "" (any board) |
REACH_LEVEL and BOUNTY_STREAK are value-based (they track a high-water mark, not an accumulating delta) - every other type accumulates toward amount.
Objective Fields
| Field | Type | Default | Description |
|---|---|---|---|
id | String | required | Unique ID within the quest. Cannot contain | = : , characters |
type | String | required | One of the objective types above |
target | String | "" | What to match against (block ID, entity type, skill name, etc.) |
amount | Integer | 1 | How many times the objective must be triggered. For REACH_LEVEL, this is the required level |
displayText | String | auto-generated | Deprecated raw fallback - prefer textKey, or leave both unset to let the renderer auto-generate a localized, pluralized sentence from type/target/amount |
textKey | String | null | Preferred explicit localization key for the objective line, checked before the inferred sentence |
matchMode | String | CONTAINS | How the target is matched against event identifiers |
qualifier | String | none | Secondary filter that must also match for progress to count. Case-insensitive exact match when set. Omit or null to match any event. Use "" (empty string) to only match events with no qualifier. Primary use: elite mob tier (e.g., "Legendary") |
zone | String | null | Scopes the objective to a zone or region (1.4.0). When set, progress only counts while the player is inside the named zone, matched case-insensitively against either the zone display name or its region (folder) name. Required for REACH_LOCATION. Omit or null for no zone scoping |
order | Integer | 0 | Ordering stage for mixed sequential/parallel objectives (see below) |
detailMarkdown | String | null | Per-objective NPC dialogue text shown in the NPC quest detail panel when this objective is the current active objective |
Objective Ordering
The order field lets you create quests with mixed sequential and parallel objectives. Objectives with the same order value can be completed in any order (parallel). Objectives with a higher order value are locked until all lower-order objectives are complete.
"objectives": [
{ "id": "gather_ore", "type": "BREAK_BLOCK", "target": "Ore_Iron", "amount": 10, "order": 1 },
{ "id": "craft_ingots", "type": "CRAFT_ITEM", "target": "Ingot_Iron", "amount": 5, "order": 1 },
{ "id": "kill_boss", "type": "KILL_ENTITY", "target": "Skeleton", "amount": 3, "order": 2 }
]In this example, the first two objectives (order 1) can be done in any order. The third objective (order 2) only becomes active after both order-1 objectives are complete. The quest UI groups objectives by stage and shows which are currently active.
order: 0or omitted - No ordering constraint (parallel with everything)- Using
sequential: trueauto-assigns incrementing order values for backward compatibility
Match Modes
| Mode | Behavior | Example |
|---|---|---|
CONTAINS | Target string appears anywhere in the identifier | "Stone" matches Stone, Cobblestone, Sandstone |
EXACT | Identifier must match the target exactly | "Stone" matches only Stone |
PREFIX | Identifier must start with the target | "Ore_" matches Ore_Iron, Ore_Gold |
Qualifier (Secondary Filter)
The qualifier field adds a secondary filter beyond target. When set, both the target and qualifier must match for progress to count. This is useful for filtering by elite mob tier, weapon type, or other event metadata.
{
"id": "kill_legendary_bears",
"type": "KILL_ENTITY",
"target": "Bear",
"qualifier": "Legendary",
"amount": 3,
"displayText": "Defeat 3 Legendary Bears"
}- Omitted or
null- matches any event (backward compatible) - Empty string
""- only matches events with no qualifier (e.g., normal mobs) - Any other value - case-insensitive exact match against the event qualifier
Currently unused for kills
Legendary, Epic, Rare) into this qualifier for KILL_ENTITY/ DEAL_DAMAGE was removed in 1.5.0. A live kill event today always fires with an empty qualifier, so any objective authoring a non-empty qualifier on those two types can never match. The field remains available and reserved (a future rarity system from the separate, optional MMO Mob Scaling mod could feed it again), and it stays fully functional for custom objective content wired to other event sources.Rewards
Quest rewards are delivered when the quest is completed (or claimed, if autoClaimRewards is false). Each quest can have multiple rewards. Rewards support four types: commands, XP grants, boost tokens, and currency.
Reward Types
| Type | Description | Required Fields |
|---|---|---|
COMMAND | Execute a server command | command |
XP | Grant skill XP directly | skill, amount |
BOOST_TOKEN | Activate a timed XP multiplier boost | skill, multiplier, durationMinutes |
CURRENCY | Grant currency (gold) | amount |
ABILITY_MOD | Grant a standalone improvement to an existing ability | modId |
If type is omitted, the reward defaults to COMMAND for backward compatibility. Existing command-based rewards continue to work unchanged.
Reward Examples
"rewards": [
{
"type": "COMMAND",
"command": "/give {player} Tool_Pickaxe_Iron --quantity=1",
"displayName": "Iron Pickaxe"
},
{
"type": "XP",
"skill": "MINING",
"amount": 500,
"displayName": "+500 Mining XP"
},
{
"type": "BOOST_TOKEN",
"skill": "ALL",
"multiplier": 2.0,
"durationMinutes": 30,
"displayName": "2x All XP (30m)"
},
{
"type": "CURRENCY",
"currencyId": "gold",
"amount": 100,
"displayName": "100 Gold"
}
]Common Reward Fields
| Field | Type | Default | Description |
|---|---|---|---|
type | String | COMMAND | Reward type: COMMAND, XP, BOOST_TOKEN, or CURRENCY |
displayName | String | "" | Shown in the quest UI and completion notification |
description | String | "" | Additional description shown in the quest UI |
nameKey | String | null | Explicit localization key for the reward's name. Preferred over displayName. XP/BOOST_TOKEN/CURRENCY rewards and a parseable /give command auto-render without needing this |
descriptionKey | String | null | Explicit localization key for the reward's description. Preferred over description |
icon | String | resolved | Inline item-id icon override for the reward chip/line |
An OPAQUE (non-/give) COMMAND reward MUST carry a nameKey (validator-enforced) since it cannot auto-render a display line from the command text. A /give {player} <item> --quantity=N command is the one form that DOES auto-render (parsed for its item icon/name) - --quantity=N is the only accepted quantity form; a positional quantity is silently ignored by Hytale's /give.
Command Reward Fields
| Field | Type | Default | Description |
|---|---|---|---|
command | String | required | The command to execute |
runAs | String | CONSOLE | CONSOLE or PLAYER |
delayTicks | Integer | 0 | Delay in ticks before executing the command |
queueIfOffline | Boolean | false | If the player is offline, queue the reward for next login |
Command Placeholders
| Placeholder | Replaced With |
|---|---|
{player} | Player name |
{questId} | The quest ID |
XP Reward Fields
| Field | Type | Description |
|---|---|---|
skill | String | Skill name (e.g., MINING, SWORDS, or a custom skill ID) |
amount | Integer | XP amount to grant |
Boost Token Reward Fields
| Field | Type | Description |
|---|---|---|
skill | String | Skill to boost (e.g., MINING) or ALL for all skills |
multiplier | Double | XP multiplier (e.g., 2.0 for double XP) |
durationMinutes | Integer | How long the boost lasts in minutes |
Currency Reward Fields
| Field | Type | Description |
|---|---|---|
currencyId | String | required - which currency to grant (e.g., gold, bounty_token, mastery_point). The reward is skipped if this is missing |
amount | Integer | Currency amount to grant (must be positive) |
Tags & Variables
Quests can include tags for categorization and variables for custom metadata. Tags are displayed as chips below the quest description in the quest UI and are also used for filtering on the docs site Quest Gallery.
{
"id": "dragon_slayer",
"displayName": "Dragon Slayer",
"tags": ["epic", "combat", "chapter-2"],
"variables": {
"difficulty": "hard",
"region": "mountains"
}
}Variables are stored as key-value pairs and are available for future conditional logic and filtering on the docs site quest gallery.
Quest States
Each quest tracks a per-player state:
| State | Description |
|---|---|
NOT_STARTED | Quest has not been accepted |
ACTIVE | Quest is in progress, objectives being tracked |
COMPLETED_UNCLAIMED | All objectives done, rewards not yet claimed (autoClaimRewards: false) |
COMPLETED | Quest finished and rewards delivered |
ON_COOLDOWN | Repeatable quest waiting for cooldown to expire |
Flow: NOT_STARTED → ACTIVE → COMPLETED (auto-claim) or COMPLETED_UNCLAIMED → COMPLETED (manual claim). For repeatable quests, the cycle resets after the cooldown period.
Admin Commands
The /mmoquestadmin command requires OP or mmoskilltree.admin permission. Arguments are pipe-separated.
| Command | Description |
|---|---|
/mmoquestadmin reload | Reload all quest files from disk |
/mmoquestadmin list [category] | List all quests, optionally filtered by category |
/mmoquestadmin give --args=player|questId | Give a quest to a player (use * for all players) |
/mmoquestadmin reset --args=player|questId | Reset a quest for a player (use all to reset all quests) |
/mmoquestadmin complete --args=player|questId | Force-complete a quest and execute rewards (queues rewards if player is offline) |
/mmoquestadmin status --args=player[|questId] | View quest states and objective progress for a player |
Player Commands
The /quest command is available to all players.
| Command | Description |
|---|---|
/quest accept <questId> | Accept a quest (checks prerequisites, level requirements, and permissions) |
/quest claim <questId|all> | Claim rewards for completed quests (checks inventory space for /give rewards) |
/quest abandon <questId> | Abandon an active quest and reset its progress |
/quest status [questId] | View your active quests and objective progress |
Examples
Simple Gathering Quest
A beginner quest that auto-accepts and rewards an iron pickaxe:
{
"id": "mining_beginner",
"displayName": "Novice Miner",
"description": "Prove your mining skills by breaking some stone.",
"category": "main",
"sortOrder": 1,
"autoAccept": true,
"objectives": [
{
"id": "mine_stone",
"type": "BREAK_BLOCK",
"target": "Stone",
"amount": 150,
"displayText": "Mine 150 Stone blocks"
}
],
"rewards": [
{
"command": "/give {player} Tool_Pickaxe_Iron --quantity=1",
"displayName": "Iron Pickaxe",
"description": "A sturdy pickaxe for your efforts"
}
]
}Sequential Quest with Prerequisites
Requires the mining_beginner quest to be completed first and Mining level 5. Objectives must be completed in order:
{
"id": "mining_journeyman",
"displayName": "Journeyman Miner",
"description": "Advance your mining career with harder materials.",
"category": "main",
"sortOrder": 2,
"autoAccept": true,
"sequential": true,
"prerequisites": ["mining_beginner"],
"requirements": {
"minSkill": "MINING",
"minSkillLevel": 5
},
"objectives": [
{
"id": "mine_iron",
"type": "BREAK_BLOCK",
"target": "Ore_Iron",
"amount": 25,
"displayText": "Mine 25 Iron Ore"
},
{
"id": "reach_mining_10",
"type": "REACH_LEVEL",
"target": "MINING",
"amount": 10,
"displayText": "Reach Mining level 10"
}
],
"rewards": [
{
"command": "/give {player} XpToken_Unarmed_Shard --quantity=2",
"queueIfOffline": true,
"displayName": "Unarmed XP Shards x2"
}
]
}Repeatable Daily Quest
Repeatable every 24 hours (86400 seconds). Not auto-accepted - players must start it manually or via admin command:
{
"id": "daily_woodcutting",
"displayName": "Daily Lumber",
"description": "Chop some wood for daily rewards.",
"category": "daily",
"sortOrder": 100,
"autoAccept": false,
"repeatable": true,
"cooldownSeconds": 86400,
"objectives": [
{
"id": "chop_logs",
"type": "BREAK_BLOCK",
"target": "Wood",
"amount": 30,
"displayText": "Chop 30 logs"
}
],
"rewards": [
{
"command": "/give {player} XpToken_Woodcutting_Fragment --quantity=2",
"queueIfOffline": true,
"displayName": "2 Woodcutting XP Fragments",
"description": "Daily woodcutting payment"
}
]
}Weekly Quest That Resets Dailies
A weekly "master" quest that fully resets a group of daily quests when claimed - clearing their state, progress, and cooldown so players can run the dailies again immediately. Pair this with repeatable: true so the master itself can be completed every cycle:
{
"id": "weekly_master",
"displayName": "Weekly Master",
"description": "Complete the weekly challenge to reset your daily quests.",
"category": "main",
"repeatable": true,
"cooldownSeconds": 604800,
"resetsOnComplete": ["daily_mining", "daily_combat", "daily_woodcutting"],
"objectives": [
{
"id": "kill_boss",
"type": "KILL_ENTITY",
"target": "Dragon",
"amount": 1,
"displayText": "Defeat a Dragon"
}
],
"rewards": [
{
"type": "XP",
"skill": "SWORDS",
"amount": 50000,
"displayName": "50,000 Swords XP"
}
]
}Hidden Quest Chain
This quest is invisible until the player completes mining_journeyman. The visibility object with requirePrerequisites: true prevents spoilers for future quest steps:
{
"id": "mining_expert",
"displayName": "Mining Expert",
"description": "Prove yourself as a true mining expert.",
"category": "main",
"sortOrder": 3,
"sequential": true,
"prerequisites": ["mining_journeyman"],
"requirements": {
"minSkill": "MINING",
"minSkillLevel": 15
},
"visibility": {
"hidden": true,
"requirePrerequisites": true
},
"objectives": [
{
"id": "mine_gold",
"type": "BREAK_BLOCK",
"target": "Ore_Gold",
"amount": 50,
"displayText": "Mine 50 Gold Ore"
},
{
"id": "reach_mining_20",
"type": "REACH_LEVEL",
"target": "MINING",
"amount": 20,
"displayText": "Reach Mining level 20"
}
],
"rewards": [
{
"command": "/give {player} XpToken_Mining_Token --quantity=3",
"queueIfOffline": true,
"displayName": "3 Mining XP Tokens",
"description": "A reward for the dedicated miner"
}
]
}Multi-Skill Requirements
This quest requires both Mining 30 and Crafting 20 before it can be accepted. The array format lets you gate quests behind any combination of skills:
{
"id": "master_smith",
"displayName": "Master Smith",
"description": "Only a skilled miner and crafter can forge the ultimate blade.",
"category": "main",
"sortOrder": 20,
"requirements": [
{ "minSkill": "MINING", "minSkillLevel": 30 },
{ "minSkill": "CRAFTING", "minSkillLevel": 20 }
],
"objectives": [
{
"id": "craft_blade",
"type": "CRAFT_ITEM",
"target": "Weapon_Longsword",
"amount": 1,
"displayText": "Craft a Longsword"
}
],
"rewards": [
{
"command": "/give {player} XpToken_Crafting_Token --quantity=2",
"queueIfOffline": true,
"displayName": "2 Crafting XP Tokens",
"description": "A reward for the master smith"
}
]
}Branching Prerequisites
A quest that requires completing an intro quest AND choosing one of two class paths. Players who completed either the warrior or mage path can continue to this quest:
{
"id": "chapter2_start",
"displayName": "Chapter 2: The Convergence",
"description": "Both paths lead here. Your journey continues.",
"category": "main",
"sortOrder": 10,
"tags": ["chapter-2", "epic"],
"prerequisites": {
"mode": "AND",
"quests": ["chapter1_intro"],
"groups": [
{
"mode": "OR",
"quests": ["path_warrior_complete", "path_mage_complete"]
}
]
},
"objectives": [
{
"id": "talk_elder",
"type": "KILL_ENTITY",
"target": "Elder_NPC",
"amount": 1,
"displayText": "Speak with the Elder"
}
],
"rewards": [
{
"type": "BOOST_TOKEN",
"skill": "ALL",
"multiplier": 2.0,
"durationMinutes": 60,
"displayName": "2x All XP (1hr)"
}
]
}Mixed Sequential/Parallel Objectives
Objectives with the same order value can be completed in any order. Higher order values unlock after lower ones are done:
{
"id": "forge_master",
"displayName": "Forge Master",
"description": "Gather materials, then forge the blade.",
"category": "main",
"sortOrder": 15,
"objectives": [
{
"id": "mine_ore",
"type": "BREAK_BLOCK",
"target": "Ore_Iron",
"amount": 20,
"displayText": "Mine 20 Iron Ore",
"order": 1
},
{
"id": "chop_wood",
"type": "BREAK_BLOCK",
"target": "Wood",
"amount": 10,
"displayText": "Gather 10 Wood",
"order": 1
},
{
"id": "craft_blade",
"type": "CRAFT_ITEM",
"target": "Weapon_Longsword_Iron",
"amount": 1,
"displayText": "Forge an Iron Longsword",
"order": 2
}
],
"rewards": [
{
"type": "XP",
"skill": "CRAFTING",
"amount": 1000,
"displayName": "+1000 Crafting XP"
},
{
"type": "CURRENCY",
"currencyId": "gold",
"amount": 50,
"displayName": "50 Gold"
}
]
}Quest with Manual Claim
With autoClaimRewards: false, the quest enters the COMPLETED_UNCLAIMED state when objectives are done. Players must use /quest claim to receive rewards - useful when inventory space matters:
{
"id": "boss_bounty",
"displayName": "Boss Bounty",
"description": "Defeat a powerful boss. Return to claim your reward.",
"category": "main",
"sortOrder": 6,
"autoClaimRewards": false,
"objectives": [
{
"id": "slay_boss",
"type": "KILL_ENTITY",
"target": "Trork_Chieftain",
"amount": 1,
"displayText": "Defeat the Trork Chieftain",
"matchMode": "EXACT"
}
],
"rewards": [
{
"command": "/give {player} Diamond_Sword --quantity=1",
"queueIfOffline": true,
"displayName": "Diamond Sword",
"description": "A legendary blade for the champion"
}
]
}NPC Quest with Turn In
Quests can be tied to NPCs using npcViewId. The built-in native dialogue engine and quest-giver runtime NPC roles let you spawn a quest giver and have players discover, accept, and turn in these quests through NPC interaction (opened via /mmoquestui) - no third-party mod required. The TURN_IN objective type removes items from the player's inventory when they click the Turn In button. Partial turn-in is supported - players can submit what they have and return later with more.
Optional: HyCitizens / Hyronix NPC Dialog
If you prefer to drive NPCs through HyCitizens (Hyronix NPC Dialog) instead of the native quest-giver roles, add mmoquestui as a command in your Hyronix NPC Dialog configuration at mods/Hyronix_NPC Dialog/. Configure the NPC's dialog to run mmoquestui <player> <npc_id> so that interacting with the NPC opens the quest page for that player.
{
"id": "blacksmith_delivery",
"displayName": "The Blacksmith's Order",
"description": "Gorrath needs iron ore for his forge.",
"category": "main",
"npcViewId": "blacksmith_01",
"npcDisplayName": "Gorrath the Blacksmith",
"npcRequiredToAccept": true,
"incompleteMarkdown": "# The Blacksmith's Request\n\n**Gorrath** looks up from his anvil.\n\n\"I need iron ore. Can you help?\"",
"activeMarkdown": "\"Still gathering? I'll be here.\"",
"completeMarkdown": "\"Excellent work! These will do nicely.\"",
"objectives": [
{
"id": "deliver_iron",
"type": "TURN_IN",
"target": "Ore_Iron",
"amount": 10,
"displayText": "Deliver 10 Iron Ore",
"matchMode": "EXACT",
"detailMarkdown": "\"Bring me **10 Iron Ore** from the northern mines.\""
}
],
"rewards": [
{
"type": "XP",
"skill": "MINING",
"amount": 500,
"displayName": "500 Mining XP"
}
]
}