Platform Tutorial Defining Quests
From Multiverse
| Contributed by: User:jcanker | Last Tested: 27 Jun 08 | Tested By: jcanker | Tested With: 1.5-Final |
| MARS |
| Mob Server |
| Object Manager |
| Combat Server |
|
Abilities and Combat Plugin • Combat Statistics • Extending the MARS Combat System • Group System • Trainer Plug-in • Professions • Experience System |
| Tutorials |
|
Defining Quests • Scripting Mobs • Defining Items |
| Other Examples |
|
Triggering an Action with a Mouseclick • Scheduled Execution • Working with Sound • Creating a Teleporter |
Contents |
Overview
In this section, you will add quests to your world. Multiverse has a flexible quest system that provides for three types of quests: collection quests, kill quests, and delivery quests. You can also create your own additional types of quests by altering or extending the MARS quest plugin. For instance, you may want to implement group quests, timed quests, or escort quests.
NOTE:
- Kill quests are not yet fully implemented as of version 1.5. This quest type will be implemented in a future release.
- Delivery quests are not explicitly defined as a quest type; instead they are implemented as a variation on the collection quest.
General procedure
To define a quest, do the following:
- Make sure all the objects and mobs required for the quest as well as reward items are defined. For more information, see “Platform Tutorial: Defining Items.” and “Platform Tutorial: Defining Mobs.”
- For a collection quest:
- Define the items to be collected for the quest.
- If the items are to be looted from a mob upon death, ensure that the mobs are defined and that the mob template is configured so that the item will drop and be able to be looted by the player.
- For a kill quest:
- Ensure the mobs to be killed are already defined.
- For a collection quest:
- Define the quest attributes. All quests have the following standard attributes:
- Quest Object Instance Name—this is the name of the quest object as it is defined in the code. When referring to the quest within a script or plugin code, you will usually use this name, not the “Name” attribute which is described next. The exception to this is when setting the
setQuestPrereq()attribute of a quest object. While this is technically not an attribute of the object, it is noted here to avoid confusion with the Name attribute. - Name – The title of the quest, such as “Culling the Wolfpack.” This name is the title as the player will see the quest. When setting the
setQuestPrereq()attribute, refer to the quest using this name, not the Quest Object Instance Name. We will see examples of this later. - Description – A detailed description of the quest. Usually this includes some story element describing why the player is doing this quest and might also include some hints as to how to accomplish the task.
- Objective – The summary of what the player needs to do, for example: “Collect 7 wolf hides.” You may have more than one objective; each objective is listed separately as we will see in a moment.
- List of Rewards – A list of item template references (passed as a String) that describes what rewards the player will receive upon completion.
- Cash Rewards—The amount of money the player will receive--in addition to any defined item rewards-- upon completing the quest.
- Quest Object Instance Name—this is the name of the quest object as it is defined in the code. When referring to the quest within a script or plugin code, you will usually use this name, not the “Name” attribute which is described next. The exception to this is when setting the
- Create one or more NPCs who will start and end the quest. In addition to having the same mob serve as the starting and ending contact for a quest, you may choose to have one mob start a quest but have a different mob be the completion point. You may also choose to have the same quest start or end with multiple mobs, meaning that the same quest is available from different mobs in different locations of your world. How to define these choices will be discussed later in this article.
NOTE: The list of attributes above can be confusing because the attributes: Name, Description, and Objective exist primarily as output to the player’s screen via the quest widget so that he can know what he must do for the quest. The attributes Name, Description, and Objective are not involved in calculating the quest's completion state. For example, the attribute Objective is part of the text that the player reads in the quest window when he must decide to accept or decline the quest. As we will see in a minute, you define this in mobserver.py using setObjective(String s). This does not actually define the quest objectives for the code, however. To set the objectives in the code, you will set CollectionGoal(String, Int). You must be careful to ensure that the text of your quest objective sent to the player’s quest widget matches the setCollectionGoal used to calculate completion of the quest within the plugin code.
Define a collection quest
In a collection quest a player must acquire a certain number each of a declared set of items. For example, one quest included in the Sampleworld is named “Get some skins.” This quest is defined inside %MV_HOME%\config\sampleworld\mobserver.py as an instance named collect.Quest.
Note:Don’t confuse this quest’s object instance name (collectQuest) with the fact that it’s a collection-type quest. It just happens to be named collectQuest. You could name it skinBones if you wanted to, provided you updated the references of collectQuest to skinBones.
In this example we will set up a collection quest using items and mob display contexts included with the Sampleworld. Because we will be using existing objects we won’t need to define any special items in Sampleworld\templates.py.
- Define the Quest
- Create an object factory that will create our NPC, Border Guard Klam.
- Create the Template for the NPC Quest Giver
- Configure Mob to Drop Necessary Lootable Item
- Configure Mob's Object Factory to Initialize on Startup:
- Restart Server
Quests are defined in sampleworld\mobserver.py. Add the following code, just before the final line which reads: log.debug(“done with mobserver.py”):
toArmsQuest = MarsCollectionQuest()
toArmsQuest.setName("We Need Some Arms!")
toArmsQuest.setName("We Need Some Arms!")
toArmsQuest.setDesc("I don't know what they expect of me. They keep sending me troops \
but they don't outfit them first! Here, take these swords over to the Quartermaster \
and tell her next time they'll be fighting with their bare fists!")
toArmsQuest.setObjective("Recover 10 swords from the zombie invaders")
toArmsQuest.addQuestPrereq("Welcome Ashore")
toArmsQuest.addCollectionGoal(MarsCollectionQuest.CollectionGoal("Bronze Longsword", 10))
toArmsQuest.addReward("Leather Boots")
toArmsQuest.setCashReward(1000)
This code creates a new quest (toArmsQuest) which the player will see as “We Need Some Arms!” The player must collect 10 bronze longswords. Note that there is a quest prerequisite: The player must first have completed the "Welcome Ashore" quest before this quest will become available. When the player finishes the quest, he will receive a pair of leather boots. There is also a cash reward of 1000 units, although this doesn't really affect our player until there are some additional changes made to the 1.5 platform--money has not yet been fully addressed.
Object Factories are also defined in mobserver.py. Add this object factory to sampleworld\mobserver.py:
class GuardKlamFactory (ObjectFactory):
def makeObject(self, spawnData, instanceOid, loc):
obj = ObjectFactory.makeObject(self, spawnData, instanceOid, loc)
klamLoc = InstanceClient.getMarkerPoint(instanceOid, "wolfmarker")
behav = QuestBehavior()
behav.startsQuest(toArmsQuest)
behav.endsQuest(toArmsQuest)
obj.addBehavior(behav)
roamBehav = RadiusRoamBehavior()
roamBehav.setCenterLoc(klamLoc)
roamBehav.setRadius(20000)
obj.addBehavior(BaseBehavior())
obj.addBehavior(roamBehav)
return obj
ObjectFactory.register("GuardKlamFactory", GuardKlamFactory("Border Guard Klam"))
This object factory creates an object instance and sets it to use the "Border Guard Klam" template when it registers the object in the last line. Notice that this is where you configure the NPC object to use quest behavior and define those quest behaviors as starting and ending the toArmsQuest which you just defined in the last step. This example uses the existing marker named "wolfmarker" from Sampleworld, but you could use any named marker. This marker places the NPC among all the wolves and spawns Klam nearby the existing quest givers for Sampleworld. This code sample also makes it so that Klam is running around among the wolves. If you'd rather she not do that and instead stand still like the other sample NPC's, then remove the behavior attributes assigned by roamBehav, and simply return the object using return obj
Create a new custom template for Border Guard Klam by adding in this template in sampleworld\templates.py:
#
# Border Guard Klam Template
#
tmpl = Template("Border Guard Klam")
tmpl.put(WorldManagerClient.NAMESPACE,
WorldManagerClient.TEMPL_DISPLAY_CONTEXT,
human_female_base_DC)
tmpl.put(WorldManagerClient.NAMESPACE,
WorldManagerClient.TEMPL_OBJECT_TYPE,
ObjectTypes.mob)
tmpl.put(InventoryClient.NAMESPACE,
InventoryClient.TEMPL_ITEMS,
"*Leather Tunic; *Leather Pants; *Leather Boots")
ObjectManagerClient.registerTemplate(tmpl)
Note that this is simply using the same template as the existing NPCs, who also give quests, but this template is renamed to match what we told the GuardKlamFactory object factory to use when it registers the object. NOTE:At the time of this writing, you cannot copy and alter the Female Leather Trainer template--it will not allow you to open the quest widget and accept the quest. Use one of the basic NPC templates instead.
Next, ensure that the zombies will in fact drop the bronze longsword as a lootable object. Find the zombie template in sampleworld\templates.py. The zombies in this template are configured to drop a sword, but it's not the sword that is needed for the quest. Find the line:
tmpl.put(InventoryClient.NAMESPACE, InventoryClient.TEMPL_ITEMS, "*sword8")
and replace the *sword8 with the item we need for the quest, the Bronze Longsword:
tmpl.put(InventoryClient.NAMESPACE, InventoryClient.TEMPL_ITEMS, "Bronze Longsword")
NOTE:This sword will drop in every instance. If you want to create a system with a random drop, you will need to configure your own plugin.
Last, we must tell the server to initialize the Object Factory for Klam and actually create an instance of her upon startup. This is done in sampleworld\instance-load.py. Add the following code block to instance-load.py:
guardKlamMarker = instance.getMarker("wolfmarker").clone()
guardKlamMarker.getPoint().setY(0)
guardKlamMarker.getPoint().add(6000, 0, 0)
spawndata = SpawnData()
spawnData.setFactoryName("GuardKlamFactory")
spawnData.setInstanceOid(Long(instanceOid))
spawnData.setLoc(guardKlamMarker.getPoint())
spawnData.setOrientation(guardKlamMarker.getOrientation())
spawnData.setNumSpawns(1)
spawnData.setSpawnRadius(1)
spawnData.setRespawnTime(5000)
spawnData.setCorpseDespawnTime(0)
MobManagerClient.createSpawnGenerator(spawnData)
Notice that this code block uses the same marker name (wolfmarker) and object factory name (GuardKlamFactory) that we created in the past steps.
After you have saved all your .py files, restart the server and create a character to test the quest. It should only appear after you complete the first available quest, "Welcome Ashore!" Please note that you don't need to hack away at all 10 zombies to test this quest. You can type /createitem Bronze Longsword to create the items in your inventory.
Additional notes on collection quests
You can assign multiple NPCs to be the start and/or end point of the same quest. Simply configure the behavior startsQuest() or endsQuest() on those mobs. This allows you to make the same quest available to players in completely different areas of your world.
Use collection quests to create "Talk to" type of quests. "Welcome Ashore!" is one of these types of quests. Simply define a collection quest that does not use any setCollectionGoal(). Set the endsQuest() behavior on the NPC that the player needs to speak to in order to end the quest.
Define a kill quest
A kill quest requires a player character to kill a certain number of mobs of a certain type.
In addition to standard quest attributes, a kill quest has an attribute that determines the number and name of the creature to be killed. The name must be a unique identifier. To define a kill quest for your world, follow these steps:
- Edit the file
multiverse/config/sampleworld/mobserver.py. - Add this code to define the standard quest attributes:
wolfQuest = MarsKillQuest() wolfQuest.setName("Kill The Wolves") wolfQuest.setDesc("Please kill 2 wolves for me.") wolfQuest.setObjective("Kill 2 Wolves") wolfQuest.setCashReward(1000) wolfQuest.addReward("Leather Tunic") wolfQuest.addQuestPrereq("Supply the Guards") - After the above code, add the following line to define the kill goal, in this case to kill two wolves:
wolfQuest.addKillGoal(MarsKillQuest.KillGoal("Wolf", 2)) - Create the NPC quest-giver. After the above code, add the following lines to
mobserver.py:class GuardWolfKill (ObjectFactory): def makeObject(self, loc): obj = ObjectFactory.makeObject(self, loc) behav = QuestBehavior() behav.startsQuest(wolfQuest) # this matches the quest variable behav.endsQuest(wolfQuest) # this matches the quest variable obj.addBehavior(behav) return obj -
Create a spawn generator for the quest giver.
npc1Factory = GuardWolfKill ("Human Female Leather") npc1SpawnGen = SpawnGenerator("npc1 generator") npc1SpawnGen.setObjectFactory(npc1Factory) npc1SpawnGen.setLoc(npc1Loc) npc1SpawnGen.setNumSpawns(1) npc1SpawnGen.setSpawnRadius(1) npc1SpawnGen.setRespawnTime(60000) npc1SpawnGen.activate()
You will also want to create a spawn generator for the the wolves.
Define a delivery quest
A delivery quest is one in which the starting NPC will give the player one or more items that need to be delivered to another NPC. While the MARS class system includes a MarsDeliveryQuest plugin, delivery quests are not actually defined there. Instead, they have been included in the MarsCollectionQuest plugin. Creating a delivery quest is almost identical to creating a collection quest, except that you:
- Define the item that is given to the PC and declare it in the mobserver.py quest definition using
addDeliveryItem(String) - Set the addCollectionGoal() to look for that item and set the quantity to match the quantity given to the player by the NPC.
The example below uses the same toArmsQuest from the Collection Quest example but this time it is configured so that the NPC gives the swords to the player, who must deliver them to another PC:
toArmsQuest = MarsCollectionQuest()
toArmsQuest.setName("We Need Some Arms!")
toArmsQuest.setDesc("I don't know what they expect of me. They keep sending me troops \
but they don't outfit them first! Here, take these swords over to the Quartermaster and \
tell her next time they'll be fighting with their bare fists!")
toArmsQuest.setObjective("Take the swords to the Quartermaster")
toArmsQuest.addQuestPrereq("Welcome Ashore")
toArmsQuest.addCollectionGoal(MarsCollectionQuest.CollectionGoal("Bronze Longsword", 5))
toArmsQuest.addDeliveryItem("Bronze Longsword")
toArmsQuest.addDeliveryItem("Bronze Longsword")
toArmsQuest.addDeliveryItem("Bronze Longsword")
toArmsQuest.addDeliveryItem("Bronze Longsword")
toArmsQuest.addDeliveryItem("Bronze Longsword")
toArmsQuest.addReward("Leather Boots")
Note that you must add each delivery item individually.
