- Created by Adrian Fulneczek on Apr 11, 2024
You are viewing an old version of this content. View the current version.
Compare with Current View Version History
Version 1 Next »
Here’s how to create a generic monster hunter quest in the Editor. We will mostly be using existing elements.
This tutorial will help you learn some of the basic aspects of quest creation.
It explain how to:
create noticeboards where the player can find the quest giver’s notice,
setup the NPC quest giver (reward_giver) and their dialogues
place an area where the monster will spawn if the player gets close
get the trophy after the monster is dead and collect the reward for it
Please take a look at the HOW-TO: Use quest nodes article since this tutorial will make use of these nodes during the quest creation process.
1. Create a quest file
Open the editor and either loading in a world or creating a new one. Now go to the Asset Browser (CTRL+A) and find your world’s main folder.
Right-click on the background and Create > Quest.
.png?version=1&modificationDate=1712835085722&cacheVersion=1&api=v2&width=829&height=244)
1.1. Quest files
When you open an existing .quest file, they are represented in the editor by quest graphs which consist of different quest nodes. Think of it as a web of interconnected dots which tell the game which action to trigger if something happens or certain conditions are met.
Main quest files for a world can define the game structure. They are used not only for quests, but also for placing in spawns, encounters, conditions and more. It's basically a complete graph of all the content of the world.
Both .quest and .questphase files can be opened and edited in Quest Editor. The main difference between the two is the fact that .quest is the parent file which may contain multiple .questphases within.
2. Game Definition
Basic information about the role and setup of Game Definition files has been previously explained here:
Before we jump into graph editing, we should create a game definition file that will be used to start the game from the context where the quest is supposed to be started, so it’s easier to test it. You can create on using Context menu > Create > R4 game defintion.
Open the definition file and connect the files visible on the screen to their properties (select files in the Asset Browser and then add them in the appropriate slot of the Game Resource Editor).

Game Definition properties:
worlds - links to world/s which are needed for the quest
defaultPlayerTemplate - templates of Geralt and gameplay camera used in game. Geralt’s default player model can be found here: \gameplay\templates\characters\player\player.w2ent.
defaultCameraTemplate - the camera we want to use that will follow the player.
startingPoint - place in world, where Geralt will be spawned after we start game from this definition
mainQuest - link to a quest file, which will be started by this definition
mapPinConfig - it’s best to copy the same one which is used in game\witcher3.redgame
journalRootDirectory - link to journal, helps refer to the journal structure so that our quest journal can be used in the level.
newGameLoadingVideo - what video should be played when the level loads in.
3. Build your quest graph
In the asset browser, go to your world’s quest file and open it.
In the graph editor first we need to create our main input and output nodes by right clicking on the graph editor, Context menu > Complexity management > Start/End
Place in a pause node which will check if the player is already present and will pause the signal until that creatia is not met. Graph context menu > Flow control > Pause. Apply the following settings on the left:
Each pause node can have multiple conditions defined, this time we only need one condition defined. Open the conditions dropdown and hit on the
button.
On the item marked as 0 (first element of the conditions array.) Click on the down arrow (▽) and find the CQuestTagsPresenceCondition. This is responsible to check if an entity marked with the specified tag is present or not.
Since we’re checking for the player’s presence we can use an already defined tag called PLAYER.
(Optional) If you want some specific layers to be hidden or shown, try using the Graph context menu > Game systems control > Hide/Show layers.
But most of the times, we don't need to manually load layers when the game loads. All layers are loaded automatically when the game starts, unless they are specifically marked NOT to load in their properties on the level, in which case that layer folder icon will have an H on it.We can use another pause condition to have a little delay so we can let the world to load in properly before placing in the player.
Now we can teleport the player by using a Teleport node in Context menu > Gameplay > Teleport
Adding a spawn node for the teleport placement:
First, Let’s define where the player will be spawned when loading the game using the definition file. For that, let’s create a layer called start_level by navigating to the Scene panel on the right:and by right clicking on the root, selecting Add layer… in the context menu. In the Create Layer dialog, put in the name of the layer, and select Quest as build Tag and Non-static (dynamic entities). This allows us to identify the layers a little more.
Hit on Create. This will create the layer and automatically highlight it in the scene tree. You can manually activate a layer by double clicking it, the currently activated layer will be highlighted. You can only place in objects or create new ones within an active layer.
Let’s place in the spawn point where the player will be placed. For that hover your mouse to the main viewport and Choose Context menu > Gameplay > SpawnPoint > Add Spawn point.
Notice that a CEntity class component called spawnpoint0 has appeared in the layer.
You can also show a visual representation of the spawn point by applying the following settings in View > Settings > Editor options > Debug and make sure that the checkbox called Waypoints is toggled.
Select the spawnpoint0 object in the scene tree and head over to Properties tab. The only things that are important for us now is to set the tags fields with player_sp. This will be used as reference later.
In the options we can see that it needs the location tag that we specified earlier in our spawnpoint. So go ahead and give the exact same value in there. Also it needs the actor’s tag that will be teleported to the location, yet again here we can use the PLAYER tag.Try out if it works by using the game definition file and loading in the world. In the asset browser, navigate to your world’s folder and select the game definition file we created earlier.
, then navigate to the main viewport’s game load option next to the main toolbar:
Using the left arrow button (⇐) you can append the definition list with your selected game definition.
Now clicking on the joystick button on the left will put the editor in gameplay mode and load in the world using the context that we provided in our quest graph.
Let’s open up the quest file of the level, and make a phase node using Complexity Management > Phase. Phase nodes can contain other sub graphs of nodes so we can maintain our overall structure a little easier. The phase node we created will contain all our quests related to our world, so let’s name it Quests.
Open it up and you will see that you have an empty graph with an in and out socket pair. Notice the top left corner where we now have two rectangles instead of one, meaning that now we are one level deeper in to our overall node structure:
You can navigate back to the parent level by double clicking on the background of the graph editor.
Again create a phase node with a name what you like to have for your quest, ( In this scenario the name will be QUEST - mh_fogling) and make sure to connect the input to the phase input pin like so:

Now open it and let’s start building the quest itself.
I tried to separate this quest to multiple steps of nodes so it is easier to read, but you can build up these phases whatever order or structure you want:
QUEST mh_fogling / Setup phase:
First, we need to decide how we want to expose our quest to the player. There are a few ways that this can happen:
Put a note on a noticeboard
Place an NPC that verbally sings the player to approach him/her to help
clues
etc.
Of course there’s a small chance that our player accidentally stumbles on our monster, that’s why we need to calculate that outcome as well (killing the main quest monster without picking up a notice or talk to any NPCs.)
In our case we’ll allow the player to get this quest from either the notice board or from the NPC quest giver. So let’s put these into our graph.
We need to spawn the NPC who will take the role of our reward giver. For this we will need a Story phase setter node (Gameplay > Story phase setter). These nodes have spawnsets as arguments where we can specify the path to the community file and the phase where we want to initiate the spawn. You can read more about communities in other parts of this documentation.
To create a community file we need to open up our level folder in the asset browser and create a new community in a folder where you feel appropriate. (e.g.: /level_assets/quests/mh_fogling/mh_fogling_reward_giver.w2comm)
Open the file and you should see an empty row in the Spawnset tab. Right click on each square separator and click on add element to activate the gray sections of the record row. There are a lot of columns that can be specified, but right now we will only focus to fill out these ones:
Entity Template: the path of the NPC’s template we will use an already made asset from quests\generic_quests\no_mans_land\quest_files\mh108_fogling\characters\mh108_reward_giver.w2ent
Entity Spawn Tags: This tag is used to reference this entity from almost anywhere in our editor, use something like mh_fogling_reward_giver.
Initializers (Entity Initializers): Give a value by right clicking and choose create object.
Using Initializers is optional, They only need to be used when working with Encounters. Or to add some extra settings to the spawned NPCs.
Please make sure that here you set the correct type of Initializers since there are different types of them in different columns. If you hover over the column headers you will be able to see the specific type of the variable that it resembles in a tooltip!
Story Phase Name - Since a community file can define multiple spawnset elements, we need to set phases where each spawnset element will use one phase. Later in our quest graph we can reference this phase so that only the elements that are using it will be spawned. We can type in a value like “Default”.
Time/Quantity/Respawn Delay/Respawn - Also be careful; We need to “Add Element” to the Time and not leave it as blank.

