Advanced Quest Tutorial

From Project Tamriel Wiki
Jump to navigation Jump to search

Welcome to the second tutorial in the series of learning how to make quests and dialogue, made originally by Kevaar! Here, we will be focusing on combat, creatures and scripting, which allows you to make many more complex quests.

I write this assuming you have already completed my first tutorial, "Kevaar Finds a Shiny". If you haven't, I can't ensure you will understand everything I am talking about here, and can only offer you blank looks or impatient glares for not doing your homework! I will also be writing this tutorial as a series of plugins, or expansions, to the "Kevaar Finds a Shiny" quest made in the first tutorial. This means you will be working off of the completed quest from the first tutorial, so you really ought to go do your homework, don't cha know.

Adding Combat To Your Quest

The first thing I wanted to touch on was combat. Though there are plenty of interesting lore tidbits or whacky NPC personalities to discover just running about town and talking to people, my quests would quickly get rather boring if all of them were done this way. The point of an RPG is to adventure, after all!

So the first thing I want to do is go back to my basic quest design and write in a combat scene. After some thought, here is my updated design:

  1. The player talks to Kevaar, who tells them about a ring he's found. He asks the player to find its owner.
  2. The player asks around town, and discovers the ring belongs to Fargoth. Fargoth asks the player to get the ring back for him.
  3. When the player returns to Kevaar, they find Kevaar has been cornered by a scamp. They must kill the scamp in order to speak with Kevaar.
  4. Kevaar thanks the player for their help, and assures the player he will return the ring to Fargoth by himself.

