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:

ToolWhat it makesUse it for
Task ManagerA task: a server script that starts on a trigger and then runs OnTick every few ticks until something stops itMining, woodcutting, fishing, cooking, any channelled or repeating activity; NPC and object behaviours; world-level timers
Quest EditorA quest: a linear list of stages, each with objectives, that tracks per-player progress and grants rewardsStory 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 or o when 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#

  1. 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 Woodcutting and 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, so Chop Tree becomes ChopTree.cs).
  2. 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.
  3. 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.
  4. Optionally set Stop after (ticks): -1 runs until the script or an interruption stops it.
  5. In Triggers, click +, pick Object Click, enter the tree's object id under Object IDs and chop under Options.
  6. 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.
  7. Click Save All (or Ctrl+S).

Task fields#

FieldSaved asMeaning
EnabledenabledDisabled tasks are loaded but never started
NamenameDisplay name and the basis of the script class name
Type badgetask_typeentity or world (set at creation)
Entity badgeentity_typeplayer, npc or object (entity tasks only)
GroupgroupPick skilling, combat, gathering, processing, any group already used by another task, or + Custom... and Add your own (lowercased). See Groups below.
Delay (ticks)delay_ticksTicks between OnTick calls; default 4
RepeatingrepeatingNew tasks are created repeating
DescriptiondescriptionFree text
Stop Task Onbreak_on_movement, break_on_damage, break_on_death, break_on_teleport, break_on_combat, break_on_equip_changeInterruptions; all true on a new task
Stop after (ticks)stop_after_ticksOmitted from the file when -1
Scriptscript_pathRelative to server/, e.g. scripts/tasks/gathering/Woodcutting.cs
FolderfolderSub-folder under scripts/tasks/

Triggers#

Which triggers a task can use depends on what it is bound to:

TriggerPlayerNPCObjectWorldFields
Object ClickyesyesObject IDs, Options
NPC ClickyesyesNPC IDs, Options
Item ClickyesItem IDs, Options
CommandyesCommands (e.g. chop, mine; the player types ::chop)
Location EnteryesLocations
Item on ObjectyesyesItem IDs, Object IDs
Item on NPCyesyesItem IDs, NPC IDs
Item on ItemyesItem IDs, Target Item IDs
Player Joinyesnone
NPC SpawnyesNPC IDs (empty = every NPC)
Object SpawnyesObject IDs (empty = every object)
On Server Startupyesnone

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.

C#
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/.

JSON
[
	{
		"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#

  1. Click + Quest. A quest called "New Quest" with id new_quest (numbered if that id is taken) and one empty stage is added.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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#

ObjectiveFieldsHow it is satisfied
Talk to NPCNPC, CountCounts interactions with that NPC definition
Kill NPCNPC, CountCounts kills of that NPC by the player
Collect itemItem, AmountChecks the live inventory; items are not consumed unless a reward takes them
Use objectObject, CountCounts interactions with that object definition
Enter locationLocationThe player enters a named location; matches the name as authored or its lowercased, underscored id
Reach skill levelSkill ID, LevelThe player's level in that skill is at or above Level
Custom variablekey, operator, valueA 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.

FieldTypeNotes
quest_idstringPrimary key; used by dialogue and progress vars
name, description, categorystringShown in the quest log
enabledboolDefault true
repeatableboolDefault false; a completed non-repeatable quest cannot be started again
auto_startboolDefault false
complete_messagestringSent when the quest finishes
prerequisitesobjectquests[] (ids), skills[] (skill_id, level), items[] (item_id, amount), vars[] (key, op, value)
stages[]arrayname, journal_text, complete_message, objectives[], rewards
stages[].objectives[]arraytype (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
rewardsobjectitems[], remove_items[] (item_id, amount), xp[] (skill_id, amount), vars[] (key, value) — same shape on each stage and on the quest
JSON
[
	{
		"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 in tasks_data.json: task_id, name, task_type, entity_type, group, delay_ticks, stop_after_ticks, repeating, the break_on_* flags and a non-empty triggers list whose types must suit the binding. script_path is relative to server/. Pair it with write_server_script using kind: "task" to author the script, then generate_script_project if you want to compile-check before launch.
  • Quests have no dedicated tool; set_schema_row with table quests writes a row driven by the quests.json field definitions, and get_schema_data reads what is there.

See the tools reference for full argument lists.

Spotted a mistake or something missing?Tell us on Discord