CCommunityInitializers element should be inserted in the cell. Now open it by double clicking and a dialog box will appear. Add an element to the initializers array field and search for CSpawnTreeInitializerGuardArea then choose it. Now the inserted fields can be ignored except the guardAreaTag. Here we will choose a tag for our guard area where the NPC will be contained.
So Go ahead open your world and place a guard area where you want to place the reward giver NPC. Make sure that you give the tag that you specified in the above community file.
Story Phase Name: Here you can specify in which phase the NPC should spawn. We want to spawn him as soon as the player enters the world so we can choose Default.
Timetable Name: This step is optional. Timetables are good for specifying the NPCs daily routines, in which layer - in what times - in what category class and what action points are associated with our NPC. Timetables are not always needed. But for the sake of this tutorial, I show what can be done with this:
AP tags are good if you want to specify some job to the NPC like sitting around or hammering something until you start talk with it. (not needed for our quest.)
Spawn Point Tags: The tag reference to the Spawn point/Way point where the NPC will be placed exactly when the layer will load up.
mh_fogling_reward_giver_sp
Go ahead and place a spawn point inside the previously marked guard area with this tag.
Back to our quest graph, put the path of your newly created community file into the spawnset of the Story phase setter node, and select Default in to the phase field. Now if you set the quest field inside the game definition properties of your world and launch the world starting with that game definition (for more details search up the Game definition part of this documentation.) You should see the NPC spawned in.
Next let’s put in a notice board where the player can read what’s the note that the NPC left there. Put an additional Script node next to the Story phase setter node and connect the setter’s output into the new Script’s input like so:
In the functionName field search for AddErrandsToTheNoticeBoard and select it.
boardTag: tag reference to the notice board.
errandStringKey: the localization string key for the contents of the notice. Basically the id of the text that’s displayed when you open the notice to read it.
newQuestFact: Facts are used in a Database called FactDB. All variables (called facts) can be found and be used globally for conditions, checks, etc.
addedItemName: The item that’s getting into the player’s inventory when he selects the contract from the board.
Some of the values are from already made assets for the sake of this tutorial being simple.
Again, Fire up the asset browser and search for notice_board_template. Add a Non_static + quest tagged layer into the scene panel and put this template into that layer. You can place this board wherever you want.
Go ahead and play the definition again, you should be able to use the board and see the contract of the quest on it.
Like in most of the quest the reward giver is usually highlighted on the map so the player can easily recognise it among the other NPCs. These highlights are called Mappin-s. For this we’re going to use another Script called EnableDynamicMappin.
Script arguments:
tag: the reference tag of the thing or NPC which should be highlighted.
type: what type of highlight should be used. (there are a lot of types, we’re going to use the EDM_QuestAvailable enum, which is the exclamation mark that is being used for the purpose to highlight quest NPCs.)
There are other scripts like EnableGlossaryImageOrverrideQuest which can update the player’s Glossary record for a certain monster. Monster hunter quests tend to use a two step reveal for the special monsters where at the start of the quest only a dark figure will appear with little info about the monster and then after the player gets the trophy the glossary will unlock and reveal the full description and image of that monster. In this example we are only at the start of the quest so now we can only get a blurry image for the wanted monster:
That will be enough for the setup phase. Make sure to connect the output socket of the last node to the ‘Out’ output of our phase, then step out of the phase by double clicking on the background.
QUEST mh_fogling / Contract phase:
In our new contract phase we will be focusing on the ways how the quest can start. This can be done by taking the job notice from the board or by meeting the quest giver NPC.
Quest progression is done by using Journal - quest nodes, where each node is referencing an entry of the overall quest. Quests are made out of entries and entries can be specified into different types like (Description, Investigation and Contract entries). For better understanding of these groups our quest entry structure is the following:
Quest - Hunt: Fogling:
Descriptions:
Start Noticeboard
Start Peat Worker
Fogling
End Short
End Long
Investigation:
Investigate strange fog
Investigate killings in the fog
Find ancient fogling’s lair, beware…
Explore fogling’s lair
Use Eye of Nephalem to dispel the illusion.
Contract:
Talk with peat digger
Kill the ancient fogling
Get ancient fogling’s trophy
Collect reward for the ancient fogling
Check Burrows village noticeboard
Return in a week to get bigger reward
Collect your full reward from quest giver..
So the quest nodes can be used to Activate, Deactivate, Succeed or fail a quest entry at certain conditions and points in our graph to progress our quest state.
Now that we know this let’s handle the case when the player gets the notice from the board. Put a pause node to setup a condition from which the signal can only continue if the specified condition(s) are met: (Flow control > Pause )
This node takes an array of conditions as arguments so you can specify more than one condition. Right now we only want to check if the fact mh108_job_taken flag is equal to one meaning our player have taken the contract from the board so insert a new CQuestFactsDBCondition element into this array.
The signal comes through because the player took the contract from the notice board let’s put a (Journal > Quest) node.
questEntry: The entry that we will modify with our node (activate / deactivate / success / fail)
showInfoOnScreen: Shows on hud that the quest was updated.
track: If the quest updates then it will set it as the tracked quest for the player.
Set the mappin to highlight the NPC and save the game with an autosave (checkpoint).
Now we can define what happens if the player decides to meet with the quest giver after taking the contract from the board. For that, create a phase node and step inside it:
QUEST mh_fogling / Contract phase / QuestGiver phase:
Notice that there’s a quest entry named ‘Talk with peat digger’ so that the quest can tell the player to go talk to the NPC. We can do this by activating it with a quest node like so:
The player approaches the NPC for a chat. For that we’ll use an interaction node. (Scenes > Interaction Dialog). Using this node we can add a scene from our asset browser which will be played with the specified actorTags when the player talks to the NPC. This tutorial uses this specific scene quests\generic_quests\no_mans_land\quest_files\mh108_fogling\scenes\mh108_reward.w2scene
But you can choose whatever scene you want.

