User Tools

Site Tools


user:cavesomething:guide_to_quest_dialogs

Dialog-Driven Quests

The following is a guide to creating your own dialog-driven quest.

By dialog-driven, I mean that most (but not all) of the events in the quest occur from talking to NPCs.

Dialogues can be used without having any associated quests at all, likewise you can create a quest without having any dialog, but you will find it much easier if you use dialogue to drive a narrative forward.

The first section of this guide covers dialogues.

Dialogs

Dialogs are specified using a JSON syntax, we'll have a simple example first, followed by a general explanation of all the options:

Example

{
  "location" : "friendlypeep",
  "rules": [
  {
  "match" : ["*"],
  "pre" : [["token", "seenbefore", "yes"], ["item", "money", "100"]],
  "post": [],
  "msg" : ["Hello again $you, you are rich."]
  },{
  "match" : ["*"],
  "pre" : [["token", "seenbefore", "yes"]],
  "post": [],
  "msg" : ["Hello again $you."]
  },{
  "include" : ["filewithmoredialog1.msg"]
  },{
  "match" : ["*"],
  "pre" : [],
  "post": [["settoken", "seenbefore", "yes"]],
  "msg" : ["Hello I'm $me."]
  },{
  "include" : ["filewithmoredialog2.msg", "filewithmoredialog3.msg"]
  }
]}

Taking it apart

Each potential line of dialog forms a 'rule' these rules are checked in sequence, from the top of the file down. Note that because of this, the most restrictive rule (with the most pre conditions) should go first. In the example above, the rules in filewithmoredialog2.msg and filewithmoredialog3.msg can never be matched because there is a rule which matches everything before they are reached. (They are however, included in the list of rules to check)

Each rule has one of two forms:

A message rule

These rules contain something that the NPC should say, they contain the following elements:

  • 'match' - Required - This is what the player has said to the NPC - These ones are all * so will match on anything the player might say.
  • 'pre' - Required - Conditions which have to be met in order to match a rule, all of the pre-conditions must be true.
  • 'post' - Required - Actions which take place after a rule has been matched these are things that happen after
  • 'msg' - Required - This is what the NPC will say.
  • 'suggests' - Optional - These are responses that the client can give which 'make sense'. The client should permit other responses not on this list.
  • 'requires' - Optional - These are a list from the responses that the client must give. The client may choose not to permit other responses that are not on this list.
  • 'comment' - Optional - This has no meaning, you can put whatever you want as a comment, this is recommended for very complex dialog.

Any other types of element will be ignored and generate a warning when the file is checked.

An include rule

These rules determine whether to include a file which can contain additional rules. These included files can also include more files, there is no theoretical limit to this, although you probably don't want to go more than 3 or 4 levels down just because it becomes a little impractical then.

The following elements are allowed.

  • 'include' - Required This is followed by a list of files that should be included
  • 'pre' - Optional This is the set of conditions under which this file will be included, it is checked in the same way as for a message rule.

Any other types of element will be ignored this does mean you can put in something like a 'comment': element to explain why some really complex rules are structured the way they are, this is recommended for very complex dialog.

Rule 1

We'll look at the topmost rule first.

  {
  "match" : ["*"],
  "pre" : [["token", "seenbefore", "yes"], ["item", "money", "100"]],
  "post": [],
  "msg" : ["Hello again $you, you are rich."]
  }

There are two conditions we must meet.

The first condition is:

["token", "seenbefore", "yes"]

This checks that the token “seenbefore” has been set to yes. Tokens are set in dialogs by the 'post' actions, if this is your first time speaking to this character, it won't have been set yet.

The second condition is:

["item", "money", "100"]

This checks if the player has enough of a certain item, first there is the item to check for, then the number required. “Money” is a special case, it checks for silver, gold or platinum, up to the required value in silver. (so in this instance 2 platinum would meet the target, as would 1 platinum, 4 gold and 10 silver)

There are no post actions for this rule, so now we look at the message:

  "msg" : ["Hello again $you, you are rich."]