Next, I look at my quest journal entries. I see I will need at least one more entry to handle this extra quest step. Luckily I remember to put a gap between Indexes 50 and 60 so that I could add more quest steps here. Here is my new shiny (don't tell Kevaar) quest step:

  • Journal Index 52: "I returned to tell Kevaar about the ring's owner, but found him being attacked by a scamp! He can't speak to me until I kill it."

Questtut - combatjournal.png

Creating the Scamp

Next, I want to be sure I have all the Objects I will need to create this quest. My design only has the inclusion of one new Object from my original design, and that is the scamp itself!

To create it, I will first navigate to the Creature window. Since the creature I want to make is of a kind already present ingame, I will scroll down to "scamp", right-click on it, and choose Edit. I get a window that looks like this:

Questtut-addscamp.png

This should look familiar. Creatures are handled in a similar way to NPCs in the game engine, though they will have different stats and a few different functions such as Movement and Blood Texture. For the purpose of my quest, there is very little I need to edit here, as my new creature will just be your ordinary scamp.

Pro tip: Consider when you expect the player to encounter your quest so that you can appropriately level your creatures. Since this is a starter town, dumping even a scamp on the player may be a good recipe for player death, so I might want to consider nerfing the scamp, such as making all of its attacks only 1 - 5 damage, or setting its health to 15. With an eye towards detail, I could then change the scamp's Scale to 0.75 and rename it Stunted Scamp – if only so the player doesn't then go around proudly bragging that they now can defeat scamps without realizing this one was a gimme! See also our difficulty guide.

There is one change I need to make regardless though, and that's creating a new ID, so that my editing of this Creature won't inadvertently cause changes to all scamps game-wide. You should already know how to do this from the previous tutorial, but in case you've forgotten, see below:

Questtut-newobjectscamp.png

Type a new ID into the ID subfield, then click Save. The CS will ask you if you want to create a new Object. Click Yes. The window will close, and I will have to find my scamp and reopen its window. Now I am ready to begin customizing my scamp without fear I might change all scamps present in the game!

While I'm here, I also want to check my scamp's AI package. Since scamps are hostile creatures normally, I will probably not need to change anything here, but adding a Creature for the player to fight only for the Creature to... not fight would be rather anticlimatic so I always want to check!

Questtut-scampai.png

I see in the above image that the scamp's Fight value is set to 90. Good. This means it will attack the player as soon as the player gets close enough. (See our creature guidelines for more information on adding creatures.)

Scripting the Scamp to Appear

To make the scamp appear, we need to delve into scripting. Now there are several ways I could do this, and the choice is yours. I have taken the time to explain the methods and what situations they are best used for. I highly recommend you create a Save of your mod now, so you can go back and try each of the methods on a clean .esp.

Disable Script Method

The first way is through use of a Disable script. In this method, I place the scamp in-game where I want the player to encounter it. I then attach a script to the scamp so that it is disabled (essentially invisible) except during the appropriate quest step. This method is best used for statics (a.k.a. scenery) or other Objects that I want placed in an exact location and position.

The first thing to do is place my scamp where I want it. I want it to attack the player in the Seyda Neen Lighthouse, so I open up the correct interior and place it inside:

Questtut-placescamp.png

Next, I want to give the scamp its disable script. I open up its stats window, then locate the Script subfield. I click the "..." button next to the drop down to write my script:

Questtut-scampscript.png

Begin TR_DisabledScamp

if ( GetDisabled == 1 )
    if ( GetJournalIndex "TR_KevaarShiny" == 50 )
        enable
        Journal "TR_KevaarShiny" 52
    endif
else
    if ( GetJournalIndex "TR_KevaarShiny" < 52 )
        disable
    endif
endif

End

This may look rather scary to a non-coder, but have no fear. In this script, I have simply told the game engine to check if the scamp is Disabled (GetDisabled == 1). If it is, check what stage the quest is on, and if on Index 50, Enable it and update the journal so it doesn't keep trying to enable it. Otherwise, if it is not Disabled, and if the quest is still at a stage where it should be, Disable it.

Below, you can see the same script added in the script window. Press the Save button, circled in red, to save it. In the Creature window, you should now see your new script name active on the scamp.

Questtut-scampscripfinished.png Questtut-scampscriptinplace.png

Pro tip: The script will keep running in a loop for as long as the player is in the same cell as the scamp, checking every thread if the scamp is disabled or not. For the small Seyda Neen Lighthouse cell, this shouldn't cause much of an issue, but if this were in the middle of the city, I might want to consider alternative ways of writing this script so that the computer has one less thing to worry about among so many Objects.

And there we are! The scamp has now been placed and is waiting in Oblivion to appear! I can move on to writing in Kevaar's reaction to it, or, I can reload my saved .esp and try out some of the other methods for making it appear.

PlaceAtMe Method

Another method involves talking to Kevaar first, and does not involve the use of a script attached to the scamp. Instead, I will open up my old friend, the dialogue window. In this method, I want to write this scene as if the player is ambushed by a scamp shortly after speaking to Kevaar.

First, I give Kevaar a new Greeting, to appear only when the player has returned from talking to Fargoth: in other words, during the Journal stage 50.

  • Journal "TR_KevaarShiny" = 50: "Hello! You've come back! Did you find the owner of the ring I found?"

Now, while I could summon the scamp with this Greeting, putting scripts in the Results window of Greetings is risky. As you've seen when creating Greetings, there are many many other Greetings that may override mine depending on the circumstances the player finds themselves in when speaking to Kevaar, and so my quest may not always trigger. To circumvent this, I would either need to put this Greeting among the Greeting 0's or 1's, or I could instead move the triggering script into its own Topic.

To be on the safe side, I will do the latter. The player will be expecting to click on the Topic "ring I found" to advance the quest with Kevaar, and so I can write in a little surprise for the player instead. I start off with writing in a new Response to the "ring I found" Topic:

"That was quick. Are you sure you didn't have any trouble on the way? Wait. What is that? B'Vehk, look behind you!"

Like all Responses, I will want to make sure this is Conditioned properly so it shows up when I want it to. For instance, though I would want to condition this Response with Journal "TR_KevaarShiny" = 50." I already have another Response that is conditioned the exact same way! I would want to make sure this new Response goes on top of the old one, or it would never appear.

Then, to summon the scamp, I will then want to add these three lines into the Results window:

Journal "TR_KevaarShiny" 52
Goodbye
PlaceAtMe, "TR_scamp", 1, 120, 3

The Journal script you have seen before. This updates the quest journal so that it is on the next step of the quest. This serves the additional of role of making sure Kevaar can't keep warning the player about the scamp when the "ring I found" Topic is clicked, and so inadvertently summoning an endless pile of scamps for the player to deal with.

The Goodbye script is also one you have probably seen played out in-game before. This forces the player to end the conversation by clicking "Goodbye", and is often used in conjunction with dialogue that ends in a fight scene, as otherwise the player would be free to chat Kevaar up for hours while the poor scamp is drooling behind them, waiting their turn.

The PlaceAtMe script is then the command that summons the scamp beside Kevaar. The first number is the amount of scamps you want to summon. (While you could summon more than one, they would be stacked on top of each other at first, and hence look rather funny). The second number is how far away from the calling NPC (in this case Kevaar, as it's Kevaar's dialogue) you want the scamp to be. The last number is where in relation to Kevaar you want the scamp to be. 0 is in front, 1 is behind, and 2 and 3 are to the left and right side, respectively.

For the sake of immersion, it's worth noting that PlaceAtMe places an Object immediately, with no flash, bang, or puff of smoke to accompany its sudden appearance. This function is then best used when the object will be placed out of sight of the player, such as when they have their back turned or their nose stuck in a book. You may notice that in my above scheme it's possible the scamp will poof into existence where the player can see it. This is when you might want to also consider using PlaceAtPC instead.

You can do much the same effect using the PlaceAtPC script (the syntax is the same, just "PC" instead of "Me"). The only difference in function is that the object will always appear in relation to the player instead of in relation to the calling Object. However, in a cramped interior like the Lighthouse, this is risky, as the player could be standing with their back to a wall. Though the game engine will make best effort to make sure the scamp is placed in such a way it is still "inside" the interior, placing it right on top of the player if it has to, I have better control of exactly where the scamp appears by using Kevaar as a reference instead. For our quest, it becomes a toss-up of which function you prefer.

Pro tip: To better offset the sudden appearance of the scamp (or any other object) in the player's field of vision, you could make use of PlaySound "sound id" and/or write the line FadeIn, 1 into your Results followed by FadeOut, 1 on the next line. PlaySound plays the given sound once. I could make it seem like the scamp appeared out of Oblivion by using the sound ID "mysticism hit", or that it jumped from a hiding place by using the sound IDs "Body Fall Medium" or "scamp roar" (choose one, for unfortunately without creating a special timer script, both sounds will be played at once). FadeIn/FadeOut makes the whole screen turn black and is a good indicator of the passage of time. However, since it may seem a little strange to insinuate that the player was standing staring slack-jawed at Kevaar for an indeterminate amount of time while the scamp sauntered into position, the PlaySound trick alone is probably the best pick. See the scripting guide and the resources linked there for more information on the possibilities.

As you can see, the PlaceAtMe method of placing the scamp is dependent on the position of Kevaar (or in the case of PlaceAtPC, of the player), and so can move around if Kevaar (or the player) has moved around. This contrasts to the Disable method described above, where you have already told the engine exactly where you want the scamp to appear. I'll leave it up to you to decide which one makes the most thematic sense, and how you may make best use of these methods for future encounters you may script for.

Pro tip: There is actually a third method, PositionCell, X Coord, Y Coord, Z Coord, Rotation, "CellID", that can be used to summon the scamp. Like the Disable method,  PositionCell allows you to place the creature exactly where you want it, and unlike the Disable script, is only called once (usually in a Dialogue Result) and so may be easier on the computer.  This method is technically better suited for moving Objects than summoning them though, as it won't work unless the Object has already been placed in the game world. (In the case of the scamp, this placement could be in a test cell or sectioned off room in the lighthouse that the player otherwise doesn't have access to.) The Object being moved must also have a unique ID, so that the game isn't trying to figure out which guard out of 50 guards you're trying to move.

