Repository navigation
Area Quests
TL;DR: An area quest is a list of steps stored in the area file. Each step names a begin trigger and an end trigger on a mob, object or room, and the game finds the Fenia handlers by method name. The engine tracks every player's step, runs the handlers, pays the declared rewards and calls global scripts at start, step end and completion. Use when: you create or edit an area quest, write step handlers, wonder why a quest never starts or never advances, or read the quest data from a script. Source of truth:
plug-ins/loadsave/areaquestutils.cpp(the engine),src/core/areaquest.h(the data),plug-ins/olc/qedit.cppandaedit.cpp(the editors),plug-ins/feniaroot/areaquestwrapper.cpp(the script API),dreamland_world/fenia.examples/queststep/(handler templates). Verified: 2026-11-02 against dreamland_code 88da8302, dreamland_world 43880467, dreamland_fenia_public c28874b
A quest has two parts.
-
Data in the
<quests>section of the area file: title, description, who may take it, and the ordered steps. The data is static. A script can read it and cannot change it. - Handlers, which are Fenia methods on the prototype of a mob, object or room, named after the quest, the step and the trigger.
On every player event that has a quest trigger (a player greets a mob, speaks, gives an item and so on), the engine looks at the methods defined on the target of the event.
For each quest that has a method on that target:
-
The player is not on the quest. The engine does something only if this target has a step 0 begin method. It checks who may take the quest (see Quest data). If the player passes, it calls that begin method, marks the quest as started (step 0) and calls the global script
onQuestStart. The start does not depend on what the begin method returns. -
The player is on the quest. The engine calls the end method of the player's current step. If that method returns
true, the engine calls the global scriptonQuestStepEnd(which pays the step rewards), moves the player to the next step, and then calls the begin method of that next step. After the last step it marks the quest as completed and callsonQuestComplete.
Facts that follow from this:
- Only an end method that returns
trueadvances. Afalseor missing return keeps the player where they are. - The begin of step N+1 is called from inside the end of step N, on the same mob, object or room, and only if it is the
postform of the same trigger. If it is not, the next step never gets its begin call. When the next begin has the same trigger type but is not thepostform (for exampleonSpeechinstead ofpostSpeech), the log getsaquest: begin/end trigger type mismatch. A different trigger type or a different target is skipped with no log line at all. The quest still advances. - Handlers are found by method name. The trigger fields in the XML are only data for the editor, so renaming a method in a script changes behaviour at once.
- Eligibility (level, class and so on) is checked once, at the start. A player who changes between steps stays on the quest. If a step must re-check something, check it in the handler.
- The quest data of a player (
thisLife, step, times) is read-only from scripts. - NPCs never take part: the engine ignores events whose actor is an NPC. A pet that kills for its master does not count.
- A quest that nobody has touched for the configured number of days is cancelled when its player logs in, through
quest cancelso thatonCancelruns. The flagsonboardingandnoexpireexempt a quest.
<quests>
<node type="AreaQuest">
<vnum>9901</vnum>
<title l="en">The Lost Lantern</title>
<description l="en">The lamplighter has lost his lantern.</description>
<minLevel>5</minLevel>
<maxLevel>101</maxLevel>
<limitPerLife>1</limitPerLife>
<oncePerDay>false</oncePerDay>
<flags></flags>
<prereq>0</prereq>
<steps>
<node type="QuestStep">
<beginType>mob</beginType>
<beginValue>9910</beginValue>
<beginTrigger>postGreet</beginTrigger>
<endType>mob</endType>
<endValue>9910</endValue>
<endTrigger>onSpeech</endTrigger>
<info l="en">The lamplighter asks for help. Tell him you will help.</info>
</node>
<node type="QuestStep">
<beginType>mob</beginType>
<beginValue>9910</beginValue>
<beginTrigger>postSpeech</beginTrigger>
<endType>mob</endType>
<endValue>9910</endValue>
<endTrigger>onGive</endTrigger>
<info l="en">Find the lantern and give it to the lamplighter.</info>
<rewardExp>200</rewardExp>
</node>
</steps>
</node>
</quests>| Field | Meaning |
|---|---|
vnum |
Quest number, inside the vnum range of the area. |
title, description
|
Per language. Shown in the quest list. |
minLevel, maxLevel
|
Level limits. A maxLevel of 100 or more means no upper limit. |
align |
Allowed alignments (good, neutral, evil). |
classes, hometowns
|
Allowed professions and home towns. Empty means all. |
limitPerLife |
How many times per rebirth. 0 or less means no limit. |
oncePerDay |
At most once in 24 hours. |
prereq |
Another quest that must have been completed at least once in this life. |
flags |
hidden (left out of the area help and the quest help listing), onboarding (newbie chain: not offered as a later quest to look for, and no pause after cancelling), nocancel (quest cancel is refused while the quest fits the player's level), noexpire (not cancelled for inactivity). |
lootVnums |
Optional list of item vnums the quest hands out, used by the gear advisor. |
steps |
The steps, in order. Each has the begin and end target, the info text and the rewards. |
A step:
| Field | Meaning |
|---|---|
beginType, beginValue, beginTrigger
|
The target kind (mob, obj, room), its vnum and the trigger that starts the step. |
endType, endValue, endTrigger
|
The same for the end of the step. |
info |
The hint the player reads in quest info, per language. |
rewardExp, rewardGold, rewardQp, rewardVnum
|
Paid when the step ends: experience, gold, quest points, an item. |
Rules the editor warns about:
- The end target of step N and the begin target of step N+1 should be the same mob, object or room.
- The end trigger
onXof step N pairs with the begin triggerpostXof step N+1:onSpeechwithpostSpeech,onGivewithpostGive.
An eligible player has to pass these gates to start: level range, alignment, home town, profession, limitPerLife, oncePerDay, completed prereq, a one hour pause after cancelling the same quest (not for onboarding quests), and the optional quest hook onCanStart. The prereq test is "completed at least once in this life", and since the count resets at rebirth, prereq with limitPerLife 1 gives a clean once-per-life chain.
The quest data cannot be created from a script. Use OLC or edit the area file and boot.
aedit <area number from alist>
quest create
quest create [vnum] takes the first free vnum of the area (or the one you give) and opens qedit. quest delete <vnum> removes a quest. quest <vnum> opens one. qedit <vnum> and qedit list also work from outside.
qedit commands:
| Command | Meaning |
|---|---|
title, uatitle, rutitle
|
Name. |
desc, uadesc, rudesc
|
Description. |
minlevel, maxlevel
|
Level limits. |
align, classes, hometowns
|
Eligibility. |
perlife <number> |
Times per rebirth. -1 means no limit. |
perday y|n |
Once per day. |
prereq <vnum> |
The quest that must be done first. |
flags <names> |
Toggles quest flags (? areaquest_flags). |
fenia <hook> |
Opens the script editor for a quest-level hook. |
step ... |
Step editor, below. |
show, done
|
Redraw, leave. done marks the area changed, so also run asave changed. |
The step command:
step add
step <n> begin|end type mob|obj|room
step <n> begin|end vnum <vnum>
step <n> begin|end trigger <name>
step <n> begin|end fenia clear
step <n> begin|end fenia
step <n> info|uainfo|ruinfo <text>
step <n> qp|gold|exp|item <number>
step <n> up
step <n> down
step <n> del
-
step addmakes a step whose begin copies the end of the previous step withonchanged topost. A first step starts with a greeting from the first mob of the area. -
trigger <name>accepts the triggers that exist for that target kind. The clickable menu offers only the sensible ones: all of them for the begin of step 0, thepostones for the begin of later steps and theonones for an end. -
feniaopens the script editor for the handler of that step and stage, and writes the code to the script store. - Changing
type,vnumortriggerof a stage deletes the script that was attached to it.upanddowndo that for both swapped steps. -
step <n> delremoves the step. It does not remove the scripts: the editor reminds you to clean them up.
A handler is a method on the prototype of the begin or end target, named:
q<quest vnum>_step<step number>_<begin|end>_<trigger>
for example q9901_step0_end_onSpeech.
The triggers below are the ones qedit offers. The arguments are those of the handler.
| Target | Trigger pair | Arguments | When |
|---|---|---|---|
| mob |
onGreet / postGreet
|
(mob, ch) |
A player enters the room, and the mob is awake and can see the player. |
| mob |
onSpeech / postSpeech
|
(mob, ch, speech) |
A player says something in the room of an awake mob, or tells it something from the same room. |
| mob |
onGive / postGive
|
(mob, ch, obj) |
A player gives the mob an item. |
| mob |
onSocial / postSocial
|
(mob, ch, victim, social) |
A player does a social on the mob. |
| mob |
onWake / postWake
|
(mob, ch) |
A player wakes the mob. |
| mob |
onDeath / postDeath
|
(mob, ch) |
The mob dies, ch is the killer. |
| mob |
onSell / postSell
|
(mob, ch, obj, cost, number) |
A player buys from the mob. |
| mob |
onTrain / postTrain
|
(mob, ch, stat) |
A player trains with the mob. |
| mob |
onTeach / postTeach
|
(mob, ch, arg) |
A player practices with the mob. |
| mob |
onPourOut / postPourOut
|
(mob, ch, container, liqname, amount) |
A player pours a liquid on the mob. |
| obj |
onGet / postGet
|
(obj, ch) |
A player picks the item up. |
| obj |
onGive / postGive
|
(obj, ch, mob) |
A player gives the item away. |
| obj |
onPut / postPut
|
(container, ch, item) |
A player puts an item into the container. |
| obj |
onUse / postUse
|
(obj, ch, args) |
A player uses the item. |
| obj |
onSpeech / postSpeech
|
(obj, ch, speech) |
A player says something in a room where the item lies on the floor or is carried (in an inventory or worn). |
| room |
onGreet / postGreet
|
(ch, fromRoom, moveType) |
A player enters the room. |
| room |
onCommand / postCommand
|
(ch, cmdName, cmdArgs) |
A player types any command in the room. |
The engine itself fires get, give, put, use, speech, greet, social, wake, command and death. The sell, train, teach and pour-out triggers are fired by game scripts through the character's trigger method.
How on and post differ:
-
on...runs at once and the engine reads the result:truemeans the step is done. Every end handler is anon..., and so is the begin of step 0 if you choose that form. -
post...is postponed until the current event is finished and its result is ignored. Use it for the begin of steps 1 and up, so that an NPC can speak after the player's own words were already shown.
Rules for handlers:
- A handler has to check its own conditions. The engine calls it for every player event of that kind on that target.
- Return
truefrom an end handler only when the step is really done. - A
posthandler cannot stop the quest from starting. To refuse a player, useonCanStart. - A
posthandler may pause for dramatic effect, but a pause means the mob or player may be gone when it resumes: check again.
A small example for the quest above:
.get_mob_index(9910).q9901_step0_end_onSpeech = function(mob, ch, speech) {
if (speech.match("help|yes|ok"))
return true;
mob.say("Will you help me find my lantern?");
return false;
};
.get_mob_index(9910).q9901_step1_end_onGive = function(mob, ch, obj) {
if (obj.vnum != 9920) {
mob.say("That is not my lantern.");
mob.giveBack(ch, obj);
return false;
}
obj.extract(true);
mob.say("My lantern! Thank you!");
return true;
};Two things to remember for greet-based quests:
- A mob greet runs only for an awake mob that can see the player. A giver in the dark, or one lying down (position
mort,sleep), never starts the quest. Give such a quest to a room greet instead. - The engine marks the quest as started before the mob's own
onGreetruns. A plainonGreeton the quest giver runs together with the quest'spostGreet, so give it a check that it should stay silent.
Only one trigger swallows the plain prototype trigger: when a quest step consumes a give, the prototype's own onGive is skipped. Greet and speech run both.
Methods on the quest object, .AreaQuest(<vnum>). The engine calls them if they exist.
| Hook | Called | Result |
|---|---|---|
onCanStart(ch) |
After all other start checks. | A non-empty string refuses the start and is the reason. An empty string allows it. |
onCanEverStart(ch) |
When listing quests. | A non-empty string says the quest is closed for the rest of this life (for example the one slot is taken). |
onCancel(ch) |
The player cancels the quest. | Clean up temporary mobs or items. |
onInfo(ch) |
quest info shows the current step. |
A string to show instead of the step's info. |
qedit fenia offers onCanStart and onCancel as templates.
Rewards are data. The step fields rewardExp, rewardGold, rewardQp and rewardVnum are paid by the game's global script onQuestStepEnd, after the end handler returns true. A handler does not pay them. For a reward that the player chooses or that depends on a condition, leave the XML field at 0 and give it in the handler.
The engine calls these global scripts: onQuestStart(ch, quest), onQuestStepEnd(ch, quest, stepIndex), onQuestComplete(ch, quest) and onQuestCooldown(ch, quest) (a player tried to start a quest in the one hour pause after cancelling). The shipped scripts show hints, pay rewards and announce completion. A quest with the onboarding flag skips the announcements.
.AreaQuest(vnum) returns the quest and raises an error for an unknown vnum. It only looks quests up. There is no call that creates a quest or changes a step.
| Member | Meaning |
|---|---|
vnum, title, description, minLevel, maxLevel, limitPerLife, oncePerDay, align, classes, hometowns, flags, area
|
The data, read-only. |
getTitle(lang), getDescription(lang)
|
By language: 0 English, 1 Russian, 2 Ukrainian. |
step(n) |
The step as a structure. |
data(ch) |
The player's progress: step, times, thisLife. A step of -1 means not on the quest. Both completing and cancelling set it to -1. |
cancel(ch) |
Cancels for the player and calls onCancel. Returns true if the quest was running. |
info(ch) |
The hint for the player's current step. |
canParticipate(ch), canParticipateEver(ch)
|
Whether the player can start it now, or at any point in life. |
The game's script library also has helpers for the usual handler chores: .tmp.quest.active, .tmp.quest.cancelled, .tmp.quest.completed, .tmp.quest.had, .tmp.quest.lastStep, .tmp.quest.canStart (the standard check at the top of a greeting) and .tmp.quest.cutscene (a pause between lines that cancels the quest if player or mob leave). They are not part of the public Fenia repository. The public utilities include .tmp.mob.canGreet and .tmp.mob.canSpeak, which a giver handler usually calls first. canSpeak is true for an NPC only if its form is sentient or magical.
- Annul before delete. Handlers are stored on the prototype in the script database. Deleting a handler's source file without first setting the method to null leaves the old method behind, and Fenia duplicates the file. Details: Fenia-Scripts.
-
Handlers before data. Posting
q<N>_step...handlers before the quest exists in the area file logsAreaQuest: unknown quest idat every matching event. - The area file is read at boot. A new quest appears after a reboot. Handlers can be hot-loaded once the quest data exists.
-
Quest start is not cancellable by a handler.
qdata.start()runs after the begin method whatever it returns. - Eligibility is checked at the start only.
-
step delandqeditdo not delete handlers. Clean up by searching the script store for the quest vnum. - Charmed pets do not count. A killing blow by a pet earns the master nothing through a quest step.
See also: OLC, OLC-Other-Editors, Fenia-Triggers, Fenia-Scripts, Area-Format.
Start here
Get running
Contribute
- Contribute
- Contributing Workflow
- Code Style
- Accessibility
- Writing Player Text
- Game Design Principles
Build areas
- Building
- OLC
- OLC Rooms
- OLC Mobiles
- OLC Objects
- OLC Resets
- OLC Line Editor
- OLC Other Editors
- Area Format
- Area Quests
- Mob Forms and Parts
Fenia
- Fenia
- Fenia Language
- Fenia Triggers
- Fenia Gotchas
- Fenia API
- Fenia Scripts
- Fenia Behaviors
- Fenia Skills and Spells
- Fenia Cookbook
- Fenia Grammar
Engine
- Engine Architecture
- Engine Time and Pulses
- Output Format
- Mechanics Combat
- Mechanics World
- Data Configs
- Item Power
Localization
Clients
More