This is the text that the NPC will say to the player. $you is replaced by the name of the player

Rule 2

Rule 2 is similar to rule 1, except that the item condition is missing, as such it will trigger when the token is present, but the amount of money held by the player is less than 100

Rule 3

{
  "match" : ["*"],
  "pre" : [],
  "post": [["settoken", "seenbefore", "yes"]],
  "msg" : ["Hello I'm $me."]
  }

Rule 3 has no pre block, and matches *, so it will always work if one of the other rules hasn't triggered first (that is why it is the last rule to be checked).

Here we have a 'post' block which sets the token 'seenbefore' to yes, this means that the next time that we speak to this character, the check [“token”, “seenbefore”, “yes”] will pass and one of the first two rules will be used.

Tokens are stored in the player file, under the “location” which was specified at the top of the file. Any dialog files which point to the same location have access to the same tokens (and can affect the values they hold). Two tokens with the same name but in different locations are treated separately.

Finally there is the message block where $me is replaced by the name of the NPC.

Rule 4

{
include" : ["filewithmoredialog2.msg", "filewithmoredialog3.msg"]
}

This is an include rule. The include section lists the files that should be opened to get more dialog rules, any rules in these files will be added to the list of rules that is actually evaluated.

There are two files listed here, rules from them will be added in order from left to right.

There is no 'pre' block for this rule, so these files will always be added.

Full List of options

The options that can be used vary in the different blocks:

The Include Block

If a rule has an include block, then either:

  • This should be the only block
  • There should be only the include block and a pre block.

No other types of block will do anything when an include block is present, so they should not be used.

The include block itself should list one or more files.

eg

"include": ["gateguardcommon.msg"]

Each of these files should contain additional rules that the npc should follow.

If a pre block is included, then all of the checks in the pre block must pass for the files to be included, otherwise they are ignored.

Included files can include more files, there is no limit to the depth of recursion, but do be careful not to get a situation where file A includes file B and file B includes file A, the python interpreter should stop that running away indefinitely, but your speech will not work correctly.

The Match Block

This holds the text that the player should say before we check if the rule applies.

There can be one or multiple words specified, or “*” which matches anything. eg

"match" : ["gork","hoard","yes","want"]

This will match any out of “gork”, “hoard”, “yes” or “want”

The Pre Block

In the Pre Block, the following checks are supported:

  • Quest
  • QuestDone
  • Item
  • Token
  • NPCToken
  • Level
  • Age
  • ArchInInventory
  • KnowledgeKnown

Quest

Quest is followed by two fields, the name of the quest, and the step that the player must be at or past. eg

["quest", "testquest", "5"]

Checks that the player has reached at least step 5 of the quest 'testquest'

Have a look at the 'Quest' Section for an explanation of the 'steps'

QuestDone

QuestDone is followed by the name of the quest. eg

["questdone", "testquest"]

Will match if the player already completed the quest at least once, whatever the current state is now. Useful to for instance give a big reward the first time, and less things if the quest is done again.

Item

Item is followed by the name of the item to check for, and optionally the number that are required.

eg

["item", "testquesttarget"]
or
["item", "money", "100"]

The first example looks to see if the player is holding at least one item called “testquesttarget”, the second looks to see if he is holding at least 100 “money”.

“Money” is a special case, it checks for silver, gold or platinum, up to the required value in silver. (so if you look for 100 money then 2 platinum would meet the target, as would 1 platinum, 4 gold and 10 silver, or 1 gold and 90 silver, etc)

The number of items required may be omitted, if so it will look for '1' of the item. The number may also be set to '0', this checks that the player does not have the item.

Token

Checks for Token to have a value from the list of values provided. eg

["token", "pester", "1"]

Checks whether the token “pester” holds the value “1” There can be more than 1 value provided.

Tokens are stored in the player file, under the “location” which was specified at the top of the file. Any dialog files which point to the same location have access to the same tokens (and can affect the values they hold). Two tokens with the same name but in different locations are treated separately.

NPCToken

This works like Token above, except the value will be saved in the NPC itself. Thus if the map is reset, this token will be lost.

