linktree Atrinik.org - Multiplayer Online Role Playing Game
Quest Making Tutorial: #1
Development -> Quest Making Tutorial -> Tutorial #1Quest Making Tutorial: #1

This tutorial explains the simplest of all quests - an NPC that wants you to recover an artifact stolen by some monster.

First off, you start by creating the script file. In order to do this, create a new file named my_quest.py in, say, the maps directory. It can be saved anywhere else, but for the purpose of this tutorial, you should save it as mentioned, or later steps will not work for you. Next, open the created file in your script editor (for example, Notepad++).

Now that you've got the created file open, you can copy paste the following into it:

Code: [Select]
from Atrinik import *
from QuestManager import QuestManager

activator = WhoIsActivator()
me = WhoAmI()
msg = WhatIsMessage().strip().lower()


Let's examine the above. The following line imports the Atrinik Python module -- which contains Atrinik-related Python functions and classes.

Quote
from Atrinik import *


The next line imports the simple quest manager class, which contains the functions necessary for making a quest.

Quote
from QuestManager import QuestManager


The next step is getting the common variables used throughout the whole script; the script's activator - in our case, the player that talks to the quest NPC, me - which is the script owner, that is to say, the NPC and last but not least, the string message that was used to activate the script, for example, playing saying hi, hello, etc to the NPC. WhatIsMessage() acquires the string, strip() trims left and right whitespace ("  hi  " becomes "hi"), lower() transforms the string into lowercase for easier message checking ("Hi" becomes "hi").

Quote
activator = WhoIsActivator()
me = WhoAmI()
msg = WhatIsMessage().strip().lower()


Next part is adding information about the quest.

Code: [Select]


quest = {
"quest_name": "Melanye's Lost Walking Stick",
"type": QUEST_TYPE_KILL_ITEM,
"arch_name": "quaterstaff",
"item_name": "Melanye's Walking Stick",
"kills": 1,
"message": "Melanye in Brynknot Tavern has asked you to bring her back her walking stick, which was stolen in the middle of the night by some evil treant, waking up the old woman, who saw the evil treant running to the east...",
}


The above is called a quest definition -- it defines what the quest is about, its configuration, etc. Below is explanation of elements in such configuration (a dictionary):

  • quest_name: The quest's name -- should be something unique that explains what the quest is about.
  • type: One of:
    • QUEST_TYPE_KILL: Quest that requires you to kill X number of monsters.
    • QUEST_TYPE_KILL_ITEM: Quest that requires you to find X number of items.
    • QUEST_TYPE_SPECIAL: Special quest that can only be completed using special requirements chosen by quest logic, instead of finding items or killing monsters.
  • arch_name: Archetype name of object to find -- teddy, quaterstaff, etc. Only used with quest type QUEST_TYPE_KILL_ITEM.
  • item_name: Actual quest object name -- should be something unique. Only used with quest type QUEST_TYPE_KILL_ITEM.
  • kills: Either number of monsters required to kill for the quest in case of quest type QUEST_TYPE_KILL, or number of items to find in case of QUEST_TYPE_KILL_ITEM (in which case it should be left out if there is only 1 item to find).
  • message: Quest message that appears in the quest list -- should explain what is required to complete the quest, where the quest NPC is and other useful information.


