Difference between revisions of "Bethesda Tutorial Papyrus Events and Properties"
imported>Frenchfrog |
imported>Lmstearn |
||
(15 intermediate revisions by 8 users not shown) | |||
Line 3: | Line 3: | ||
{{Tutorial Index | {{Tutorial Index | ||
|series=Scripting | |series=Scripting | ||
|chapter= | |chapter=4 | ||
|Prev=Bethesda_Tutorial_Papyrus_Introduction_to_Properties_and_Functions | |Prev=Bethesda_Tutorial_Papyrus_Introduction_to_Properties_and_Functions | ||
|Next=:Category:Tutorials | |Next=:Category:Tutorials | ||
Line 25: | Line 25: | ||
=Setting the Stage= | =Setting the Stage= | ||
First, let's set up the draugr we want to resurrect. In the editor, open the cell '''LokirsTomb''' and focus your view on the cave area; this is our boss chamber. In the [[Object Window]], navigate to '''Actors>Actor''' - or ''' | First, let's set up the draugr we want to resurrect. In the editor, open the cell '''LokirsTomb''' and focus your view on the cave area; this is our boss chamber. In the [[Object Window]], navigate to '''Actors>Actor''' - or '''All''' - and use the filter to locate '''"LvlDraugrMissileMale"''' and '''"LvlDraugrWarlockMale"'''. Drag and drop one of each into the room. | ||
Placed actors like these | Placed actors like these Draugr start alive (''well, relatively speaking''), and we need them to be dead. Double-click on each of them and check the box marked '''"Starts Dead"'''. | ||
{{WarningBox|Modders familiar with earlier BGS titles take note - simply setting the health of a base actor to zero will no longer cause the actor to begin dead. You must use the "Starts Dead" checkbox as specified here.}} | |||
[[:Category:Getting_Started#Loading_Your_Plugin_in_the_Game|Save your plugin and run it in-game to check things out so far.]] You'll notice that two dead Draugr now lie in ragdoll on the floor of the room. That's what we want for now - but let's head back to the Creation Kit and get them back on their feet. | [[:Category:Getting_Started#Loading_Your_Plugin_in_the_Game|Save your plugin and run it in-game to check things out so far.]] You'll notice that two dead Draugr now lie in ragdoll on the floor of the room. That's what we want for now - but let's head back to the Creation Kit and get them back on their feet. | ||
Line 52: | Line 48: | ||
===Triggering the Activation Event=== | ===Triggering the Activation Event=== | ||
You may not realize it, but our example already uses an event sent to Papyrus. This is how the boss knows when to get out of his sarcophagus. If you've done the [[Bethesda_Tutorial_Traps_and_Prefabs|Ambushes Tutorial]], you may recall that the boss | You may not realize it, but our example already uses an event sent to Papyrus. This is how the boss knows when to get out of his sarcophagus. If you've done the [[Bethesda_Tutorial_Traps_and_Prefabs|Ambushes Tutorial]], you may recall that the boss uses a trigger volume as its Activate Parent. When the player steps into the trigger volume, the trigger activates the boss, causing him to get up. | ||
{{InDepth| There are pre-existing scripts at play here, but here's what's basically happening: | |||
* The player collides with the trigger, causing an [[OnTriggerEnter_-_ObjectReference|onTriggerEnter]] event | * The player collides with the trigger, causing an [[OnTriggerEnter_-_ObjectReference|onTriggerEnter]] event | ||
* A script on trigger sends an Activate event in response to this. | * A script on trigger sends an Activate event in response to this. | ||
* The Draugr boss is an activate child of the trigger, and therefore also receives an activation | * The Draugr boss is an activate child of the trigger, and therefore also receives an activation | ||
* A script on the Draugr boss responds to the Activation by making him climb out of his tomb | * A script on the Draugr boss responds to the Activation by making him climb out of his tomb}} | ||
We can use the same trigger to also send [[OnActivate_-_ObjectReference|Activate]] events to our two dead draugr. | We can use the same trigger to also send [[OnActivate_-_ObjectReference|Activate]] events to our two dead draugr. | ||
Line 73: | Line 65: | ||
# When the Crosshair cursor appears, double-click on the trigger volume. | # When the Crosshair cursor appears, double-click on the trigger volume. | ||
{{WarningBox|If you don't see the trigger, remember to use "'''M'''" to toggle visibility for markers.}} | |||
Both Draugr will now receive an Activate Event when the player enters the trigger. We aren't actually using that event to do anything yet, however. Next you'll write a script that responds to that event. | Both Draugr will now receive an Activate Event when the player enters the trigger. We aren't actually using that event to do anything yet, however. Next you'll write a script that responds to that event. | ||
Line 92: | Line 80: | ||
</source> | </source> | ||
<br> | <br> | ||
{{InDepth| | |||
* '''scriptName LokirsDraugrResurrection''' - This is the name of the script. The name of the script must match the name of the file ''exactly''. | * '''scriptName LokirsDraugrResurrection''' - This is the name of the script. The name of the script must match the name of the file ''exactly''. | ||
* '''extends Actor''' - This script will be placed on an Actor (the Draugr), so it can do any of the things an Actor can do. | * '''extends Actor''' - This script will be placed on an Actor (the Draugr), so it can do any of the things an Actor can do. | ||
* '''{...}''' - The text in braces is just a file description - similar to a comment. It's not required, but it's good practice to have one here. | * '''{...}''' - The text in braces is just a file description - similar to a comment. It's not required, but it's good practice to have one here.}} | ||
<br> | <br> | ||
Line 116: | Line 100: | ||
Next, we need to cast the Reanimate spell on the Draugr when the Activation event is received. To do this, we'll need to create a '''"[[Variables_and_Properties_(Papyrus)|Property]]"'''. In this case, it's a property of the [[Spell_Script|Spell]] type we'll be using. This will allow us to tell the Creation Kit which specific spell to use. | Next, we need to cast the Reanimate spell on the Draugr when the Activation event is received. To do this, we'll need to create a '''"[[Variables_and_Properties_(Papyrus)|Property]]"'''. In this case, it's a property of the [[Spell_Script|Spell]] type we'll be using. This will allow us to tell the Creation Kit which specific spell to use. | ||
{{NewFeature|For those who used the "[[Papyrus_Glossary#L|legacy]]" scripting language in editors for earlier Bethesda tools, this is a significant change. In Papyrus, you can't simply write in the Editor ID of the object you want to manipulate-- you have to use a property. This allows much more flexible and reusable scripts, since each instance of the script can have a different values for its properties. [[Variables_and_Properties_(Papyrus)|This page provides an overview of properties and variables in Papyrus]]}} | |||
Line 135: | Line 115: | ||
</source> | </source> | ||
<br> | <br> | ||
{{InDepth| | |||
* '''Spell''' - Is the property '''[[:Category:Script_Objects|type]]'''. It tells Papyrus that we'll provide it with a [[Spell Script|Spell]] to use. | |||
* '''Spell''' - Is the property '''[[Category:Script_Objects|type]]'''. It tells Papyrus that we'll provide it with a [[Spell Script|Spell]] to use. | |||
* '''property''' - Is a '''[http://en.wikipedia.org/wiki/Reserved_word reserved word]''' this is how we tell Papyrus to expect a new property. | * '''property''' - Is a '''[http://en.wikipedia.org/wiki/Reserved_word reserved word]''' this is how we tell Papyrus to expect a new property. | ||
* '''reanimateSpell''' - This is the name we're giving to our variable. In-game, Papyrus will use our spell whenever we reference this property. | * '''reanimateSpell''' - This is the name we're giving to our variable. In-game, Papyrus will use our spell whenever we reference this property. | ||
* '''auto''' - Another '''reserved word''' - it tells Papyrus to automatically generate 'get' and 'set' functions for the variable. Don't worry about the details here; this usually doesn't matter. | * '''auto''' - Another '''reserved word''' - it tells Papyrus to automatically generate 'get' and 'set' functions for the variable. Don't worry about the details here; this usually doesn't matter. | ||
* '''; ...''' - Semicolons are the comment character in Papyrus. Use these to keep notes on what your script is doing. Anything after a semicolon is ignored by the game, so you can write whatever you want. | * '''; ...''' - Semicolons are the comment character in Papyrus. Use these to keep notes on what your script is doing. Anything after a semicolon is ignored by the game, so you can write whatever you want.}} | ||
We've defined a spell, but it isn't doing anything yet. Spell properites can use the [[Cast_-_Spell|Cast]] function, which we'll use next. Copy the new line of script inside the onActivate event below: | We've defined a spell, but it isn't doing anything yet. Spell properites can use the [[Cast_-_Spell|Cast]] function, which we'll use next. Copy the new line of script inside the onActivate event below: | ||
Line 170: | Line 146: | ||
# Double-click on the script to add it to the Draugr. | # Double-click on the script to add it to the Draugr. | ||
That's attached the script to this reference, but we still need to tell the Creation Kit what spell to cast. We'll be using "dunReanimateSelf", a special non-playable spell created especially for events like this one. | That's attached the script to this reference, but we still need to tell the Creation Kit what spell to cast. We'll be using "dunReanimateSelf", a special non-playable spell created especially for events like this one.<br/> | ||
# Select ''LokirsDraugrResurrection'' in the ''Scripts'' Tab | {{WarningBox|If you do not do what follows, any properties (any external resource) that you use in your script will NOT register correctly. (This is because properties are '''local''' names only). Even if the property has the same name as an existing external object, it will not work properly without this step.}} | ||
# Click'''Properties''' | # Select ''LokirsDraugrResurrection'' in the ''Scripts'' Tab. | ||
# Click '''Properties'''. | |||
# The list that appears only has a single property - ''reanimateSpell''. | # The list that appears only has a single property - ''reanimateSpell''. | ||
# Select reanimateSpell and click '''Edit Value'''. | # Select reanimateSpell and click '''Edit Value'''. | ||
# Notice that this list only displays valid forms - spells, in this case | # Notice that this list only displays valid forms - spells, in this case. | ||
# Select '''dunReanimateSelf'''. Click OK. | # Select '''dunReanimateSelf'''. Click OK. | ||
Line 190: | Line 167: | ||
<source lang="papyrus"> | <source lang="papyrus"> | ||
scriptName LokirsDraugrResurrection extends Actor | |||
{This Script lives on the dead minion draugr in Lokir's Tomb. It handles their resurrection} | |||
Spell property reanimateSpell Auto ; this is the special self-resurrection spell to use | |||
objectReference property myTrigger auto ; This is the reference we are waiting on to send an activate | |||
Event OnActivate(ObjectReference akActionRef) | |||
; I've been activated - see if was my trigger | ; I've been activated - see if was my trigger | ||
if (akActionRef == myTrigger) | if (akActionRef == myTrigger) | ||
; Cast a Reanimate spell on the Draugr | |||
reanimateSpell.Cast(Self, Self) | |||
EndIf | EndIf | ||
EndEvent | |||
</source> | </source> | ||
{{InDepth| | |||
* '''objectReference''' - This is the data type of our new property. | |||
* '''myTrigger''' - As before, this is a name we've chosen for our new property. | |||
* '''if''' - This is the beginning of an '''"if statement'''. If the statement beneath the if is true, then the script will run. Otherwise, Papyrus skips ahead and resumes processing commands after the corresponding '''"endIf"''' below. | |||
* '''(akActionRef <nowiki>==</nowiki> myTrigger)''' - This is our test condition. The "<nowiki>==</nowiki>" is a logical operator which translates to "is equal to". In this case, it's telling the '''"if"''' statement to continue only if ''akActionRef'' and ''myTrigger'' are filled with the same reference. | |||
* '''endIf''' - This is necessary to close any '''"if"''' statement. It's possible to have multiple if statements within each other for more complicated operations, so it becomes important to keep track of your logic using these. When an '''"if"''' statement is beneath another '''"if"''' statement, it is said to be nested.}} | |||
Now, the last thing we need to do is assign a value to our new property. To do so, let's go back to the Creation Kit, and double click either of our two dead Draugr. Get to the scripts tab and select our '''LokirsDraugrResurrection''' script. Then hit properties. Then select our new '''myTrigger''' property and hit "Edit Value". Now, select "Pick Reference in Render Window". Finally, double click our blue activation box that triggers our boss Draugr to come forth from his tomb. | |||
Save the plugin, then go ahead and test it. | |||
You will find that the Draugr will be resurrected only once. The reason why the Draugr aren't resurrected (and the boss Draugr trap triggered) every time the player moves through the activation box is because of a property of the script attached to it called '''doOnce'''. It is probably a good idea to check the script attached to our triggerbox ('''defaultActivateSelf''') out in more detail to see how you can assign default, or automatic, values to properties. | |||
Latest revision as of 06:50, 17 February 2015
Bethesda Tutorial Papyrus Events and Properties | |
---|---|
Scripting Series, Chapter 4 | |
Return to Tutorial Hub | |
Previous Tutorial | Next Tutorial |
Overview[edit | edit source]
This tutorial introduces Papyrus Events and Properties.
You will learn:
- Basic information about Papyrus Events and how they are triggered.
- Basic information about Papyrus Properties, and how to create, fill, and use them.
- How the game interacts with Papyrus by communicating events.
- How Papyrus interacts with the game by performing actions on properties.
This tutorial builds on the events in Lokir's Tomb, the sample dungeon created in the Level Design Tutorials. If you haven't completed that tutorial, you can download a plugin with the finished level, but be aware that this tutorial assumes you are already familiar with the elements introduced there, including things like:
- Basic editor navigation.
- Concepts like Ambushes, Activation, and Activate Parents.
The Plan[edit | edit source]
Currently, when you enter the final chamber in Lokir's Tomb, the boss Draugr rises from his sarcophagus and attacks. Let's make this battle more exciting by introducing some unique scripted elements. We'll start by adding two dead Draugr to the room that we reanimate (with full spell effects) as the boss emerges from his tomb.
Setting the Stage[edit | edit source]
First, let's set up the draugr we want to resurrect. In the editor, open the cell LokirsTomb and focus your view on the cave area; this is our boss chamber. In the Object Window, navigate to Actors>Actor - or All - and use the filter to locate "LvlDraugrMissileMale" and "LvlDraugrWarlockMale". Drag and drop one of each into the room.
Placed actors like these Draugr start alive (well, relatively speaking), and we need them to be dead. Double-click on each of them and check the box marked "Starts Dead".
Save your plugin and run it in-game to check things out so far. You'll notice that two dead Draugr now lie in ragdoll on the floor of the room. That's what we want for now - but let's head back to the Creation Kit and get them back on their feet.
Event Planning[edit | edit source]
Introduction to Events[edit | edit source]
We want these Draugr to be resurrected as the boss emerges from his tomb. That is, we want this to happen in response to an Event.
"Events" are actions or state changes that the game notifies Papyrus about. There are hundreds of different events, such as:
- Interacting with an object or character (pulling a lever, opening a door, looting a body).
- Picking up, equipping, unequipping, or dropping items.
- Entering or leaving a Location.
- Entering or leaving combat, getting hit in combat, dying.
- And much, much more.
Simply put - if we want to react to something in the game, Events are the way to do it.
Triggering the Activation Event[edit | edit source]
You may not realize it, but our example already uses an event sent to Papyrus. This is how the boss knows when to get out of his sarcophagus. If you've done the Ambushes Tutorial, you may recall that the boss uses a trigger volume as its Activate Parent. When the player steps into the trigger volume, the trigger activates the boss, causing him to get up.
There are pre-existing scripts at play here, but here's what's basically happening: - The player collides with the trigger, causing an onTriggerEnter event
- A script on trigger sends an Activate event in response to this.
- The Draugr boss is an activate child of the trigger, and therefore also receives an activation
- A script on the Draugr boss responds to the Activation by making him climb out of his tomb
We can use the same trigger to also send Activate events to our two dead draugr.
For each of the Draugr:
- Double-click on the Draugr to open its properties window.
- Locate and choose the Activate Parents tab.
- Right-click in the empty list and select 'New'. The Activate Ref Selection dialog appears.
- Click 'Select Reference in Render Window'
- When the Crosshair cursor appears, double-click on the trigger volume.
Both Draugr will now receive an Activate Event when the player enters the trigger. We aren't actually using that event to do anything yet, however. Next you'll write a script that responds to that event.
Scripting the Resurrection[edit | edit source]
Initial Setup[edit | edit source]
It's time to start scripting. Open your preferred text editor (we provide setups for Notepad++ and Sublime Text), and create a new file named "LokirsDraugrResurrection.psc".
Start with the following two lines of script:
scriptName LokirsDraugrResurrection extends Actor
{Resurrects the two dead Draugr in Lokir's Tomb.}
We want to tell it to listen for the onActivate event. Activation occurs when another entity, such as the player or an NPC, tries to "use" or "activate" the object. In this case, the script on the ambush trigger will be sending the activation.
scriptName LokirsDraugrResurrection extends Actor
{Resurrects the two dead Draugr in Lokir's Tomb.}
Event OnActivate(ObjectReference akActionRef)
; Cast a Reanimate spell on the Draugr
EndEvent
Casting the Spell[edit | edit source]
Next, we need to cast the Reanimate spell on the Draugr when the Activation event is received. To do this, we'll need to create a "Property". In this case, it's a property of the Spell type we'll be using. This will allow us to tell the Creation Kit which specific spell to use.
For those who used the "legacy" scripting language in editors for earlier Bethesda tools, this is a significant change. In Papyrus, you can't simply write in the Editor ID of the object you want to manipulate-- you have to use a property. This allows much more flexible and reusable scripts, since each instance of the script can have a different values for its properties. This page provides an overview of properties and variables in Papyrus
Begin by "declaring" the Spell property, as below:
scriptName LokirsDraugrResurrection extends Actor
{Resurrects the two dead Draugr in Lokir's Tomb.}
Spell property reanimateSpell auto
EVENT OnActivate(ObjectReference akActionRef)
; Cast a Reanimate spell on the Draugr
EndEVENT
- Spell - Is the property type. It tells Papyrus that we'll provide it with a Spell to use.
- property - Is a reserved word this is how we tell Papyrus to expect a new property.
- reanimateSpell - This is the name we're giving to our variable. In-game, Papyrus will use our spell whenever we reference this property.
- auto - Another reserved word - it tells Papyrus to automatically generate 'get' and 'set' functions for the variable. Don't worry about the details here; this usually doesn't matter.
- ; ... - Semicolons are the comment character in Papyrus. Use these to keep notes on what your script is doing. Anything after a semicolon is ignored by the game, so you can write whatever you want.
We've defined a spell, but it isn't doing anything yet. Spell properites can use the Cast function, which we'll use next. Copy the new line of script inside the onActivate event below:
scriptName LokirsDraugrResurrection extends Actor
{Resurrects the two dead Draugr in Lokir's Tomb.}
Spell property reanimateSpell Auto
EVENT OnActivate(ObjectReference akActionRef)
; Cast a Reanimate spell on the Draugr
reanimateSpell.Cast(Self, Self)
EndEVENT
Like many functions, we have to pass one or more "arguments" to let the function know what to do. In this case, Cast requires a source and a target. For this example, we just needed the draugr to "cast" the spell at itself. The reserved word "Self" is useful in this case; by passing it into both fields, Papyrus automatically knows we want to reference the same entity the script is attached to. In this case, then, "self" is the same as creating a new property for the draugr we'll be resurrecting - only much simpler.
Save and compile the script, then return to the Creation Kit.
Attaching the Script[edit | edit source]
Back in the Editor, double-click on one of the Draugr to open its Properties window, then go to the Scripts tab (you can hit the 'End' key to jump right there; it's the last tab in the list). This is where we'll hook up our script:
- Click Add.
- In the Add script... Window, enter the name of our new script, 'LokirsDraugrResurrection'.
- Double-click on the script to add it to the Draugr.
That's attached the script to this reference, but we still need to tell the Creation Kit what spell to cast. We'll be using "dunReanimateSelf", a special non-playable spell created especially for events like this one.
- Select LokirsDraugrResurrection in the Scripts Tab.
- Click Properties.
- The list that appears only has a single property - reanimateSpell.
- Select reanimateSpell and click Edit Value.
- Notice that this list only displays valid forms - spells, in this case.
- Select dunReanimateSelf. Click OK.
Repeat this process for the other Draugr. Save your work and give it a try in the game!
Debugging the Resurrection[edit | edit source]
At first glance, everything probably worked out as expected. However, if you kept playing, you may have noticed that the resurrection will occur any time the draugr receives an activate event. This includes when the player loots the body! That's not the sort of nasty surprise we intended on! Unexpected bugs like this can be handled using a bit of extra logic, however.
There are a number of ways we could fix this, like making sure the resurrection only happens once by using a control variable. We could also make sure the script ignores activation from the player, but that could still result in accidental re-resurrection by an NPC.
Instead, we'll ensure that the resurrection only occurs when the triggerbox sends the activation. We'll do this by creating another property - an Object Reference this time - which we'll assign to our trigger.
We'll compare this new property to "akActionRef", the parameter supplied by the OnActivate() event. This parameter is a sort of special variable, usable only within this onActivate event, which Papyrus automatically assigns to the reference responsible for the activation.
scriptName LokirsDraugrResurrection extends Actor
{This Script lives on the dead minion draugr in Lokir's Tomb. It handles their resurrection}
Spell property reanimateSpell Auto ; this is the special self-resurrection spell to use
objectReference property myTrigger auto ; This is the reference we are waiting on to send an activate
Event OnActivate(ObjectReference akActionRef)
; I've been activated - see if was my trigger
if (akActionRef == myTrigger)
; Cast a Reanimate spell on the Draugr
reanimateSpell.Cast(Self, Self)
EndIf
EndEvent
Now, the last thing we need to do is assign a value to our new property. To do so, let's go back to the Creation Kit, and double click either of our two dead Draugr. Get to the scripts tab and select our LokirsDraugrResurrection script. Then hit properties. Then select our new myTrigger property and hit "Edit Value". Now, select "Pick Reference in Render Window". Finally, double click our blue activation box that triggers our boss Draugr to come forth from his tomb.
Save the plugin, then go ahead and test it.
You will find that the Draugr will be resurrected only once. The reason why the Draugr aren't resurrected (and the boss Draugr trap triggered) every time the player moves through the activation box is because of a property of the script attached to it called doOnce. It is probably a good idea to check the script attached to our triggerbox (defaultActivateSelf) out in more detail to see how you can assign default, or automatic, values to properties.