This is useful to store temporary data that can be lost without harm, like a conversation state if the conversation can restart.

Level

Checks if the player is at or above the specified level,

"pre" : [["level", "10"]],

Will trigger if the player is at least at level 10, and will not trigger if they are below it. While you can use this to block of entire quest lines until certain levels, it is probably better in general to use this test to bypass a warning about being too weak.

Age

This checks if the date of a time marker is older than the specified age.

there are 6 things you will want to set.

The marker name, which should've been set with 'marktime' (see the post section) then the time to wait in

  • years
  • months
  • Days
  • Hours
  • Minutes
"pre" : [["age", "mymarker", "0", "0", "1", "10", "0"]],

is true if the marker “mymarker” was last marked as being set at least 1 day and 10 hours ago.

This time is in game time, which is accelerated from real-time, it is also the time defined in the game world. In the crossfire game world:

  • 1 Year has 17 Months
  • 1 Month has 35 Days
  • 1 Day has 28 hours
  • 1 Hour has 60 minutes

Any attempt to check for an bigger numbers than these will be silently rewritten in use - ie,

"pre" : [["age", "mymarker", "0", "0", "36", "0", "0"]],

gets treated the same as:

"pre" : [["age", "mymarker", "0", "1", "1", "0", "0"]],

Although it would be better to use the second version because it is easier to compare to other age checks.

ArchInInventory

This check evaluates to true if the player has in her inventory an item with the specified archetype name. eg

["archininventory", "skill_inscription"]

will evaluate to true if the player has the inscription skill.

KnowledgeKnown

This check evaluates to true if the player knows, through the knowledge system, the specified knowledge item.

["knowledgeknown", "item"]

item is a special format, describing the knowledge item to check for.

Example: alchemy:1:3829:the wise will check for the water of the wise alchemy formulae.

Random