Finishing Up the Encounter

Now that I have made sure my scamp will be summoned at the appropriate stage of the quest, I want to deal with the aftermath. While most players will kill the scamp and that will be that, there will always be that smart player who tries to magically calm it instead, or the cowardly one who tries to talk to Kevaar anyway with it chewing away on their back. While in some cases this may be appropriate, Kevaar has a particular fear of scamps after walking into a house with over 1,000 of them crammed inside, and wants the thing dead before he can even think of talking to the player again.

Just like summoning the scamp, there are a couple ways I can do this. Once again, you may want to consider saving your mod now so that you can go back and try each method with a clean .esp. These same methods can also be used as general kill counters, such as when giving the player a quest to kill a certain guy for a Morag Tong writ, or become the next Saint Jiub by slaying 5,000 cliffracers. (Boo, post-TES III fan-service!)

First, both methods entail making another special Greeting for Kevaar, intended to block progress while the scamp is still alive. This Greeting will have to go in Greeting 1, right after the Oath of Silence Greeting, so that it can't be overridden by anything else. As usual, I will want to condition it for Kevaar. I will also want to be sure to condition it so it only appears during Journal Index 52. Finally, I want to add a Goodbye script into the Results window, so the player can't convince Kevaar to talk to them anyway. The final Greeting will look thus:

Questtut-finalgreeting.png

The next step is making the game engine aware that the scamp is dead and so the blocked Greeting is no longer necessary. There's a few different methods for determining whether the scamp is dead.

The "Dead" Dialogue-Condition Method

One method of checking for the scamp's death is using the death counter built right into the dialogue window. Opening up the same Greeting I just created above, I will add one more Function/Variable to its conditions. I choose the "Dead" Function, then "TR_scamp" in the next drop-down, then finally < 1:

Questtut-checkdead.png

This means the blocking Greeting only shows when the player hasn't yet killed the scamp. If the player has, the engine skips right past this dialogue, and Kevaar will talk to the player as normal. This is certainly the easiest method, and can also be used when the player must kill more than one type of creature to advance a quest. For instance, maybe not only a scamp attacked, but a clannfear too, and Kevaar also can't stand clannfears because they smell. Or maybe a scamp and a clannfear AND a golden saint attacked, and Kevaar....er. Hold that thought while I retrieve him from the closet...

The OnDeath Script Method

Ahem. The second way to make Kevaar refuse to speak to the player until the scamp dies is to add a script to the scamp. This can be more daunting for new modders, as it involves editing any script I might have placed on the scamp before (such as the Disable script), as well as creating a new journal entry, BUT, because of the journal entry, this method is more useful when killing Creatures or NPCs while out in the field, as the player does not have to return to the questgiver to see if they've succeeded in slaying the right thing yet.

First, I will add a new Journal entry:

  • Journal Index 54: "I killed the scamp. Kevaar has calmed down enough to talk to me now."

Now, just like when I added my Disable script to the scamp, I will want to open up the scamp's attached script by clicking on the "..." button in its statistics window. Then I will want to add the following lines into the script (don't forget to add a Begin Scriptname and End if you are creating a whole new script):

if ( OnDeath == 1 )
    Journal "TR_KevaarShiny" 54
endif

Questtut-scampscriptdead.png

This will update the journal to stage 54 once the game registers the scamp has been killed, once again allowing the player to bypass that blocking Greeting.

Pro tip: The command, GetDeadCount "creature ID", can be used in a similar manner to OnDeath, with the added bonus that it can be put inside any script, not just the local script attached to the creature you're tracking for, as well as track for multiple creatures of the same type being killed. However, caution must be taken with this one, as GetDeadCount will try and count up all the instances the given monster has died in the entire playthrough each thread, much to the detriment of your framerate! For this reason, GetDeadCount should be conditioned with timers or other methods to keep it from trying to run all the time.

Finishing the Quest, Take Two

Finally, I will want to check if Kevaar will now agree to speak to the player after the scamp is slain, by checking the conditions on the rest of his dialogue. And oops, it's a good thing I did! My old "ring I found" topic was set to complete the quest only if the quest journal index equalled 50 exactly! I will need to change this to a new set of conditions:

Journal "TR_KevaarShiny" > 50
Journal "TR_KevaarShiny" < 60

Questtut-finaljournalconditions.png

Adding both of these conditions means the quest finishing dialogue will appear if the current quest journal index is anywhere between 50 and 60, but not 50 and 60 themselves (for remember, 50 is now what triggers the scamp to appear in one of my scamp summoning methods, and 60 is for when the quest has been completed). Whew, problem averted.

If I didn't do so already when testing out the different scamp methods, I now want to playtest my mod, and see if I forgot any other conditions or introduced any other bugs by accident. If everything plays cleanly, then I have successfully added combat to my simple quest!

This is it for this small series of tutorials! For a reference and more information on how to make quests, see the Quest Guidelines. Maybe you are ready to do a showcase for Tamriel Rebuilt or Project Tamriel?