We want to loop the conversation so it can be repeated, this can be done by the following condition within a pause node:
If the player is finished talking with our guy, we can go ahead and close this entry of the quest saying (‘Talk with peat digger’ entry succeeded)
At each quest entry node, you can specify whether you want to fire a notification, track the quest automatically or auto save the game when the quest progresses with these properties:

Let’s say that our fellow player is unlucky enough to stumble into the monster without even talking to the NPC beforehand. For that we’re going to use a pause CQuestActorCondition with a checkType of W3QuestCond_IsTargetOf condition where the monster targets the player.
In this case the entry of talking with the NPC will fail like so:

Jumping back to our QUEST mh_fogling / Contract phase:
So we handled the case where the player visits the notice board and then the quest giver. Now we only need to handle what happens if the player only approaches the quest giver. For that we don’t have to do anything new that the previously mentioned methods:
We setup a simple interaction dialog (in this example in a phase node)
then enable a dynamic mappin
save the game
Finally we can modify a fact for the factDB so we can note that the player took the job. This can be done by using (Game systems control > FactsDB Change) We the arguments of the factID what we want to modify, and the new value, which now will be 1.
Now after that’s done, here is the complete Contract phase:
QUEST mh_fogling / Combat phase:
In this phase we will specify how the quest spawns the monster that needs to be hunted, what happens in combat and after combat (like taking the trophy of the monster etc.)
Spawning of course can be done the same way we spawned our quest giver, in an instant after the level is loaded at the start of playing our game definition. But let’s make our monster spawn a little more interesting with a simple trigger. Let’s say that we only want to spawn down the monster when the player gets inside a trigger area to make it feel like a more sudden encounter. In our world scene viewport let’s place in a trigger area using context menu > Gameplay > Area > Trigger Area. Scale it a little bigger, and give a reference tag to it.
As we previously needed a spawnpoint for the player, this time we need another spawnpoint for the monster. So go ahead and use Gameplay > SpawnPoint > Add Spawnpoint in the viewport and place it somewhere inside the trigger area. It’s important to give this spawnpoint a tag that can be later referenced in the quest graph. You can name the tag however you want, the only important thing to remember is that the tag value needs to be the same inside the community that will later spawn the monster with a story phase setter node. We’ll talk about this in a moment. So go ahead and give a tag to the spawnpoint properties: mh108_ancient_fogling_wp.
Getting back to the quest graph, place a pause condition with a CQuestInsideTriggerCondition using this trigger’s tag as argument. This condition node will only let the signal pass if the player gets inside the trigger area.
Now we can use a previously described Story phase setter with a community file for the monster. We’re going to use an already defined community located at quests\generic_quests\no_mans_land\quest_files\mh108_fogling\spawnsets\mh108_ancient_fogling.w2comm. (You can also use your very own custom community if you know what you’re doing. ) When opening up the community editor, you can see the tag reference inside it that matches the spawnpoint tag we defined earlier:



Spawn Point Tags: This is refering to the spawnpoint (using it’s tag reference) where the entity should be spawned at.
Entity Spawn Tags: This is the tag value which the entity will be marked with once it’s spawned, so that later after the spawn we can reference it from the quest graph.
Save the community once you finished editing it, and paste it’s reference into a story phase setter like so:

Since the community already had a phase defined we can use that in the phase field. (But you can edit the community file with some other phase if you’d like to.)
After the monster was spawned we need to make sure that it will be hostile as well to the player. This can be done with a script right? Yes, indeed but be careful here. Spawning an NPC is not done instantaneously all the time. We need to make sure that the spawn has finished entirely. For that we can check it’s presence; We can use a presence condition like so:
This also references the monster by a tag reference. This reference was also defined with the community’s Entity Spawn Tag.

Assign attitude to a hostile group (monsters):
call AssignNPCGroupAttitudeQuest script.

Another way to do this is to call SetAttitudeTowardsNPC script

At this point the player will be attacked by the monster.
Given that the player killed the monster we can setup a condition that will check if that’s done
Put a FactDB change that the fogling was killed (mh108_fogling_died = 1)
For the is killed condition we can use a CQuestFightCondition:

Now that the monster is dead it will Drop it’s loot specified in the inventory tab of it’s entity template. Make sure that you generated navmesh under the area where the monster spawns. If there’s no valid navmesh under the monster when it dies it won’t drop down its loot therefore the player cannot get the trophy.
We can make a pause condition whether the player looted the trophy out already:
After that we can play the scene where Geralt decapitates the monster’s head, for this we need a script that sets up the actors positions for the scene:

Then we can play the scene of beheading:
Then call another script called ProcessMonsterHuntTrophyQuest which is will move the looted trophy item to the horse’s inventory thus making the trophy appear on the horse saddle

, and finally fade out from the scene.
QUEST mh_fogling / Combat Journal phase:
In here we will make a few checks related to combat and passing them will make the quest progress further. Basically we will use the same fogling_is_killed and player_has_trophy checks what we used previously at combat node, but this time these checks are only for quest progression.
QUEST mh_fogling / Collect Reward phase:
The only thing whats left is to handle the part when the player already took care of the monster, has the trophy and only need to get back to the quest giver to say that the job is done.
Progress the quest by notifying the player to collect the reward for the monster.
As you might have guessed, we need an interaction dialog but this time the scene has a new dialog option (hopefully) to get the reward.
using this scene: quests\generic_quests\no_mans_land\quest_files\mh108_fogling\scenes\mh108_reward.w2scene
To get the reward dialog option, the player needs the trophy at hand!
You might be wondering where does the dialog check whether the player has the trophy or not? The answer is that dialogs also have graphs with conditions like this. Open the interaction dialog scene editor by double clicking on the dialog node. Notice that next to the Timeline tab there is a Graph tab, let’s select that.
Notice the choice node (mh108_choice) with the text “I’m here to collect the reward.” We can see that the signal goes from that socket and gets put into a condition that checks if a fact is true or not. This is the exact condition needed to make the reward collect option available to our NPC.
Back to our quest graph’s interaction dialog: Notice that the interaction dialog has a Reward output socket. This can be used to attach to a reward node that will reference to the exact reward that was defined for our quest. Go ahead and create a Gameplay > Reward node with the reward name of mh108_final_reward and the targetEntityTag of PLAYER.
Finally, we can put an end to this quest by using a journal quest node and connect the signal to its success socket. Go ahead and choose the root Generic/NML Hunt: Fogling/ quest entry so that the quest itself will be marked as succeeded.
Additionally we can put a FactsDB Change (mh108_done = 1) that the quest is done and save the game with a checkpoint node. After all that we can put the signal to the ‘Out’ socket.
Now the entire phase in one picture should look like this:
Done! Now we have a functional quest in our hands. There is a lot more you can still do to it like investigating clues, place illusion walls that would hide the monster's lair, unlock the monster’s true identity by updating the glossary record of it, etc.. This very basic quest is based on an existing monster hunting quest located in the main quest file of the game.
If you want to learn more about it you can find it in witcher3_quest witcher3_quest graph file > witcher 3 game structure > Sidequests (at the top right) > no_mans_land > QUEST - mh108_fogling
Table of contents:
- No labels