TODO

  • 'random' [chance] [seed] - should trigger chance% of the time. This should be generated against the object ID of the NPC (which won't change between resets), seed should be used to control the group of NPCs that are selected (so that two rules with the same chance and seed number will trigger on the same NPCs). Likewise, changing chance but not seed should increase or decrease the number of people included, but not reroll them (so you could have “chance” “20” “43” to tell a character about something, then “chance” “10” “43” to give more useful information on further questioning). '-seed' should invert the selection of 'seed' - more for convenience than anything else.

The Post Block

After the Pre block is used to decide whether a message should trigger, the post block holds the consequences.

There are 9 different options here:

  • quest
  • giveitem
  • givecontents
  • takeitem
  • settoken
  • setnpctoken
  • connection
  • marktime
  • animate

quest

This sets the named quest to the stage which is specified.

eg:

["quest", "testquest" "5"]

Moves the player to stage 5 in “testquest”

If the quest hasn't been started yet, this also starts the quest.

Note that you can not set a quest to a stage which is earlier than the one it is currently at.

In order to prevent that happening, you should have an earlier rule which checks for the stage you want to advance to, typically this not going to do much other than remind the player what already happened.

eg

  "match" : ["*"],
  "pre" : [["quest", "testquest" "5"]],
  "post": [],
  "msg" : ["I've already opened the door."]
  },{
  "match" : ["*"],
  "pre" : [["quest", "testquest" "4"]],
  "post": [["connection", "4"], ["quest", "testquest" "5"]],
  "msg" : ["I'll just open the door for you."]

The first rule triggers when we are already at stage 5, the second rule when we are at stage 4, and then moves us to stage 5

giveitem

Gives the player an item or items from the NPCs inventory.

eg

["giveitem", "biccie", "2"]
or
["giveitem", "money", "74"]

The first of these gives the player a stack of 2 'biccie's - There must be at least one such item in the NPC's inventory already for this to work. This item may be customised in any manner you want in the map editor. It does not matter how many biccies the NPC actually has, one is enough; when they give items, they create copies of the ones they already have.

The second of these gives the player 74 money, made up of the smallest possible number of platinum, gold and silver coins. The NPC does not need to have money in his inventory in order to give it.

The name should be the name of the item to be given, or money. The number may be omitted, if so it is treated as 1.

Note that items which are given away are in the NPCs inventory, so if the NPC is killed, they will drop. If you don't want to be able to acquire these items through killing the NPC, then you should create an 'NPC_Gift_Box' item in the NPCs inventory, and put the items in there. This is checked before the rest of the NPCs inventory is, so if you want to give an item that is similar to one that might be generated from the NPCs treasure list, then it is probably better to put that in their also.

You will particularly want to use the NPC_Gift_Box if you have any easily accessible NPCs who are rewarding inaccessible quests. (eg, someone in a town who asks you to fetch something from a very deep dungeon).

givecontents

This is like giveitem, except that it gives whatever is in the container specified, rather than the container.

You should use this when:

  • There are a lot of items you want to give at the same time
  • You want to use a treasure list to create an item, you won't know what items will be generated exactly, but by putting it in a container, you can give the player whatever is in there.

Warning: You probably Don't want to use a treasure list that contains monsters, it'll sort of work, but don't do it.

takeitem

Removes the given number of an item from the inventory of the player.

eg

["takeitem","testquesttarget"]

Removes one instance of an item called testquesttarget from the players inventory.

You may specify “money” and coins will be removed to the correct value. You may omit the number, and 1 will be assumed. You may specify '0', which will take all of the items that the player has. - Be careful with this, while you can specify [“takeitem”, “money”, “0”], you probably shouldn't.

Note that you will need to check that the player has the item(s) to take first, as such you should always use this in the following way:

  "match" : ["*"],
  "pre" : [["item", "testquesttarget"]]
  "post": [["takeitem","testquesttarget"]]

That is to say, having a matching item check before removing the item.

settoken

Sets the specified token to the specified value, other rules can query this value using 'token' in the pre block.

Tokens are stored in the player file, under the “location” which was specified at the top of the file. Any dialog files which point to the same location have access to the same tokens (and can affect the values they hold). Two tokens with the same name but in different locations are treated separately.

NB: Tokens and time markers are actually the same thing, so if you need to reset a timemarker, then you can use settoken, and set it to 0, if you do, all age checks against the marker will fail until it is set again.

setnpctoken

Same as settoken, except the token is set in the NPC itself, and will be lost eg if the map resets. Tokens are player-specific, so no collision can occur between players.

Useful to store transitory data, like a conversation state.

Tokens are checked with npctoken in the pre conditions.

connection

Triggers the specified connection on the map

eg

["connection", "4"]

This allows NPCs to appear to do things for the player.

eg

{
  "match" : ["*"],
  "pre" : [["quest", "testquest" "4"]],
  "post": [["connection", "4"], ["quest", "testquest" "5"]],
  "msg" : ["I'll just open the door for you."]
  }

will give the impression the NPC has opened a door as long as there is a door with connection 4 set in the map editor.

marktime

Stores the current server time in a time token with the name specified.

eg

["marktime", "mymarker"]

Stores the current time in “mymarker”, you can then check this with 'age' in a 'pre' block to check how long has passed since that point.

This can be used for putting time limits on parts of dialog/quests etc.

animate

Start an animation. The only argument is the path to the animation file.

eg

["animate", "/test/quest_handling/ninja.animation"]

FIXME explain, describe animation in its own page

The msg Block

This is send directly to the player, if the pre conditions are matched, with the following exceptions: $you is replaced by the name of the player $me is replaced by the name of the NPC \n is replaced by a new line character.

Obviously, it probably doesn't make sense to use $you on the first line of dialogue that will be spoken, unless there is a story-based reason for the NPC to know who the player is.

Note also that you can expect clients to wrap message text for you, the reason to use \n is if you use

some blank space

for emphasis.

The replies Block

This lets you specify replies that will be suggested to the player.

The block should be a list of items in the form [word, text, type], with word the actual word the player should say, text the text the player will actually say if she says the word, type an optional integer to specify if the text is a regular sentence (0), a reply (1) or a question to ask (2).

Sanity Checking

Ok, so now you have finished writing your dialog file, the next step will be testing it. You can do this with a local server and watch for error messages that it spits out, but you can also save yourself some time by checking the dialog file beforehand.

There is a script in maps/python/dialog called check_dialog.py which can do this for you.

run it against your dialogue file, like this: maps/python/dialog $ python dialog_check.py ../../scorn/shops/smith.msg and you should see an output like this: checked 12 rules from file ../../scorn/shops/smith_finished.msg Found 0 errors and 0 warnings checked 47 rules from file ../../scorn/shops/smith.msg Found 0 errors and 0 warnings

If there are errors in the dialog file, this will pick them up and allow you to correct them before needing to run a server and speak to the character the dialog is bound to.

NB This script only checks for grammatical errors in your dialog files, there is no check that the rules you have set up actually make sense, nor is there any checking for the order of your rules, it is up to you to ensure your rules do what you want when you want.

Quest Definition

Quests are defined in a text file, in a similar way to archetypes. There are a collection of fields that may be used to express different properties of the quest.

The following is an example of a quest definition.

quest scorn/GorksTreasure
title Gorks Treasure Hoard
description
Gain access to Gork's treasure
end_description
restart 1
step 10
description
I have been told that there is a troll in town called Gork, who guards a stash of treasure, it may be worth paying him a visit.
end_description
end_step
step 20
description
I have spoken to a peaceful, but distinctly unpleasant troll called Gork, who is guarding a stash of treasure. Maybe if I continue to speak with him I can find a way to get access to his stash.
end_description
end_step
step 30
description
Gork has indicated that his friend Mork can also open the treasure room. Maybe if I can find Mork he will be more agreeable than Gork is
end_description
end_step
step 40
description
I have spoken to Mork, and he has given me the key to Mork's treasure room
end_description
end_step
step 50
finishes_quest
description
I have used Mork's key to gain access to Gork's treasure room.
end_description
end_step
end_quest

Again, I'll take this apart step by step:

The header

quest scorn/GorksTreasure title Gorks Treasure Hoard description Gain access to Gork's treasure end_description restart 1

There are a number of entries here, I'll cover them in the order they appear, then explain the other options this quest doesn't use.

quest

The start of any quest is the 'quest' line, which gives the name the quest is known by internally. This is a mandatory entry Good practice is to set up quests as region/questname -

title

The title provides the name that the players will know the quest by - This is optional (but recommended)

description

'description' appears on a line on its own on a line on its own, everything that follows until a line containing 'end_description' is a summary of the purpose of the quest overall. - This is optional (but recommended for non-hidden quests)

restart

either 0 or 1. Whether it is permitted for a player to play through this quest multiple times. 1 indicates a restartable quest, 0 a non-restartable one. If constructing a quest which is restartable, then you should be careful of items like keys, which might be able to be kept between attempts allowing parts of the quest to be bypassed. consider also the use of a marktime/age combination in a conversation to stop the quest restarting immediately.

Other options:

hidden

TODO: Indicates that the quest is tracked, but that updates are not sent to the player. You can use this to create convenience quests which silently track progress in other quests.

party

TODO: define how the quest should interact with party membership, options are:

  • solo - No members of the player's party can assist with this quest at all.
  • assist - members of the player's party can choose to assist with this quest, and can trigger updates for the player if they are on the same map.
  • team - members of the player's party can choose to work with the player on this quest, and any updates triggered act for all players if they are on the same map. (regardless of what the state was for non-triggering players)
  • globalteam - members of the player's party can choose to work with the player on this quest, and any updates triggered act for all players regardless of where they are in the world. (regardless of what the state was for non-triggering players)

Members of a party can find quests to help their party members with by using the 'party quest' command TODO Not implemented yet.

The Quest Steps

A quest step is a single 'state' within a quest. Any given player may only be in one state in a quest at any one time.

A quest step looks like this:

step 40
finishes_quest
description
I have found another way through the gate.
end_description
setwhen
scorn/PortGate finished
scorn/CityGate finished
scorn/ScornHero <=10
end_setwhen
end_step

entries are as follows:

step

Followed by a number, this marks the start of the step definition, which continues until you reach the next end_step The step numbers are what you will need to use to refer to this quest in dialogs and other events. It is good practice (though not required) to number steps 10,20,30, etc, that way if it is necessary to add additional steps at a later date, it will not be necessary to hunt through maps and dialogs for existing step numbers to change.

finishes_quest

If this line is present in the quest step, then being advanced to this step causes the quest to be marked as 'complete.'

setwhen

This is used in a block, until 'end_setwhen' appears every line in between contains a condition in the form of a quest and a stage requirement.

Every time a quest is updated, all other quests are checked to see if the conditions to advance are met.

A condition can take one of the following forms:

questcode n (the quest questcode must be at step n) questcode ⇐n (the quest questcode must not be beyond step n) questcode m-n (the quest questcode must be between steps m and n) questcode finished (the quest questcode must have been completed)

Where m and n are positive integers.

From the example above:

setwhen
scorn/PortGate finished
scorn/CityGate finished
scorn/ScornHero <=10
end_setwhen

This will trigger when the quest scorn/PortGate is finished, and when scorn/CityGate is finished, and when scorn/ScornHero is at step 10 or earlier.

If these conditions are never met at the same time (if the player were beyond step 10 in scorn/ScornHero before completing the other two quests, say) then the player will not advance to step 40 through meeting the conditions. (it is, however, still possible to advance to this stage by some other means, such as a script triggered on a map).

Quests will not go to an earlier step than the one they are on currently as a result of conditions, and when several steps meet the conditions to advance at the same time, then they will only advance once, to the latest (biggest numbered) step where all conditions match.

Warning

Advancing a quest may trigger further quests to advance, in this way it can be possible to have one quest be advanced to several steps sequentially. - However you should be careful with very complex dependancies.

Consider the following example:

quest foo
step 10
finishes_quest
end_step
end_quest

quest bar
step 10
finishes_quest
setwhen
foo finished
end_setwhen
end_step
end_quest

quest baz
step 10
setwhen
foo finished
end_setwhen
end_step
step 20
setwhen
bar finished
end_setwhen
end_step
step 30
setwhen
bar finished
baz 10
end_setwhen
end_step
end_quest

Here quests bar and baz update in response to quest foo, if quest foo is finished, then bar and baz will update, but it is Not Defined which order they will update in.

  • If bar is updated first, then quest baz will go straight to step 20,
  • If baz is updated first, then it will go to step 10, then bar will update, then baz will go to step 30.

ie; The end result will depend on the order in which the quests update, you have no way to know what this will be.

TODO Should be followed by the name of a region, as defined in the regions file; indicates that the region in question is somehow connected to the quest. Once client side interfaces to this are supported, then expect there to be a dropdown - 'show only quests connected to …..'

This should be used when updating to a quest stage would let the player know to go there, not before. (ie, if the NPC says go to 'navar and speak to foo' then link_region navar, if he merely says 'try and find where foo lives and speak to him' then you probably shouldn't link Navar until some other character hints to go to navar - and these would be two separate steps in the quest).

description

descriptions act the same as the description for the quest overall, but have a slightly different meaning. The description of a step should be thought of as a 'what just happened/what should I be doing now' type of text, whereas the quest description is more abstract.

TODO If you have a quest that can be played by a party of people, your description, rather than saying I or me or my, use $I $me and $my, if the player is part of a party, then we, us and our will be substituted in, if they are alone then I, me and my will be. As long as you write in the correct tenses past, perfect or imperative (I think?) then the grammer will be the same.

solo

TODO may appear on its own, and indicates that an update to this step should not apply to other members of the team, this can be used for steps where items are given out so that all players in a team can get the item(s) in question.

Binding dialog and NPC

Once you have created the .msg file and defined the quest, open the map containing the NPC you wish to have interact. Add to its inventory a npc_dialog archetype (it is located in the “system” tab of the “archetypes panel”), and set the name attribute, or the script options, to the .msg file.

That's it, your NPC will interact based on your dialog rules.

Interacting with quests outside of Dialog

We have covered how to handle quest updates in Dialog, and how to define the quests that are updated, but what about updating a quest in response to picking up an object, killing a particular monster or reaching a certain place?

Interaction is based on adding archetypes in the inventory of NPCs, monsters or items you wish to add special behaviour to.

Archetypes are located in the system tab of the “Archetypes” panel of Gridarta.

They need to have their name defined to the quest parameters to match to interact.

item refers to the item the archetype is inserted in, whether a NPC, a monster or another item.

quest_advance_xxx

Advance a quest when event happens and if the conditions match.

The following archetypes exist:

  • quest_advance_apply: the item is applied
  • quest_advance_death: the item (monster) is killed
  • quest_advance_pickup: the item is picked up
  • quest_advance_trigger: the item is triggered

name must be set to questname rule1 rule2 …, with

  • questname the quest's internal name, for instance scorn/CoffeeGoose
  • rule1, rule2, … are state intervals, in the format n-p>q, with n and p defining the interval the quest's step must be, and q the new step to set. -p can be omitted, in which case the state must be the specified value.

Example:

  • name darcap/Elemental-Water 10>20
  • name scorn/GoblinKing 10>20

quest_apply_if

This archetype will prevent its item to be applied unless the conditions match.

name must be set to questname condition1 condition2 … with

  • questname the quest's internal name
  • condition a quest step value, either an interval like n-p or a single value like n

quest_conditional_drop

If inserted in a monster, this archetype will prevent some items this monster owns to be dropped when it dies unless conditions match.

Items from the monster to be conditionally dropped must have a drop_if_quest value set, with the format drop_if_quest questname condition1 condition2 …, with

  • questname the quest's internal name
  • condition a quest step value, either an interval like n-p or a single value like n

The item will be dropped if any condition matches.

quest_prevent_drop

This archetype will prevent its item from being dropped by a player unless the specified quest step is reached. If the item it can be dropped, it will be marked as god-given so it disappears. This is to prevent both players losing important items and cheating by having someone else give said item.

name must be set to questname queststep, queststep being the step from which the item can be dropped.

Low-level description

Note: this section is a technical description of the previous one, and intended for developers only. Map makers shouldn't be concerned by that.

In the map editor there is the option to bind events to objects, you will already have used this to bind event_say to an NPC.

In order to have map-based updates, you will want to bind other events to items.

The following page lists the types of events you can bind to, and describes when they trigger.

http://wiki.metalforge.net/doku.php/server_plugin#hooking_to_an_object-specific_event

For quest building, you will probably find that apply, attack, death, pickup and trigger are the most useful events, although you may find occasional use for other events too.

When you bind events to objects, you need to pick a script to use, so far you will have been using python/dialog/npc_dialog.py, but you can use any script you want.

For general-purpose quest updating the first place to look will be QuestAdvance.py

QuestAdvance.py

QuestAdvance.py should be used as in the following example:

Here we see that the script is bound to event_pickup, which means it will trigger whenever this item is picked up.

First the name of the quest is specified, then the update rules.

An update rule is a step number or range of step numbers and then a step number to move to.

So 0>3 means, if the player is currently at step 0 (ie, haven't started the quest yet) then move them to step 3 1>2 means, if the player is currently at step 1, move them to step 2

if the player were not at either step 0 or 1 in this example, then nothing would happen when the event triggered.

NB: Be careful about binding to event_pickup for items that can be dropped and picked up multiple times, see the next section for how to stop that happening.

QuestEssentialUntil.py

This script should be bound to the drop event of items. It prevents players disposing of quest items in a way that would make the quest impossible to finish. It should be used as in the following example.

Here you have the script bound to an event on the 'mcguffin'. If the player tries to drop the mcguffin and has started the quest 'testquest2' but isn't yet up to step 40, he won't be able to drop the item, if he has passed that stage, he will be able to drop the item, but it will be considered god-given and disappear (in the same way that starting equipment does).

If the player has never started the quest, then:

  1. He should never have been able to get hold of the item
  2. It will disappear when dropped.

QuestApplyIf.py

This script should be bound to an apply event of an item. It prevents players to apply the item unless the specified quest is in specific states.

This is useful to prevent using stairs for instance.

The syntax is: <questname> <rule> <rule> …, with rule being either a precise step like 10, or a step range like 20-40.

QuestTriggerConnect.py

This script is used to trigger connections, like open gates or such, if the player is at a certain quest step.

The syntax is: <questname> <rule> <connection> …, with rule being either a precise step like 10, or a step range like 20-40, and connection the connection code to trigger if the rule matches.

General Principles/Advice

  • If you have a 'takeitem' instruction in a dialog's post block, you should always have a matching 'item' check in the 'pre' block. - possible exception, if you want to take '0' (ie, all) of an item, whether the player has it or not.
  • “takeitem”, “money”, “0” will work, but don't expect it to win you friends.
  • if you are updating a quest as part of a dialog, and you want to only make a quest go forward, not backwards, then you can 'shield' the quest advance rule by putting a similar rule just above it. ie:
{
  "match" : ["cookie"],
  "pre" : [["quest", "cookiequest", "30"]],
  "post" : [],
  "msg" : ["I already gave you a cookie."]
  },{
  "match" : ["cookie"],
  "pre" : [["quest", "cookiequest", "20"]],
  "post" : [["quest", "cookiequest", "30"], ["giveitem", "cookie"]],
  "msg" : ["Well done, have a cookie."]
  }

This will only give the player one cookie when they do the quest (with more possible later if it is a restartable quest.

  • If you have a quest item that the player is given and which they will need later in the quest, then attach the script “python/dialog/questessentialuntil.py” to an event_drop for the object. This will make the player unable to drop the item while they are early on in the quest, and if they pass a stage where it is no longer needed, it will disappear when they drop it. You probably shouldn't use this for a quest reward.
  • If you are using that script on an item, it is probably best that it isn't too heavy, remember a player may need to carry it around for quite a long time before they complete the quest.

You can modify the weight of the item in the map editor.

  • If you are picking a quest item, be sure to pick a type of item that is fairly inert, it is no good making an item undroppable and relying on it to be present if the player can middle click on it and eat it. Keys are quite a good base item to modify from, remember that you can customise the face of an object independently of it's other properties.
  • NPCs will respond to talking within a range of a couple of squares, if you have a map with more than one dialog character on, especially if you have quite complex dialog there, try to space out the NPCs so that you don't have 2 or 3 respond to a player at once.

How Do I?

The following are common things to want to do, and how to go about doing them.

Make an object either be given in a dialogue, or be found on the corpse of the NPC when killed, but not both?

  • First of all, put one copy of the item in an NPC_Gift_Box (this is the copy that will be tied to the dialog), and one on the NPC directly.
  • Make sure that the same messages that cause the item(s) to be given to the player, also set a quest step (if you have no suitable quest, then you can create a simple one-step quest which is hidden TODO )
  • Next, put an event_death listener on the NPC
  • Use the script Script not written yet with the options TODO to remove the copy of the object from the monster's inventory if they are killed by a player who is past a certain stage in the given quest.

Now the player can either kill the NPC and claim the item, or he can talk to him and claim the item, but can not talk to the NPC, get the item, then kill the NPC and get another copy of the item.

Make a quest update on a player reaching a certain point on a map?

Put a checkinv under the floor, set it to match type =1. Now put an event_trigger in the inventory of the check inv and point it at python/dialog/QuestAdvance.py giving the appropriate stage update rules.

Do something really complex that no one has considered before?

Hop on IRC and and ask for advice.

Make the outcome of a quest determine which map an exit leads to (ie, define aftermath maps for quests that players can return to)

I don't know yet, working on it.

user/cavesomething/guide_to_quest_dialogs.txt · Last modified: 2011/10/29 07:06 (external edit)