Tasks and Quests
Build tick-based activities with the Task Manager and multi-stage quests with objectives, prerequisites and rewards in the Quest Editor.
12 min read
Two tools, two jobs#
The Tools hub has two cards that sound similar but solve different problems:
| Tool | What it makes | Use it for |
|---|---|---|
| Task Manager | A task: a server script that starts on a trigger and then runs OnTick every few ticks until something stops it | Mining, woodcutting, fishing, cooking, any channelled or repeating activity; NPC and object behaviours; world-level timers |
| Quest Editor | A quest: a linear list of stages, each with objectives, that tracks per-player progress and grants rewards | Story lines, tutorial chains, "kill 10 rats then talk to the guard" progression |
A task is code with a data wrapper around it: the Task Manager decides when the script starts and when it is interrupted, and you write what happens on each tick. A quest is pure data: you never write code, and the bundled quest module evaluates objectives from game events. Dialogue is the glue between both of them and the player, so read Dialogue alongside this page.
Both tools live under Tools in the top bar. Click the Task Manager or Quest Editor card; the icon rail on the left switches between tools, and Main Menu returns to the hub. Leaving a tool saves its unsaved changes automatically.
Task Manager#
Layout#
- Tasks (left) — a folder tree of every task, with a Search tasks... box and two + buttons: New Task Folder and New Task. The visible Tasks root is right-clickable. Each task is prefixed with
*when enabled orowhen disabled, and a type tag:[P]player,[N]NPC,[O]object,[W]world. - Task Properties (center) — open tasks appear as tabs (right-click a tab for Close Tab, Close Other Tabs, Close All Tabs). The header holds Save All. Below it are the task's fields, and a Script dock that opens at the bottom when you open the task's script.
- Triggers (right) — the list of things that start the task, a + button to add one, and a Trigger from Script box underneath.
Right-click any task or folder for New Task Here, New Subfolder, Rename, Move to Folder... (tasks only) and Delete. You can also drag a task onto a folder. Folders are real directories under server/scripts/tasks/, so moving or renaming a task moves its script file with it.
Create a gathering task#
- Click + (New Task). In the New Task dialog choose Task Type (Entity or World), and for an entity task the Entity Type (Player, NPC or Object). Type a Task Name such as
Woodcuttingand click Create. Type and entity type are fixed once the task exists; the name becomes the script's class name (spaces and punctuation are stripped, soChop TreebecomesChopTree.cs). - The task opens in a tab. Set Delay (ticks) (1–1000, default 4; four ticks is one second), turn Repeating on or off, and add a Description.
- Under Stop Task On, tick the interruptions that should cancel the task. New player tasks start with all six on: Movement, Take Damage, Death, Teleport, Enter Combat and Equipment Change. NPC tasks show only Movement, Take Damage, Death and Enter Combat; object and world tasks have no interruption flags.
- Optionally set Stop after (ticks):
-1runs until the script or an interruption stops it. - In Triggers, click +, pick Object Click, enter the tree's object id under Object IDs and
chopunder Options. - Click Create Script. The Create Task Script dialog lets you pick which methods to include (OnStart, OnTick, OnStop) and Include TaskState, shows a Preview, and writes the file when you click Create Script. The button relabels to Open Script.
- Click Save All (or
Ctrl+S).
Task fields#
| Field | Saved as | Meaning |
|---|---|---|
| Enabled | enabled | Disabled tasks are loaded but never started |
| Name | name | Display name and the basis of the script class name |
| Type badge | task_type | entity or world (set at creation) |
| Entity badge | entity_type | player, npc or object (entity tasks only) |
| Group | group | Pick skilling, combat, gathering, processing, any group already used by another task, or + Custom... and Add your own (lowercased). See Groups below. |
| Delay (ticks) | delay_ticks | Ticks between OnTick calls; default 4 |
| Repeating | repeating | New tasks are created repeating |
| Description | description | Free text |
| Stop Task On | break_on_movement, break_on_damage, break_on_death, break_on_teleport, break_on_combat, break_on_equip_change | Interruptions; all true on a new task |
| Stop after (ticks) | stop_after_ticks | Omitted from the file when -1 |
| Script | script_path | Relative to server/, e.g. scripts/tasks/gathering/Woodcutting.cs |
| Folder | folder | Sub-folder under scripts/tasks/ |
Triggers#
Which triggers a task can use depends on what it is bound to:
| Trigger | Player | NPC | Object | World | Fields |
|---|---|---|---|---|---|
| Object Click | yes | yes | Object IDs, Options | ||
| NPC Click | yes | yes | NPC IDs, Options | ||
| Item Click | yes | Item IDs, Options | |||
| Command | yes | Commands (e.g. chop, mine; the player types ::chop) | |||
| Location Enter | yes | Locations | |||
| Item on Object | yes | yes | Item IDs, Object IDs | ||
| Item on NPC | yes | yes | Item IDs, NPC IDs | ||
| Item on Item | yes | Item IDs, Target Item IDs | |||
| Player Join | yes | none | |||
| NPC Spawn | yes | NPC IDs (empty = every NPC) | |||
| Object Spawn | yes | Object IDs (empty = every object) | |||
| On Server Startup | yes | none |
Every id and option field is a comma-separated list (1, 5, 12). A trigger fires for every combination of the ids you list, so one trigger can cover a whole family of trees. A task only needs one matching trigger to start; the server stops at the first task that accepts the click.
How a task runs#
When a trigger fires, the server looks up the task, skips it if disabled, and calls the script's OnStart. Returning false there cancels the start. Then it schedules OnTick every Delay ticks (the tick counter starts at 1), and calls OnStop when OnTick returns false, when Stop after is reached, when an interruption flag matches, or when the bound entity leaves the world (logout, despawn). The script receives a TaskContext carrying what triggered it: ctx.Object, ctx.Npc, ctx.ItemId, ctx.Option, ctx.Args (command arguments), and ctx.SecondaryObject, ctx.SecondaryNpc, ctx.SecondaryItemId for item-on-something triggers.
public class Woodcutting : TaskScript<ScriptPlayer>
{
public override bool OnStart(ScriptPlayer player, TaskContext ctx)
{
return true; // return false to prevent starting
}
public override bool OnTick(ScriptPlayer player, int tick, TaskContext ctx)
{
// Called every 4 tick(s) (repeating) -- tick starts at 1
return true; // return false to stop the task
}
public override void OnStop(ScriptPlayer player, TaskContext ctx) { }
}
World tasks extend WorldTaskScript and take no entity; NPC and object tasks receive NPC npc or WorldObject obj instead of the player. With Include TaskState the class gains a nested State class and every method gets a State state parameter; each running invocation gets its own fresh instance, so counters never bleed between two players chopping at once.
Groups decide what a new task cancels. Starting a task cancels any other running task on the same entity that shares its group; a task with no group cancels every task bound to that entity. Put all your gathering skills in skilling so starting to mine stops woodcutting.
Scripts are matched to tasks by script_path, so a script file does not need to know its task id. Task scripts compile individually with the rest of server/scripts/; the Script dock has Save and Open in Script Editor for larger edits. See NPC, object and interaction scripts for the rest of the scripting model.
Files written#
Tasks are saved to server/schemas/Tasks/tasks/tasks_data.json as a JSON array, with a tasks.json schema description written alongside the first time. Scripts live under server/scripts/tasks/.
[
{
"task_id": "task_1775859724811",
"task_type": "entity",
"entity_type": "player",
"name": "Woodcutting",
"group": "skilling",
"folder": "gathering",
"script_path": "scripts/tasks/gathering/Woodcutting.cs",
"delay_ticks": 4,
"repeating": true,
"enabled": true,
"break_on_movement": true,
"break_on_combat": true,
"triggers": [
{ "type": "object_click", "target_ids": "12, 13", "options": "chop" }
]
}
]
The default template ships one disabled example task, Test, bound to scripts/tasks/Player.cs on a Player Join trigger, plus example ::delay, ::countdown, ::pulse, ::walkpattern, ::stoptasks and ::taskinfo commands in scripts/commands/TaskCommands.cs that drive the underlying scheduler directly from a command. Turning the Tasks & Achievements gameplay system off removes these files and the Tasks schema folder; see Templates and gameplay systems.
Quest Editor#
Layout#
The header reminds you that quests are saved to server/schemas/Quests/quests/ and holds the Save button. The left column is the Quests list with a Search quests... box and the + Quest, duplicate and delete buttons; each row shows the name and stage count, prefixed with a circle when the quest is disabled. The right column is the detail form for the selected quest: overview fields, Prerequisites, Stages (n) and Completion rewards.
Create a quest#
- Click + Quest. A quest called "New Quest" with id
new_quest(numbered if that id is taken) and one empty stage is added. - Fill in Name, Quest ID, Description and Category. The id is what dialogue uses in
startQuest("id")and what keys the player's saved progress, so settle it early: changing it later orphans existing progress. - Set Enabled, Repeatable and Auto-start. Auto-start begins the quest at login as soon as its prerequisites are met, which is how you build a tutorial chain. Add a Completion message if you want one.
- Under Prerequisites, add rows with + Required quest (a picker of your other quests), + Required skill level (Skill ID and Level), + Required item (an item id with a live name label, and an amount) or + Required variable (a key, an operator from
==,!=,>=,<=,>,<, and a value). All of them must be true before the quest can start. - Under Stages, edit Stage 1: its name, Journal text (sent to the player when the stage begins) and On complete message. Click + Objective and choose a type (see below). Add more stages with + Add stage and reorder them with the arrow buttons.
- Add Stage rewards per stage and Completion rewards for the whole quest: + Give item, + Take item (turn-in), + XP reward (XP → skill id and amount) and + Set variable.
- Click Save.
Stages run in order, and a stage completes when all of its objectives are met. A stage with no objectives completes immediately, which is handy for a reward-only epilogue.
Objective types#
| Objective | Fields | How it is satisfied |
|---|---|---|
| Talk to NPC | NPC, Count | Counts interactions with that NPC definition |
| Kill NPC | NPC, Count | Counts kills of that NPC by the player |
| Collect item | Item, Amount | Checks the live inventory; items are not consumed unless a reward takes them |
| Use object | Object, Count | Counts interactions with that object definition |
| Enter location | Location | The player enters a named location; matches the name as authored or its lowercased, underscored id |
| Reach skill level | Skill ID, Level | The player's level in that skill is at or above Level |
| Custom variable | key, operator, value | A persistent player variable matches; the escape hatch for scripted or minigame progress |
Every objective also has a Label shown in the quest log; leave it blank to get a generated line such as "Defeat Goblin x5". Skill ids come from your skills configuration. NPC, item and object ids are the definition ids from the Data Editor; the pickers show the matching name next to the number.
Counted objectives are tallied per stage and reset when a stage advances, so a stage 2 "kill five rats" never starts already satisfied by stage 1 kills. Live-state objectives (items, skill level, variables) are re-checked whenever an item arrives, a level is gained or the player logs in, and a stage whose objectives are already satisfied when it begins advances straight away.
How quests are consumed#
The bundled QuestModule (installed by the Quests gameplay system, which requires Dialogue) loads quests_data.json, skips disabled quests, and listens for NPC deaths, NPC and object interactions, item pickups, level-ups, location entries and logins. Progress is stored in the player's persistent variables under q.<quest_id> (stage index, or done) and q.<quest_id>.o<n> (objective counters), so it survives logout and restarts with no extra save code.
Players see quests in the Quests tab: the list orders quests active first, then available, complete, then locked, and the detail popup shows the description, prerequisites with met/unmet marks, live objective progress, rewards, and an Accept button for quests that can be started. Projects created without the Quests system get neither the tab nor the module.
Quests are deliberately linear. Branching belongs in Dialogue, which can start a quest, force-complete a stage, complete or reset a quest from an action node, and branch on questStage("id") >= n, questComplete("id"), questActive("id") or questStarted("id") in a condition node. Server scripts reach the same API through ModuleLoader.Module<QuestModule>() (StartQuest, CompleteStage, CompleteQuest, ResetQuest, GetStage, IsComplete, IsActive).
Files written#
server/schemas/Quests/quests/quests_data.json holds an array of quests; quests.json next to it is the schema description, written once if missing and never overwritten.
| Field | Type | Notes |
|---|---|---|
quest_id | string | Primary key; used by dialogue and progress vars |
name, description, category | string | Shown in the quest log |
enabled | bool | Default true |
repeatable | bool | Default false; a completed non-repeatable quest cannot be started again |
auto_start | bool | Default false |
complete_message | string | Sent when the quest finishes |
prerequisites | object | quests[] (ids), skills[] (skill_id, level), items[] (item_id, amount), vars[] (key, op, value) |
stages[] | array | name, journal_text, complete_message, objectives[], rewards |
stages[].objectives[] | array | type (talk_npc, kill_npc, collect_item, use_object, enter_location, skill_level, var), label, count, plus npc_id / object_id / item_id / skill_id + level / location / key + op + value |
rewards | object | items[], remove_items[] (item_id, amount), xp[] (skill_id, amount), vars[] (key, value) — same shape on each stage and on the quest |
[
{
"quest_id": "rat_problem",
"name": "A Rat Problem",
"enabled": true,
"repeatable": false,
"auto_start": false,
"prerequisites": { "quests": [], "skills": [], "items": [], "vars": [] },
"stages": [
{
"name": "Clear the cellar",
"journal_text": "The innkeeper wants five rats gone.",
"objectives": [ { "type": "kill_npc", "label": "Kill cellar rats", "count": 5, "npc_id": 7 } ],
"rewards": { "items": [], "remove_items": [], "xp": [], "vars": [] }
}
],
"rewards": { "items": [ { "item_id": 3, "amount": 50 } ], "remove_items": [], "xp": [], "vars": [] }
}
]
With AI (MCP)#
define_task— writes or replaces one task intasks_data.json:task_id,name,task_type,entity_type,group,delay_ticks,stop_after_ticks,repeating, thebreak_on_*flags and a non-emptytriggerslist whose types must suit the binding.script_pathis relative toserver/. Pair it withwrite_server_scriptusingkind: "task"to author the script, thengenerate_script_projectif you want to compile-check before launch.- Quests have no dedicated tool;
set_schema_rowwith tablequestswrites a row driven by thequests.jsonfield definitions, andget_schema_datareads what is there.
See the tools reference for full argument lists.