Next we create the NPC dialog and the quest logic. The comments (prefixed with hash symbol: #) explain the logic.

Code: [Select]


# Initializes the quest manager, which allows use to check the quest progress, start it, complete it, etc.
qm = QuestManager(activator, quest)

# The main function which is called every time the quest is ran.
def main():
# This checks for common greetings -- "hi", "hey" and "hello" are the common ones checked.
if msg == "hi" or msg == "hey" or msg == "hello":
# If the player has not started the quest, try to offer it.
if not qm.started():
# Text between <a> and </a> becomes a link that the player can click.
me.SayTo(activator, "\nWell, hello there my dear. I wonder -- have you seen any <a>evil treants</a> around the place?")
# Now check if player has already completed the quest or not.
elif qm.completed():
me.SayTo(activator, "\nThank you for recovering my walking stick and teaching that evil treant a lesson!")
# It wasn't started _or_ completed, so check if it has been finished -- in other
# words; have the criteria for completing the quest been met?
elif qm.finished():
me.SayTo(activator, "\nOh, you found my walking stick! Thank you, thank you. Here, it's all I can spare... I hope it will be useful to you.")
# Give the player 25 silver coins for their trouble.
# Note that this method is rarely used to hand over rewards -- usually
# the reward is found in the NPC's inventory used FindObject(), then cloned
# using Clone() and inserted into the player using InsertInto().
activator.CreateObject("silvercoin", 25)
# Inform the player about the reward. format() is a string method
# which can be used to easily insert numbers and other strings into
# another string.
activator.Write("{0} hands you 25 silver coins.".format(me.name), COLOR_YELLOW)
# Finally, complete the quest, removing the quest item and marking the
# quest as completed.
qm.complete()
# None of the above; so remind the player what needs to be done in order to complete the quest.
else:
me.SayTo(activator, "\nOh... you haven't found my walking stick yet? I'm sure the evil treant ran off to the east of Brynknot...")

# The message wasn't a greeting, so if the quest wasn't started yet, try to give out
# some background information and eventually a way to start the quest itself.
elif not qm.started():
# Background information #1
if msg == "evil treants":
me.SayTo(activator, "\nI was asleep just last night, when a treant broke into my room here at the tavern!\n\n<a>How did you know it was a treant?</a>")
# Background information #2; notice the text is in lowercase and not mixed-case like in the above link.
elif msg == "how did you know it was a treant?":
me.SayTo(activator, "\nWell, it woke me up! I only saw an evil-looking treant running away, holding my enchanted <a>walking stick</a>!")
# Now the NPC nicely asks the player to do the quest.
elif msg == "walking stick":
me.SayTo(activator, "\nMy walking stick has been enchanted to give off a soft glow wherever it goes. That is how I saw that it was a treant, running off to the east... Say, would you look for this treant and recover my walking stick? I might have something in return for your troubles...\n\n<a>Sure</a>")
# And the player accepts...
elif msg == "sure":
me.SayTo(activator, "\nWhy, thank you! I'm sure a strong-looking adventurer like yourself will be able to sort this out in no time at all. Just head east of Brynknot -- that is where the treant ran off to...")
# This is important -- it actually starts the quest.
qm.start()

# Start the dialog.
main()


Now with the above copy-pasted into your file as well, the quest is complete. If you want to use this and change the quest details and the dialog, go ahead; below is a version ready to be copy-pasted, without the explanation comments. Make sure when you're editing that you use tabs for indentation -- otherwise the script won't work.

Code: [Select]
from Atrinik import *
from QuestManager import QuestManager

activator = WhoIsActivator()
me = WhoAmI()
msg = WhatIsMessage().strip().lower()

quest = {
"quest_name": "Melanye's Lost Walking Stick",
"type": QUEST_TYPE_KILL_ITEM,
"arch_name": "quaterstaff",
"item_name": "Melanye's Walking Stick",
"kills": 1,
"message": "Melanye in Brynknot Tavern has asked you to bring her back her walking stick, which was stolen in the middle of the night by some evil treant, waking up the old woman, who saw the evil treant running to the east...",
}

qm = QuestManager(activator, quest)

def main():
if msg == "hi" or msg == "hey" or msg == "hello":
if not qm.started():
me.SayTo(activator, "\nWell, hello there my dear. I wonder -- have you seen any <a>evil treants</a> around the place?")
elif qm.completed():
me.SayTo(activator, "\nThank you for recovering my walking stick and teaching that evil treant a lesson!")
elif qm.finished():
me.SayTo(activator, "\nOh, you found my walking stick! Thank you, thank you. Here, it's all I can spare... I hope it will be useful to you.")
activator.CreateObject("silvercoin", 25)
activator.Write("{0} hands you 25 silver coins.".format(me.name), COLOR_YELLOW)
qm.complete()
else:
me.SayTo(activator, "\nOh... you haven't found my walking stick yet? I'm sure the evil treant ran off to the east of Brynknot...")

elif not qm.started():
if msg == "evil treants":
me.SayTo(activator, "\nI was asleep just last night, when a treant broke into my room here at the tavern!\n\n<a>How did you know it was a treant?</a>")
elif msg == "how did you know it was a treant?":
me.SayTo(activator, "\nWell, it woke me up! I only saw an evil-looking treant running away, holding my enchanted <a>walking stick</a>!")
elif msg == "walking stick":
me.SayTo(activator, "\nMy walking stick has been enchanted to give off a soft glow wherever it goes. That is how I saw that it was a treant, running off to the east... Say, would you look for this treant and recover my walking stick? I might have something in return for your troubles...\n\n<a>Sure</a>")
elif msg == "sure":
me.SayTo(activator, "\nWhy, thank you! I'm sure a strong-looking adventurer like yourself will be able to sort this out in no time at all. Just head east of Brynknot -- that is where the treant ran off to...")
qm.start()

main()


Now you should save the file you copy-pasted the quest into, and use Gridarta to create the quest NPC and the enemy to kill. Here are the step-by-step instructions:

  • Make a new map; name it whatever you want, for example, Quest Test.
  • Go to Archetypes tab in Gridarta, and select floors. In the select-list that says all by default, select flagstone.
  • Click once on any tile on the created map, go to Edit menu at the top and click Flood Fill.
  • Go to special in the Archetypes chooser and select spawn_point and right-click in the top corner of the map to add it there.
  • Now go to mobs tab and select old woman NPC by clicking on it once. Then click the Add Inv button in the area below the map.
  • Now click the old woman picture in the right panel. Go to the special tab again and select Python. Click Add Inv once again.
  • Object with the name Python should now be in the old woman's inventory in the right panel (its image says Event). Double-click it to open its properties.
  • In the opened properties window, click the Special tab. Change event ID to say and change script to say /my_quest.py (if the quest file was saved to the maps directory). You can now also double-click the old woman in the right panel and change her name to Melanye or a name of your choice, but this is not required for the script to function.
  • Repeat the NPC adding procedure, but now instead add the spawn point (and so the monster) to the right corner of the map, and choose evil treant as the monster to add. Do not add the Python object this time, just click the evil treant in the right panel and continue to the next step.
  • With the evil treant added and selected in the right panel, go to the special tab and select quest_container, and use the Add Inv button. Select the added quest_container in the right panel (its image will say Quest), select the items tab and find quaterstaff in the list (alternatively, you can use CTRL+ALT+A to search for quaterstaff) and use the Add Inv button once again.
  • Now double click the quest_container in the right panel to open its properties, switch to the Special and change quest name to Melanye's Lost Walking Stick. If you have changed the quest name in the script file, you will need to change this. Set type of quest to Kill item quest.
  • Double click the quaterstaff in the right panel to open its properties, scroll up and change its name to Melanye's Walking Stick. Again, if you changed this in the script, you will have to change it here as well.
  • You are done! Press CTRL+s to save the map and type my_quest. Now you can start up your local server, log in, and use this DM command to go test your quest: /goto /my_quest 1 1.