Difference between revisions of "Introduction à Papyrus/fr"
imported>Ereksen m |
imported>Ereksen m (Échange de contenus article principal et redirection) |
||
Line 1: | Line 1: | ||
{{PageTitle|Introduction à Papyrus}} | |||
[[Category:Papyrus/fr]] | {{Incomplete Translation/fr}} | ||
[[Category:Papyrus | |||
==Papyrus, qu'est-ce que c'est ?== | |||
Papyrus est le langage de script utilisé pour réaliser Skyrim. Il fonctionne par la réception d'[[Events/fr|évènements (Events)]] émis par le jeu, et envoyés [[Function Reference/fr|par des fonctions call]]. C'est le ciment qui relie les [[quests/fr|quêtes]] ensembles, en donnant des valeurs à des [[Variables and Properties/fr|variables et propriétés]] basées sur les actions du joueur, se mettant à l'écoute et répondant aux évènement spécifiques du jeu. Il pilote également la plupart des fonctionnalités des [[Activator/fr|objets animés]] qui nécessitent une interaction avec le joueur ou PNJ, et la plupart du comportement complexe des [[Magic Effect/fr|effets magiques]]. | |||
Fondamentalement, Papyrus est orienté-objet par nature. Pour avoir un aperçu de ce que cela signifie et une large vue du langage, continuez la lecture plus avant. Les utilisateurs ayant une expérience d'environnement orientés objets, peuvent simplement parcourir la page [[:Category:Script Objects/fr|Script Objects]] qui devrait donner une assez bonne idée de la façon dont les choses fonctionnent. | |||
===Qu'est-ce qu'un script ?=== | |||
A papyrus script is essentially a plain text source document ('''.psc''' file) that you can use any text editor to write and [[Papyrus_Compiler_Reference|compile]] it into a form the game can understand ('''.pex''' file). To make changes after it is compiled, the raw .psc must be updated or replaced, re-compiled, replacing the outdated .pex. | |||
===Qu'est-ce qu'un script Papyrus ?=== | |||
The language of papyrus can be broken down into a handful of concepts: '''[[:Category:Script_Objects|Objects]], [[Function Reference|Functions]], [[Events]], [[Variables and Properties/fr]].''' The '''[[Script_File_Structure|Script File Structure]]''' page provides information and examples of these concepts. | |||
Each script is defined to be a type of '''[[:Category:Script_Objects|Object]]''', such as a “[[Quest]]”, a “[[Reference]],” an “[[Actor]],” or a “[[Book]].” For the most part, these script objects correlate exactly to the objects found in the editor's masterfile. | |||
These objects have '''[[Function Reference|Functions]]''' that can be used to get at the data they have in them from the masterfile or that is saved in them at run time. For example, you can use the function [[GetActorValue_-_Actor|GetActorValue(“Health”)]] to get the current health value of an actor, and [[ModActorValue_-_Actor|ModActorValue(“Health”, 50)]] to add 50 points to an actor's health value. You could also use the function [[Kill - Actor|Kill()]] to kill an actor. Each of these functions is part of the [[Actor Script]] object. If you tried to call Kill() on a [[Book Script]], because the Book script has no Kill() function defined for it (''since the game code has no notion of killing books''), the compiler will complain and refuse to compile the script, giving you an error message that Kill() is not a function on the Book script. | |||
Scripts also have '''[[Events]],''' which are like function calls that the game itself calls on the object. For example, there is an [[OnDeath - Actor|OnDeath()]] event, that the game sends to [[Actor Script]]s attached to an actor when the actor dies. This allows you to respond to game events, for example, completing a quest after the player kills a particular enemy. Therefore, [[Function Reference|Functions]] as described in the previous paragraph are typically called ''within'' an event. | |||
A script can also have '''[[Variables and Properties/fr|Variables]]'''. A variable is a value that can be modified and reference by the game and scripts. For example, you might want to store the number you get from a [[GetActorValue_-_Actor|GetActorValue()]]. With variables you can store this information, and use it later. | |||
A script can also have '''[[Variables and Properties/fr|Properties]]''', which function almost identically to variables, except that ''other'' scripts can ask about and set them. Properties can also be modified in the editor. (''Note: In reality properties are a bit more complex than simply a “variable that you can get/set externally,” but for practical purposes, that's a fine way to think of them''). | |||
[[Variables and Properties/fr]] can be defined to be simple structures like a [[Literals_Reference#Boolean_Literals|Boolean, an Integer, a Float, etc.]] But their real power lies in that they can be defined as and hold '''any''' [[:Category:Script_Objects|Object Type]]. A Quest script, for instance, can have a property that holds a pointer to an Actor. Once you have a pointer to an object in a script property, that script can run functions on that property and thus on the object in it. For example, a quest script holding a property pointing to an Actor, can call Kill() on that property and it will kill that actor. Properties can be set by the script at run time, or can be set by pointing to objects in the editor. | |||
It's also important to remember that papyrus scripts '''only''' run in response to the game and other scripts. Therefor all of your code needs to live inside an '''[[Event]]''' block, or inside a '''[[#Fragments |Script Fragments]]''' such as a quest stage fragment, or topic info fragment. | |||
<br><br> | |||
{{InDepth/fr|Scripts being called '''[[:Category:Script_Objects|Objects]]''' might be confusing to some people. Scripts are added to objects in the game, they describe their behaviour and functionality. They are the "soul" of a static mesh made out of triangles and covered with a few textures which make it '''look real'''. Papyrus scripts are what makes it '''real'''. What makes it a true, "full" '''object'''. | |||
Strictly speaking, a script is not really an object, rather an important part of it. Usually, an object has to be '''encapsulated''', contained in a single unit which might '''inherit''' some functionality from a parent class which is a more '''abstract''' or '''general''', if you will, version of it. This concept is easily employed natively in code, without a scripting language, where everything from "look" to behaviour is defined by one single, coherent, encapsulated unit of code which correlates to other units of code. "True objects" in the game are actually those which are responsible for showing the mesh on the screen (native code added on top of the Creation Engine), they encapsulate basic properties. Scripts are appended to these objects to give game designers a simple, versatile tool of defining their behaviour without going into battle with low level mechanisms that impede creativity and productivity. They extend the base object by adding behaviour to it or modifying its basic properties (eg. initiating animations in response to an event), they are not an object in their own right. They modify an object or add to it, sometimes they contain most of the object's definition. But they are not '''the object'''. That's why it's been emphasized that they correlate exactly '''for the most part''' to objects found in the editor's masterfile. | |||
Since they add functionality to base objects they are appended to and employ object oriented paradigms like inheritance, abstraction and encapsulation (through the use of '''properties''' which, among other things, serve as a getter/setter mechanism), they can be considered as objects, as long as you keep the former in mind. '''They are the strings behind the puppets, together they make a sufficient whole. An object.''' | |||
}} | |||
==Comment écrire un script papyrus ?== | |||
You will use a text editor to write the script (favorites include [[Notepad%2B%2B_Setup|Notepad++]] and [[Sublime_Text_Setup|Sublime Text]]). Once you have written the script, you will need to [[Papyrus_Compiler_Reference|compile]] it before it will work in game. | |||
Before you sit down to write a script, you need to think about what type of script it's going to be. Or in other words, what type of object it's going to be running on. Once you decide that, you create a new script by declaring it at the top of a .psc file, and EXTEND the Object Script that it's based on. For example, if you are going to be creating a triggerbox that sets a variable, you will create a script that extends [[ObjectReference Script|ObjectReference]]. | |||
==Scripts étendus== | |||
For every object in the game that can be scripted, or pointed at by a property, there is a corresponding "base" papyrus script already premade: see [[:Category:Script_Objects|Script Objects]] for a list. Essentially, the game can give any object of the proper type this script at runtime. | |||
For example, there is an [[ObjectReference Script]]. So any ObjectReference in the game, essentially has the ObjectReference script attached to it. This is so that you can define an ObjectReference property on a script, and point it at any ObjectReference, and call any of the ObjectReference functions on it, such as Disable(), without having to manually attach the ObjectReference script in the editor to every ObjectReference in the game that you might want to call disable() on. | |||
However, you are writing a script, presumably because you want to do something special. Respond to a specific event like waiting for the player to activate the object, or respond to the death of an actor, etc. None of this special functionality exists in the premade object scripts. However, so that the compiler knows what type of object you are dealing with, and to gain access to all the events and functions already defined for that type of object, you '''[[Extending_Scripts_(Papyrus)|extend]]''' the existing “base” script, and add your special code to it. | |||
==Exemple de Script (extending ObjectReference)== | |||
For example, let's say you want to set a quest stage when the player hits a particular triggerbox. You would create a script called “MyTriggerBoxScript” that '''[[Extending_Scripts_(Papyrus)|extends]]''' “ObjectReference” and attach that to the triggerbox's [[reference]] in the [[Render Window]]. You then create a Property called MyQuest, and point it to the your quest in the editor. Then you write your version of the OnTriggerEnter event, that calls set stage on the quest in the myQuest property. | |||
<source lang="papyrus"> | |||
Scriptname MyTriggerBoxScript extends ObjectReference | |||
Quest Property MyQuest Auto | |||
Int Property StageToSet Auto | |||
Event OnTriggerEnter(ObjectReference akActionRef) | |||
If akActionRef == Game.GetPlayer() | |||
MyQuest.SetStage(StageToSet) | |||
EndIf | |||
EndEvent | |||
</source> | |||
There's a lot going on in the above example, let's break it down and further illustrate some points, and raise new ones. | |||
The first line of any script starts ([[Script_File_Structure#Header_Line|the "header line"]]) with “Scriptname” that is us telling the compiler that this script is going to have following name (in this case the name is “MyTriggerBoxScript). This name MUST match the name of the text file you are writing the script in (in this case “MyTriggerBoxScript.psc”). The word “[[Extending_Scripts_(Papyrus)|extends]]” means this script is going to be based on another script (in this case the “[[ObjectReference Script|ObjectReference]]” script). That means your script will have access to all the functions and events defined in “ObjectReference.psc” and you can change them, or add new ones to your script. | |||
The next line is [[Variables_and_Properties#Auto_Properties|declaring an "auto" property]]. “Property” is saying that we are about to declare a new property to use in our script. Quest is the Type of the property (in other words, only Quest objects can be put into this property). MyQuest is the name of the property. So all together this line is telling the compiler “I want you to know about a property called MyQuest, and it will only be able to have a Quest object in it, nothing else.” (For now we will ignore the “auto” keyword. Just know that you almost always write “auto” at the end of every property you declare. For the full discussion see: [[Variables_and_Properties#Declaring_Properties|Declaring Properties.]]) | |||
Similarly the next line is defining an [[Literals_Reference#Integer_Literals|integer]] property. | |||
Next we see some lines that start with [[Events_Reference|Event]] and end with [[Events_Reference|EndEvent]]. You can think of these like book ends for this particular event. Everything in between is how this script will respond to the “[[OnTriggerEnter_-_ObjectReference|OnTriggerEnter]]” event. You might have a script that responds to multiple events. Using Event/EndEvent you isolate everything you want to happen for this event from another one elsewhere in the script. | |||
The OnTriggerEnter is the name of the event your script is implementing. The ( ) [[Events_Reference#Parameters|define what parameters]] the game is going to send to your script's OnTriggerEnter event. "akActionRef" is the name of the parameter, and "ObjectReference" defines what type that parameter is – and, if you have been paying attention, you also know that "ObjectReference" is itself a papyrus script ([[ObjectReference Script]]). | |||
The next line starts with “if” and a line below it “EndIf.” This is a conditional [[Statement_Reference|statement]]. You will be writing a lot of “[[Statement_Reference#If_Statement|if-then-else statements]]” in your script. Here we are testing if the ObjectReference “akActionRef” that the game passed into our OnTriggerEnter when an actor walked into it is the player or not. | |||
Game.GetPlayer() is a function [[GetPlayer - Game|GetPlayer()]] on the [[Game Script]] that returns the [[Actor Script|Actor]] that represents the player. The == is a test (a "[[Operator_Reference#Comparison_Operators|Comparison Operator]]") which means “is this thing exactly this other thing?” So we are asking is the [[reference]] that the game sent to our script when an actor entered our trigger box, is that the same as the actor [[reference]] returned by Game.GetPlayer()? In other worlds “did the player enter the trigger?” If so, then call the SetStage() function on the quest that is in the MyQuest property, and tell it the stage we want to set is whatever we have stored in the StageToSet property. | |||
If you have been following along carefully, you may wonder at how we can compare an [[ObjectReference Script|ObjectReference]] "akActionRef" to an [[Actor Script|Actor]] (the return value of Game.GetPlayer()). The reason we can do this is because the compiler knows that all Actors are also ObjectReferences because the [[Actor Script]] '''[[Extending_Scripts_(Papyrus)|extends]]''' the [[ObjectReference Script]]. So here you see that extension isn't used only for your own scripts, but the base scripts also extend each other as well! The wiki writeup for each [[:Category:Script Objects|Script Object]] contains at the top of its page, which script it is extending from. | |||
So now we know how to set up a script that checks and does things with properties, but we may be wondering, where do we tell the script what objects and what values to assign into your properties? (In the above example, which quest, and which quest stage are we talking about here?) | |||
==Donner une valeur aux Variables et aux Propriétés== | |||
There is one basic way to set a variable, and two basic ways to set properties. Assigning values in a script, and assigning data to a property in the editor. | |||
===Donner une valeur aux Variables et aux Propriétés dans le script=== | |||
The way to set a variable and one of the ways to set a property is to [[Operator_Reference#Assignment_Operators| assign it a value]] in the script itself. | |||
You can declare it one place, and set it some place else: | |||
<source lang="papyrus"> | |||
;At the top of the script we declared a variable: | |||
Int MyStage | |||
;... | |||
;Later, inside an event block: | |||
MyStage = 100 | |||
</source> | |||
Or you can assign it at the time you declare it: | |||
<source lang="papyrus"> | |||
Int MyStage = 100 | |||
</source> | |||
Note: you can set variables and properties when you declare them ''everywhere in the script'' as long as their type is a "[[Literals_Reference|Literal]]." However variables/properties of all other types (an actor for example) can be set when declared, only if declared inside a function or event. | |||
For example, declaring at top, and setting elsewhere: | |||
<source lang="papyrus"> | |||
;Delcared at top | |||
ObjectReference ThingActivatingMe | |||
;set inside an event | |||
Event OnActivate(ObjectReference akActionRef) | |||
ThingActivatingMe = akActionRef | |||
EndEvent | |||
</source> | |||
You might do the above if you wanted to hang onto the thing that activated the object for longer than the one instance of the activation. | |||
Or you could declare it and set at the same time (inside a function or event) like this: | |||
<source lang="papyrus"> | |||
;set inside an event | |||
Event OnActivate(ObjectReference akActionRef) | |||
ObjectReference ThingActivatingMe = akActionRef | |||
EndEvent | |||
</source> | |||
For a more detailed discussion of how to declare and set variables and properties, see: [[Variables and Properties/fr|Variables and Properties]] | |||
===Donner une valeur aux Propriétés dans l'éditeur=== | |||
The other way is to assign a property by pointing to an object in the editor, or typing in the value in the editor. This is how we might set the MyQuest and StageToSet property in the example script we were looking at earlier. | |||
Many forms in the editor have a "Scripts Tab" that allows you to a) add scripts to the object, and b) set the values of properties on those scripts. | |||
For now, let's look at an Activator form in the editor, and look at its script area: | |||
If you select the script by clicking it, then click the properties button, you will see a list of all the properties. If you click the "Edit Value" button and edit field will appear either a text field to enter a numeric data, checkbox if it's a bool, or one or more drop down if it's an object. | |||
In the above image you can see that myQuest has been set to MQ101, and StageToSet has been set to 100. | |||
<gallery> | |||
File:MyTriggerBoxScriptEx.jpg|Accessing Script Properties on an Activator Base Object | |||
File:MyTriggerBoxScriptPropertiesEx.jpg|Viewing and Setting properties | |||
</gallery> | |||
===Les Références héritent des scripts et des valeurs de propriétés de leur parent objet de base === | |||
[[Image:ReferenceScriptPropertyEx.jpg|thumb|600px|Accessing the Scripts Tab on a Reference]] | |||
[[Reference|References]] inherit any scripts and property settings from their [[Base Object]]s (as listed in the [[Object Window]]). | |||
For example, [[:Category:Actor|Actors]] and [[Activator|Activators]]. If you attach a script to an Actor or an Activator all of the individual [[Reference|references]] of those objects in the game will also have those same scripts. Their references will also inherit the data on any properties you have set on their scripts. | |||
You can override these properties by opening the "Scripts Tab" on the [[reference]] in the [[Render Window]], and set property data that you want to override on the [[reference]] itself. You can also remove the inherited script altogether from the reference, by removing the script from the reference's scripts tab in the render window. Note: you can also add scripts directly to the [[reference]] in the [[Render Window]] as well. | |||
==''Fragments'' Papyrus== | |||
In addition to adding scripts to forms in the editor, certain forms have what is called "Papyrus fragments" that you can write script in, that run at particular times. They also have special variables that can be used, for example, Topic Info and Package fragments have an akSpeaker and akActor variables that you can use to gain access to the actor saying the line, or the actor running the package. | |||
Example topic info fragment: | |||
<source lang="papyrus"> | |||
akSpeaker.StartCombat(Game.GetPlayer()) | |||
</source> | |||
A common technique is to set the stage of the owning quest. Some types of forms can be owned by quests (For example scenes, and topic infos in the quest are said to be owned by it, as well as any package whose "owning quest" drop down is set). Example of setting a quest stage from a fragment that is on a form that is owned by a quest: | |||
<source lang="papyrus"> | |||
GetOwningQuest().SetStage(100) | |||
</source> | |||
Follow the links below for details on when each fragment runs, as well as any special variables it has. | |||
*[[Quest Stage Fragments|Quest Stages]] | |||
*[[Topic Info Fragments|Topic Infos]] | |||
*[[Package_Fragments|Packages]] | |||
*[[Scene Fragments|Scenes]] | |||
*[[Scene Phase Fragments|Scene Phases]] | |||
*[[Scene Timer Action Fragments|Scene Timer Actions]] | |||
*[[Perk Fragments|Perks]] | |||
==États== | |||
Not only can a papyrus script respond to various events from the game, it can also be put into various "states" each with its own version of the event, so that it can respond differently to the same event, depending on what state it is in at the time the event comes through. | |||
For example, many activators in the game, especially ones that need to play animations associated with their activation, have an "at rest" state and a "I'm busy animating state." | |||
To give a sense of why this is important, consider a simple pull chain activator. When the player activates it, it needs to animate being pulled down. If the script simple played the animation every time it was activated, you could interrupt the pulling animation, and it would immediately start from the first frame of the animation, "jumping" unnaturally back to the top and animating down again. We'd rather make sure it was finished before allowing the player to activate it again. | |||
Let's take a look at what this might look like inside ascript. | |||
<source lang="papyrus"> | |||
Event OnActivate(ObjectReference akActionRef) | |||
;DO STUFF HERE | |||
EndEvent | |||
</source> | |||
The above would happen every time the player activated the object, potentially causing things to happen too fast if he spams on the activate button. | |||
Shown below is the use of states to control things: | |||
<source lang="papyrus"> | |||
Auto State AtRest | |||
Event OnActivate(ObjectReference akActionRef) | |||
GoToState("Busy") | |||
;DO STUFF HERE | |||
;play animations, etc. - left vague for illustrative purposes | |||
;Done doing stuff | |||
GoToState("AtRest") | |||
EndEvent | |||
EndState | |||
State Busy | |||
Event OnActivate(ObjectReference akActionRef) | |||
debug.trace("I'm busy, so not doing anything.") | |||
EndEvent | |||
EndState | |||
</source> | |||
In the above you'll see two State/EndState pairs, the first of which has the "auto" keyword. The auto keyword here means, "this is the default state" so before any calls to "[[GotoState_-_All_Scripts|GoToState()]]" happen, this is the state the script is in. Like the events inside them, these "state/endState" lines enclose the part of the script that belongs to each of the states. All the events and functions inside each state are the versions that the script uses when it is in each of those states. | |||
You put a script into a state by calling the function [[GotoState_-_All_Scripts|GoToState("StateName")]] where "StateName" is a string that is the same as the name of a state you declared. | |||
Any event outside a State/EndState block will run if the script isn't in ANY state. (For example, if you declare states and none of them have the "auto" keyword, then the events and functions declared outside all of the states will be used until a "GoToState()" call has put the script in a different state. Calling "[[GotoState_-_All_Scripts|GoToState()]]" with an empty string parameter will put the script in into no state (aka, the "empty state"). | |||
If an event only exists in a state that the script is not currently in, nothing will happen if that event is sent to the script. | |||
For more information about states, see: [[States (Papyrus)]] | |||
States are also used to control different versions of functions as well. The discussion above applies equally with functions, but we haven't discussed writing your own functions yet. Which brings us to: | |||
==Fonctions personnalisées== | |||
You can write your own functions in Papyrus. You can then call these functions from within the script where you create them, but also from other scripts as well. This is a more advanced, but powerful and helpful feature of the scripting language. | |||
This will be the most complex discussion in the Introduction page. And in the discussion of writing functions, we will encounter other concepts as well. But stick with it. You will be able to achieve great things with less headaches. You don't often need to write your own functions, but when you do, it can save you time and energy, because you can write snippets of script that you reuse in multiple places, that will cut down on the time it takes you to implement things, and make debugging much easier. | |||
===Appel de fonctions=== | |||
Before we discuss writing your own functions, lets discuss a little more about what functions are. | |||
A function is a section of script that you use (or "call") to do something or get something. A script can call a function that lives in its own file, or can "call" a function on other script. | |||
Some example functions that exist on base scripts: | |||
*GetDisabled() - used to find out if an ObjectReference is disabled or not | |||
*Disable() - used to cause an ObjectReference to become disabled | |||
*Enable() - used to cause an ObjectReference to become enabled | |||
*GetDead() - used to find out if an Actor is dead or not | |||
*Kill() - used to cause an Actor to die | |||
*GetActorValue() - used to get the numeric actor value of an Actor (for example get an Actor's "Health," or "TwoHanded" skill level) | |||
*SetActorValue() - used to set the numeric actor value of an Actor (for example to set an Actor's "Health," or "TwoHanded" skill level) | |||
In the examples above, you'll note a few things. You can use functions to get information about an object, as well as set values and cause them to do things. Functions are used by particular types of objects (you call Enable() on ObjectReferences, and Kill() on Actors). What might not be clear at first is that the object that the function runs on, also has a corresponding script attached to it. The game essentially adds the appropriate base script to objects at run time (Actors get the [[Actor Script]], ObjectReferences get the [[ObjectReference Script]]). | |||
If you want to call GetDisabled(), you must have a "pointer" to an ObjectReference (to which the game attaches the [[ObjectReference Script]]. If you want to call GetDead(), you must have a "pointer" to an Actor (to which the game attaches the [[Actor Script]]). | |||
So, how do you get a "pointer" to an object? | |||
A common way to do this is by setting up a property in your script that points to that object in the [[Masterfile]]. For example, you might set up a property called "myReference" and point it to a large bolder blocking a dungeon entrance in a particular cell in the [[Render Window]], and then you can write this in your script "myReference.Disable()" to cause it to disable, revealing an entrance to a dungeon the player is now allowed to explore. | |||
Once you have a property set up: | |||
<source lang="papyrus"> | |||
ObjectReference Property myReference Auto ;defines the property, you would hook it up in the editor to point at a particular reference | |||
</source> | |||
You can then call functions on it: | |||
<source lang="papyrus"> | |||
myReference.Disable() | |||
</source> | |||
See the section above [[#Setting Property Data in the Editor|Setting Property Data in the Editor]] for information on how to set property data to things in the masterfile. | |||
Another way to get a pointer to an object, is by getting one FROM another function. | |||
For example: | |||
Game.GetPlayer() RETURNS an object of type Actor, that is pointing to the player. | |||
For example: | |||
<source lang="papyrus"> | |||
Actor Property myActor Auto ;defines the property | |||
</source> | |||
Later in the script, you could then call a function that returns an actor, store it, and then call the function from the property | |||
<source lang="papyrus"> | |||
myActor = Game.GetPlayer() | |||
myActor.Kill() ;this will kill the player. Not very nice. But sometimes it is well deserved. ;) | |||
</source> | |||
You could also just skip the assignment to the property altogether, and string things together like this instead: | |||
<source lang="papyrus"> | |||
Game.GetPlayer().Kill() | |||
</source> | |||
The above works because the compiler knows that Game.GetPlayer() always results in an object of the type Actor, and so that object will be able to use the script function Kill(). | |||
And a reminder about what we learned about Extending scripts. Some of the base scripts extend other scripts. For instance the [[Actor Script]] extends the [[ObjectReference Script]]. This means that the [[Actor Script]] has all the same functions that the [[ObjectReference Script]] has, and for all intents and purposes, all Actors can be treated as ObjectReferences as well. So that if you have a pointer to an Actor you can Disable() it, even though that function lives in the ObjectReference script. | |||
This can all be summed up thusly: | |||
'''Objects in the game have default scripts attached to them, based on their type. You can call functions on particular instances of these objects (calling function in their script, or in a script their script extends), by getting a pointer to them and calling the function on that pointer. Pointers can be objects stored in properties, or objects returned by functions.''' | |||
===Appel de fonctions personnalisées=== | |||
Your custom functions will be called just like existing functions. You get a pointer to an object with your script attached to it, and then you call the function on it. The pointer to the object that has your script attached to it that you will call the function on, must be declared as a ''type'' that is your ''script''. | |||
A common practice is to write functions in a script attached to a quest. For example, lets say you made a script called "myQuestScript" and wrote a function called "DoSomeStuff()"): | |||
<source lang="papyrus"> | |||
Scriptname myQuestScript extends Quest | |||
Function DoSomeStuff() | |||
;cool stuff happens here | |||
EndFunction | |||
</source> | |||
And then you attach this script to a quest in the game. | |||
The script knows about all the functions declared inside it. So if you were going to call the "DoSomeStuff()" function in the script above that you attached to the quest, you could simply call the function. | |||
<source lang="papyrus"> | |||
DoSomeStuff() | |||
</source> | |||
But, if you were going to call this function somewhere else you will need a pointer to your quest (just like we discussed above for base scripts). However, you will need to type the pointer to be the ''type of your script''. Your script's type is the same as the name you declared it in the first line of your script. In this case "myQuestScript." Here we are defining a new property of this new type: | |||
<source lang="papyrus"> | |||
myQuestScript Property myQuest Auto | |||
</source> | |||
Then you can open the scripts property button and assign it to be that particular [[Quest]]. For example, on the "Quest Giver" [[alias]] in your [[quest]], you added a script that on death of the quest giver, you want to call "DoSomeStuff()" on the quest. | |||
Let's look at what this might look like. Here is an example of a script that might be attached to a "Quest Giver" [[alias]] in your [[quest]]. | |||
<source lang="papyrus"> | |||
Scriptname myQuestGiverScript extends Quest | |||
myQuestScript Property myQuest Auto ;you would point this to your quest in the "Quest Giver" [[alias]]'s script properties tab. | |||
Event OnDeath(Actor akKiller) | |||
myQuest.DoSomeStuff() | |||
EndEvent | |||
</source> | |||
There's a shortcut to the above as well. Because [[Alias]]es are owned by quests, the [[Alias Script]] has the function "GetOwningQuest()" on it, that returns an object of type Quest, that is a pointer to the quest the alias is in. So you might be tempted to do something like this: | |||
Event OnDeath(Actor akKiller) | |||
GetOwningQuest().DoSomeStuff() ;this results in a compile error | |||
EndEvent | |||
But that won't work, because the object that is returned by GetOwningQuest() is the type Quest. Which only knows about the functions declared in it. The compiler will give an error that there is no function called "DoSomeStuff" on Quest. Your "DoSomeStuff()" function lives in your script called "myQuestScript." | |||
Now, because myQuestScript extends Quest, you can make a promise to the compiler that the thing returned by GetOwningQuest will have your myQuestScript attached to it. You do this by "CASTING" the ''Quest'' object into a ''myQuestScript'' object. | |||
<source lang="papyrus"> | |||
Scriptname myQuestGiverScript extends Quest | |||
Event OnDeath(Actor akKiller) | |||
(GetOwningQuest() As myQuestScript).DoSomeStuff() ;this works | |||
EndEvent | |||
</source> | |||
GetOwningQuest() will return a Quest, the word "As" means think of this AS a myQuestScript. The enclosing () allow you to use the "." to access on the cast object, so you can call DoSomeStuff() on it. | |||
===Écrire des fonctions personnalisées=== | |||
We'll get into the nitty gritty of writing Custom functions by looking at an example of something we might want to do while writing a quest: handle the death of the quest giver. | |||
A function has a start/end line (Function/EndFunction) may contain parameter definitions. In this case lets create a function called "HandleQuestGiverDeath" that takes a parameter of the actor who did the killing, so we can do different things if the player killed him, or something else. We'll write this function on a script "myQuestScript" that we'll attach to our quest in the editor. | |||
<source lang="papyrus"> | |||
Scriptname myQuestScript extends Quest Conditional | |||
ReferenceAlias Property QuestGiver Auto | |||
ReferenceAlias Property QuestGiverBackup Auto | |||
int Property QuestGiverKiller auto conditional ;0 = unset, 1 = player, -1 not the player | |||
function HandleQuestGiverDeath(Actor Killer, ReferenceAlias DeadActorAlias) | |||
if DeadActorAlias == QuestGiver | |||
QuestGiver.ForceRefTo(QuestGiverBackup.GetReference()) | |||
SetStage(110) | |||
if Killer == Game.GetPlayer() | |||
QuestGiverKiller = 1 | |||
else | |||
QuestGiverKiller = -1 | |||
endIf | |||
endif | |||
endFunction | |||
</source> | |||
A new keyword was introduced in the above example. You'll notice the keyword "Conditional" on both the Scriptname line, as well as the line declaring the QuestGiverKiller property. This allows the QuestGiverKiller property to be found by the [[GetVMQuestVariable]] [[condition]], so that you can test the value of this property at run time to filter dialogue stacks to get appropriate dialogue, conditionalize package stacks on actors, etc. Having variables like this that track the specifics about what happened is also helpful for debugging as well. (To see the values of scripts at run time see: [[ShowQuestVars]] and [[ShowVars]]) | |||
So let's look at the function line. Here you see "function/EndFunction" pair that delineates the section of the script that is in our function. "HandleQuestGiverDeath" is the name we are giving our function. And everything in between the parentheses () are the parameters we expect any call to "HandleQuestGiverDeath" to pass in to the function. In this case we are requiring a "Killer" parameter of the type Actor, and a ReferenceAlias that we are calling "DeadActorAlias." | |||
Let's break down this line: | |||
<source lang="papyrus"> | |||
QuestGiver.ForceRefTo(QuestGiverBackup.GetReference()) | |||
</source> | |||
Here we are calling the function[[ForceRefTo - ReferenceAlias|ForceRefTo]] which will force a reference into the the alias, so it can take on the dialogue and packages of that alias. The ForceRefTo function takes a Reference as a parameter. It just so happens that [[GetReference - ReferenceAlias|GetReference]] returns a reference, the reference inside that alias. So here were are asking the QuestGiverBackup alias "hey, what's the reference that is currently in you?" and then taking that result and passing it into the ForceRefTo function we are calling on QuestGiver alias, so that the QuestGiver alias now has in it the same reference that is in the QuestGiverBackupAlias. | |||
Now what's missing is the script we attach to the QuestGiverAlias: | |||
<source lang="papyrus"> | |||
Scriptname myQuestGiverAliasScript extends ReferenceAlias | |||
Event OnDeath(Actor akKiller) | |||
(GetOwningQuest() as myQuestScript).HandleQuestGiverDeath(akKiller, Self) | |||
EndEvent | |||
</source> | |||
Here we see that during the OnDeath event, we will be calling the "HandleQuestGiverDeath" function on the myQuestScript script we attached to the quest. Here again is the common shortcut "(GetOwningQuest() as TheNameOfYourQuestScript)" syntax so we can append the .FunctionName() to it. This works because the script we are doing this in is "owned" by the quest (all Aliases are said to be owned by the quest they are created in) | |||
Notice that we are simply "passing in" the akKiller that the event gives us, as the "Killer" actor parameter on our function, and we are passing in "self" as the parameter for the DeadActorAlias ReferenceAlias parameter. We can use "Self" to mean, "the object this script is attached to" which in this case, is a ReferenceAlias, the type our function is looking for it's DeadActorAlias parameter. (Astute readers may ask "Why do are we bothering to pass in the DeadActorAlias, can't we assume it's the quest giver?" We could. And probably would. But I wanted to give you an example that show cased some of these other concepts such as passing in "Self" as a parameter value) | |||
And there's one more thing to complete your basic understanding of the scripting language, and that is your custom functions can return a value just like native functions do. | |||
For example, we could change the above script to this: | |||
<source lang="papyrus"> | |||
Scriptname myQuestScript extends Quest Conditional | |||
ReferenceAlias Property QuestGiver Auto | |||
ReferenceAlias Property QuestGiverBackup Auto | |||
int Property QuestGiverKiller auto conditional ;0 = unset, 1 = player, -1 not the player | |||
function HandleQuestGiverDeath(Actor Killer, ReferenceAlias DeadActorAlias) | |||
if IsQuestGiver(DeadActorAlias) | |||
QuestGiver.ForceRefTo(QuestGiverBackup.GetReference()) | |||
SetStage(110) | |||
if Killer == Game.GetPlayer() | |||
QuestGiverKiller = 1 | |||
else | |||
QuestGiverKiller = -1 | |||
endIf | |||
endif | |||
endFunction | |||
bool function IsQuestGiver(ReferenceAlias RefAliasToCheck) | |||
if RefAliasToCheck == QuestGiver | |||
Return True | |||
else | |||
Return False | |||
endif | |||
endFunction | |||
</source> | |||
Here we put the check testing if the alias in question is the quest giver into it's own function "IsQuestGiver" that returns a value (in this case a bool) that we can then use in our comparison check. (Again, this is probably more complex than you would need for a simple function like this, but it illustrates the point that you can return your own values. You could also write a function that returns an actor, a quest, or any other [[Script Objects|Script Object]]. This can be a big boon in more complex scripting situations.) | |||
==Que lire ensuite ?== | |||
Once you grasp the basic concepts on this page, next places to go from here: | |||
*Read this page again, and follow the links to get more information about the particulars that might have been glossed over. | |||
*Look over the documentation for the language here: [[:Category:Papyrus Language Reference|Papyrus Language Reference category]] | |||
*Examine the pages in the [[:Category:Script Objects|Script Objects category]] and see what kinds of functions are available to you for different objects. | |||
*Look over some of the other informational pages and tutorials at the top of the [[:Category:Papyrus|Papyrus category page]] | |||
*Poke around in the editor, and deconstruct things. | |||
[[Category:Papyrus/fr|Introduction à Papyrus]] | |||
[[Category:Tutoriels Papyrus/fr]] | |||
{{Languages/fr|Papyrus Introduction}} |
Latest revision as of 15:24, 24 October 2012
Introduction à Papyrus
Papyrus, qu'est-ce que c'est ?[edit | edit source]
Papyrus est le langage de script utilisé pour réaliser Skyrim. Il fonctionne par la réception d'évènements (Events) émis par le jeu, et envoyés par des fonctions call. C'est le ciment qui relie les quêtes ensembles, en donnant des valeurs à des variables et propriétés basées sur les actions du joueur, se mettant à l'écoute et répondant aux évènement spécifiques du jeu. Il pilote également la plupart des fonctionnalités des objets animés qui nécessitent une interaction avec le joueur ou PNJ, et la plupart du comportement complexe des effets magiques.
Fondamentalement, Papyrus est orienté-objet par nature. Pour avoir un aperçu de ce que cela signifie et une large vue du langage, continuez la lecture plus avant. Les utilisateurs ayant une expérience d'environnement orientés objets, peuvent simplement parcourir la page Script Objects qui devrait donner une assez bonne idée de la façon dont les choses fonctionnent.
Qu'est-ce qu'un script ?[edit | edit source]
A papyrus script is essentially a plain text source document (.psc file) that you can use any text editor to write and compile it into a form the game can understand (.pex file). To make changes after it is compiled, the raw .psc must be updated or replaced, re-compiled, replacing the outdated .pex.
Qu'est-ce qu'un script Papyrus ?[edit | edit source]
The language of papyrus can be broken down into a handful of concepts: Objects, Functions, Events, Variables and Properties/fr. The Script File Structure page provides information and examples of these concepts.
Each script is defined to be a type of Object, such as a “Quest”, a “Reference,” an “Actor,” or a “Book.” For the most part, these script objects correlate exactly to the objects found in the editor's masterfile.
These objects have Functions that can be used to get at the data they have in them from the masterfile or that is saved in them at run time. For example, you can use the function GetActorValue(“Health”) to get the current health value of an actor, and ModActorValue(“Health”, 50) to add 50 points to an actor's health value. You could also use the function Kill() to kill an actor. Each of these functions is part of the Actor Script object. If you tried to call Kill() on a Book Script, because the Book script has no Kill() function defined for it (since the game code has no notion of killing books), the compiler will complain and refuse to compile the script, giving you an error message that Kill() is not a function on the Book script.
Scripts also have Events, which are like function calls that the game itself calls on the object. For example, there is an OnDeath() event, that the game sends to Actor Scripts attached to an actor when the actor dies. This allows you to respond to game events, for example, completing a quest after the player kills a particular enemy. Therefore, Functions as described in the previous paragraph are typically called within an event.
A script can also have Variables. A variable is a value that can be modified and reference by the game and scripts. For example, you might want to store the number you get from a GetActorValue(). With variables you can store this information, and use it later.
A script can also have Properties, which function almost identically to variables, except that other scripts can ask about and set them. Properties can also be modified in the editor. (Note: In reality properties are a bit more complex than simply a “variable that you can get/set externally,” but for practical purposes, that's a fine way to think of them).
Variables and Properties/fr can be defined to be simple structures like a Boolean, an Integer, a Float, etc. But their real power lies in that they can be defined as and hold any Object Type. A Quest script, for instance, can have a property that holds a pointer to an Actor. Once you have a pointer to an object in a script property, that script can run functions on that property and thus on the object in it. For example, a quest script holding a property pointing to an Actor, can call Kill() on that property and it will kill that actor. Properties can be set by the script at run time, or can be set by pointing to objects in the editor.
It's also important to remember that papyrus scripts only run in response to the game and other scripts. Therefor all of your code needs to live inside an Event block, or inside a Script Fragments such as a quest stage fragment, or topic info fragment.
Scripts being called Objects might be confusing to some people. Scripts are added to objects in the game, they describe their behaviour and functionality. They are the "soul" of a static mesh made out of triangles and covered with a few textures which make it look real. Papyrus scripts are what makes it real. What makes it a true, "full" object. Strictly speaking, a script is not really an object, rather an important part of it. Usually, an object has to be encapsulated, contained in a single unit which might inherit some functionality from a parent class which is a more abstract or general, if you will, version of it. This concept is easily employed natively in code, without a scripting language, where everything from "look" to behaviour is defined by one single, coherent, encapsulated unit of code which correlates to other units of code. "True objects" in the game are actually those which are responsible for showing the mesh on the screen (native code added on top of the Creation Engine), they encapsulate basic properties. Scripts are appended to these objects to give game designers a simple, versatile tool of defining their behaviour without going into battle with low level mechanisms that impede creativity and productivity. They extend the base object by adding behaviour to it or modifying its basic properties (eg. initiating animations in response to an event), they are not an object in their own right. They modify an object or add to it, sometimes they contain most of the object's definition. But they are not the object. That's why it's been emphasized that they correlate exactly for the most part to objects found in the editor's masterfile.
Since they add functionality to base objects they are appended to and employ object oriented paradigms like inheritance, abstraction and encapsulation (through the use of properties which, among other things, serve as a getter/setter mechanism), they can be considered as objects, as long as you keep the former in mind. They are the strings behind the puppets, together they make a sufficient whole. An object.
Comment écrire un script papyrus ?[edit | edit source]
You will use a text editor to write the script (favorites include Notepad++ and Sublime Text). Once you have written the script, you will need to compile it before it will work in game.
Before you sit down to write a script, you need to think about what type of script it's going to be. Or in other words, what type of object it's going to be running on. Once you decide that, you create a new script by declaring it at the top of a .psc file, and EXTEND the Object Script that it's based on. For example, if you are going to be creating a triggerbox that sets a variable, you will create a script that extends ObjectReference.
Scripts étendus[edit | edit source]
For every object in the game that can be scripted, or pointed at by a property, there is a corresponding "base" papyrus script already premade: see Script Objects for a list. Essentially, the game can give any object of the proper type this script at runtime.
For example, there is an ObjectReference Script. So any ObjectReference in the game, essentially has the ObjectReference script attached to it. This is so that you can define an ObjectReference property on a script, and point it at any ObjectReference, and call any of the ObjectReference functions on it, such as Disable(), without having to manually attach the ObjectReference script in the editor to every ObjectReference in the game that you might want to call disable() on.
However, you are writing a script, presumably because you want to do something special. Respond to a specific event like waiting for the player to activate the object, or respond to the death of an actor, etc. None of this special functionality exists in the premade object scripts. However, so that the compiler knows what type of object you are dealing with, and to gain access to all the events and functions already defined for that type of object, you extend the existing “base” script, and add your special code to it.
Exemple de Script (extending ObjectReference)[edit | edit source]
For example, let's say you want to set a quest stage when the player hits a particular triggerbox. You would create a script called “MyTriggerBoxScript” that extends “ObjectReference” and attach that to the triggerbox's reference in the Render Window. You then create a Property called MyQuest, and point it to the your quest in the editor. Then you write your version of the OnTriggerEnter event, that calls set stage on the quest in the myQuest property.
Scriptname MyTriggerBoxScript extends ObjectReference
Quest Property MyQuest Auto
Int Property StageToSet Auto
Event OnTriggerEnter(ObjectReference akActionRef)
If akActionRef == Game.GetPlayer()
MyQuest.SetStage(StageToSet)
EndIf
EndEvent
There's a lot going on in the above example, let's break it down and further illustrate some points, and raise new ones.
The first line of any script starts (the "header line") with “Scriptname” that is us telling the compiler that this script is going to have following name (in this case the name is “MyTriggerBoxScript). This name MUST match the name of the text file you are writing the script in (in this case “MyTriggerBoxScript.psc”). The word “extends” means this script is going to be based on another script (in this case the “ObjectReference” script). That means your script will have access to all the functions and events defined in “ObjectReference.psc” and you can change them, or add new ones to your script.
The next line is declaring an "auto" property. “Property” is saying that we are about to declare a new property to use in our script. Quest is the Type of the property (in other words, only Quest objects can be put into this property). MyQuest is the name of the property. So all together this line is telling the compiler “I want you to know about a property called MyQuest, and it will only be able to have a Quest object in it, nothing else.” (For now we will ignore the “auto” keyword. Just know that you almost always write “auto” at the end of every property you declare. For the full discussion see: Declaring Properties.)
Similarly the next line is defining an integer property.
Next we see some lines that start with Event and end with EndEvent. You can think of these like book ends for this particular event. Everything in between is how this script will respond to the “OnTriggerEnter” event. You might have a script that responds to multiple events. Using Event/EndEvent you isolate everything you want to happen for this event from another one elsewhere in the script.
The OnTriggerEnter is the name of the event your script is implementing. The ( ) define what parameters the game is going to send to your script's OnTriggerEnter event. "akActionRef" is the name of the parameter, and "ObjectReference" defines what type that parameter is – and, if you have been paying attention, you also know that "ObjectReference" is itself a papyrus script (ObjectReference Script).
The next line starts with “if” and a line below it “EndIf.” This is a conditional statement. You will be writing a lot of “if-then-else statements” in your script. Here we are testing if the ObjectReference “akActionRef” that the game passed into our OnTriggerEnter when an actor walked into it is the player or not.
Game.GetPlayer() is a function GetPlayer() on the Game Script that returns the Actor that represents the player. The == is a test (a "Comparison Operator") which means “is this thing exactly this other thing?” So we are asking is the reference that the game sent to our script when an actor entered our trigger box, is that the same as the actor reference returned by Game.GetPlayer()? In other worlds “did the player enter the trigger?” If so, then call the SetStage() function on the quest that is in the MyQuest property, and tell it the stage we want to set is whatever we have stored in the StageToSet property.
If you have been following along carefully, you may wonder at how we can compare an ObjectReference "akActionRef" to an Actor (the return value of Game.GetPlayer()). The reason we can do this is because the compiler knows that all Actors are also ObjectReferences because the Actor Script extends the ObjectReference Script. So here you see that extension isn't used only for your own scripts, but the base scripts also extend each other as well! The wiki writeup for each Script Object contains at the top of its page, which script it is extending from.
So now we know how to set up a script that checks and does things with properties, but we may be wondering, where do we tell the script what objects and what values to assign into your properties? (In the above example, which quest, and which quest stage are we talking about here?)
Donner une valeur aux Variables et aux Propriétés[edit | edit source]
There is one basic way to set a variable, and two basic ways to set properties. Assigning values in a script, and assigning data to a property in the editor.
Donner une valeur aux Variables et aux Propriétés dans le script[edit | edit source]
The way to set a variable and one of the ways to set a property is to assign it a value in the script itself.
You can declare it one place, and set it some place else:
;At the top of the script we declared a variable:
Int MyStage
;...
;Later, inside an event block:
MyStage = 100
Or you can assign it at the time you declare it:
Int MyStage = 100
Note: you can set variables and properties when you declare them everywhere in the script as long as their type is a "Literal." However variables/properties of all other types (an actor for example) can be set when declared, only if declared inside a function or event.
For example, declaring at top, and setting elsewhere:
;Delcared at top
ObjectReference ThingActivatingMe
;set inside an event
Event OnActivate(ObjectReference akActionRef)
ThingActivatingMe = akActionRef
EndEvent
You might do the above if you wanted to hang onto the thing that activated the object for longer than the one instance of the activation.
Or you could declare it and set at the same time (inside a function or event) like this:
;set inside an event
Event OnActivate(ObjectReference akActionRef)
ObjectReference ThingActivatingMe = akActionRef
EndEvent
For a more detailed discussion of how to declare and set variables and properties, see: Variables and Properties
Donner une valeur aux Propriétés dans l'éditeur[edit | edit source]
The other way is to assign a property by pointing to an object in the editor, or typing in the value in the editor. This is how we might set the MyQuest and StageToSet property in the example script we were looking at earlier.
Many forms in the editor have a "Scripts Tab" that allows you to a) add scripts to the object, and b) set the values of properties on those scripts.
For now, let's look at an Activator form in the editor, and look at its script area:
If you select the script by clicking it, then click the properties button, you will see a list of all the properties. If you click the "Edit Value" button and edit field will appear either a text field to enter a numeric data, checkbox if it's a bool, or one or more drop down if it's an object.
In the above image you can see that myQuest has been set to MQ101, and StageToSet has been set to 100.
Les Références héritent des scripts et des valeurs de propriétés de leur parent objet de base[edit | edit source]
References inherit any scripts and property settings from their Base Objects (as listed in the Object Window).
For example, Actors and Activators. If you attach a script to an Actor or an Activator all of the individual references of those objects in the game will also have those same scripts. Their references will also inherit the data on any properties you have set on their scripts.
You can override these properties by opening the "Scripts Tab" on the reference in the Render Window, and set property data that you want to override on the reference itself. You can also remove the inherited script altogether from the reference, by removing the script from the reference's scripts tab in the render window. Note: you can also add scripts directly to the reference in the Render Window as well.
Fragments Papyrus[edit | edit source]
In addition to adding scripts to forms in the editor, certain forms have what is called "Papyrus fragments" that you can write script in, that run at particular times. They also have special variables that can be used, for example, Topic Info and Package fragments have an akSpeaker and akActor variables that you can use to gain access to the actor saying the line, or the actor running the package.
Example topic info fragment:
akSpeaker.StartCombat(Game.GetPlayer())
A common technique is to set the stage of the owning quest. Some types of forms can be owned by quests (For example scenes, and topic infos in the quest are said to be owned by it, as well as any package whose "owning quest" drop down is set). Example of setting a quest stage from a fragment that is on a form that is owned by a quest:
GetOwningQuest().SetStage(100)
Follow the links below for details on when each fragment runs, as well as any special variables it has.
États[edit | edit source]
Not only can a papyrus script respond to various events from the game, it can also be put into various "states" each with its own version of the event, so that it can respond differently to the same event, depending on what state it is in at the time the event comes through.
For example, many activators in the game, especially ones that need to play animations associated with their activation, have an "at rest" state and a "I'm busy animating state."
To give a sense of why this is important, consider a simple pull chain activator. When the player activates it, it needs to animate being pulled down. If the script simple played the animation every time it was activated, you could interrupt the pulling animation, and it would immediately start from the first frame of the animation, "jumping" unnaturally back to the top and animating down again. We'd rather make sure it was finished before allowing the player to activate it again.
Let's take a look at what this might look like inside ascript.
Event OnActivate(ObjectReference akActionRef)
;DO STUFF HERE
EndEvent
The above would happen every time the player activated the object, potentially causing things to happen too fast if he spams on the activate button.
Shown below is the use of states to control things:
Auto State AtRest
Event OnActivate(ObjectReference akActionRef)
GoToState("Busy")
;DO STUFF HERE
;play animations, etc. - left vague for illustrative purposes
;Done doing stuff
GoToState("AtRest")
EndEvent
EndState
State Busy
Event OnActivate(ObjectReference akActionRef)
debug.trace("I'm busy, so not doing anything.")
EndEvent
EndState
In the above you'll see two State/EndState pairs, the first of which has the "auto" keyword. The auto keyword here means, "this is the default state" so before any calls to "GoToState()" happen, this is the state the script is in. Like the events inside them, these "state/endState" lines enclose the part of the script that belongs to each of the states. All the events and functions inside each state are the versions that the script uses when it is in each of those states.
You put a script into a state by calling the function GoToState("StateName") where "StateName" is a string that is the same as the name of a state you declared.
Any event outside a State/EndState block will run if the script isn't in ANY state. (For example, if you declare states and none of them have the "auto" keyword, then the events and functions declared outside all of the states will be used until a "GoToState()" call has put the script in a different state. Calling "GoToState()" with an empty string parameter will put the script in into no state (aka, the "empty state").
If an event only exists in a state that the script is not currently in, nothing will happen if that event is sent to the script.
For more information about states, see: States (Papyrus)
States are also used to control different versions of functions as well. The discussion above applies equally with functions, but we haven't discussed writing your own functions yet. Which brings us to:
Fonctions personnalisées[edit | edit source]
You can write your own functions in Papyrus. You can then call these functions from within the script where you create them, but also from other scripts as well. This is a more advanced, but powerful and helpful feature of the scripting language.
This will be the most complex discussion in the Introduction page. And in the discussion of writing functions, we will encounter other concepts as well. But stick with it. You will be able to achieve great things with less headaches. You don't often need to write your own functions, but when you do, it can save you time and energy, because you can write snippets of script that you reuse in multiple places, that will cut down on the time it takes you to implement things, and make debugging much easier.
Appel de fonctions[edit | edit source]
Before we discuss writing your own functions, lets discuss a little more about what functions are.
A function is a section of script that you use (or "call") to do something or get something. A script can call a function that lives in its own file, or can "call" a function on other script.
Some example functions that exist on base scripts:
- GetDisabled() - used to find out if an ObjectReference is disabled or not
- Disable() - used to cause an ObjectReference to become disabled
- Enable() - used to cause an ObjectReference to become enabled
- GetDead() - used to find out if an Actor is dead or not
- Kill() - used to cause an Actor to die
- GetActorValue() - used to get the numeric actor value of an Actor (for example get an Actor's "Health," or "TwoHanded" skill level)
- SetActorValue() - used to set the numeric actor value of an Actor (for example to set an Actor's "Health," or "TwoHanded" skill level)
In the examples above, you'll note a few things. You can use functions to get information about an object, as well as set values and cause them to do things. Functions are used by particular types of objects (you call Enable() on ObjectReferences, and Kill() on Actors). What might not be clear at first is that the object that the function runs on, also has a corresponding script attached to it. The game essentially adds the appropriate base script to objects at run time (Actors get the Actor Script, ObjectReferences get the ObjectReference Script).
If you want to call GetDisabled(), you must have a "pointer" to an ObjectReference (to which the game attaches the ObjectReference Script. If you want to call GetDead(), you must have a "pointer" to an Actor (to which the game attaches the Actor Script).
So, how do you get a "pointer" to an object?
A common way to do this is by setting up a property in your script that points to that object in the Masterfile. For example, you might set up a property called "myReference" and point it to a large bolder blocking a dungeon entrance in a particular cell in the Render Window, and then you can write this in your script "myReference.Disable()" to cause it to disable, revealing an entrance to a dungeon the player is now allowed to explore.
Once you have a property set up:
ObjectReference Property myReference Auto ;defines the property, you would hook it up in the editor to point at a particular reference
You can then call functions on it:
myReference.Disable()
See the section above Setting Property Data in the Editor for information on how to set property data to things in the masterfile.
Another way to get a pointer to an object, is by getting one FROM another function.
For example: Game.GetPlayer() RETURNS an object of type Actor, that is pointing to the player.
For example:
Actor Property myActor Auto ;defines the property
Later in the script, you could then call a function that returns an actor, store it, and then call the function from the property
myActor = Game.GetPlayer()
myActor.Kill() ;this will kill the player. Not very nice. But sometimes it is well deserved. ;)
You could also just skip the assignment to the property altogether, and string things together like this instead:
Game.GetPlayer().Kill()
The above works because the compiler knows that Game.GetPlayer() always results in an object of the type Actor, and so that object will be able to use the script function Kill().
And a reminder about what we learned about Extending scripts. Some of the base scripts extend other scripts. For instance the Actor Script extends the ObjectReference Script. This means that the Actor Script has all the same functions that the ObjectReference Script has, and for all intents and purposes, all Actors can be treated as ObjectReferences as well. So that if you have a pointer to an Actor you can Disable() it, even though that function lives in the ObjectReference script.
This can all be summed up thusly: Objects in the game have default scripts attached to them, based on their type. You can call functions on particular instances of these objects (calling function in their script, or in a script their script extends), by getting a pointer to them and calling the function on that pointer. Pointers can be objects stored in properties, or objects returned by functions.
Appel de fonctions personnalisées[edit | edit source]
Your custom functions will be called just like existing functions. You get a pointer to an object with your script attached to it, and then you call the function on it. The pointer to the object that has your script attached to it that you will call the function on, must be declared as a type that is your script.
A common practice is to write functions in a script attached to a quest. For example, lets say you made a script called "myQuestScript" and wrote a function called "DoSomeStuff()"):
Scriptname myQuestScript extends Quest
Function DoSomeStuff()
;cool stuff happens here
EndFunction
And then you attach this script to a quest in the game.
The script knows about all the functions declared inside it. So if you were going to call the "DoSomeStuff()" function in the script above that you attached to the quest, you could simply call the function.
DoSomeStuff()
But, if you were going to call this function somewhere else you will need a pointer to your quest (just like we discussed above for base scripts). However, you will need to type the pointer to be the type of your script. Your script's type is the same as the name you declared it in the first line of your script. In this case "myQuestScript." Here we are defining a new property of this new type:
myQuestScript Property myQuest Auto
Then you can open the scripts property button and assign it to be that particular Quest. For example, on the "Quest Giver" alias in your quest, you added a script that on death of the quest giver, you want to call "DoSomeStuff()" on the quest.
Let's look at what this might look like. Here is an example of a script that might be attached to a "Quest Giver" alias in your quest.
Scriptname myQuestGiverScript extends Quest
myQuestScript Property myQuest Auto ;you would point this to your quest in the "Quest Giver" [[alias]]'s script properties tab.
Event OnDeath(Actor akKiller)
myQuest.DoSomeStuff()
EndEvent
There's a shortcut to the above as well. Because Aliases are owned by quests, the Alias Script has the function "GetOwningQuest()" on it, that returns an object of type Quest, that is a pointer to the quest the alias is in. So you might be tempted to do something like this:
Event OnDeath(Actor akKiller) GetOwningQuest().DoSomeStuff() ;this results in a compile error EndEvent
But that won't work, because the object that is returned by GetOwningQuest() is the type Quest. Which only knows about the functions declared in it. The compiler will give an error that there is no function called "DoSomeStuff" on Quest. Your "DoSomeStuff()" function lives in your script called "myQuestScript."
Now, because myQuestScript extends Quest, you can make a promise to the compiler that the thing returned by GetOwningQuest will have your myQuestScript attached to it. You do this by "CASTING" the Quest object into a myQuestScript object.
Scriptname myQuestGiverScript extends Quest
Event OnDeath(Actor akKiller)
(GetOwningQuest() As myQuestScript).DoSomeStuff() ;this works
EndEvent
GetOwningQuest() will return a Quest, the word "As" means think of this AS a myQuestScript. The enclosing () allow you to use the "." to access on the cast object, so you can call DoSomeStuff() on it.
Écrire des fonctions personnalisées[edit | edit source]
We'll get into the nitty gritty of writing Custom functions by looking at an example of something we might want to do while writing a quest: handle the death of the quest giver.
A function has a start/end line (Function/EndFunction) may contain parameter definitions. In this case lets create a function called "HandleQuestGiverDeath" that takes a parameter of the actor who did the killing, so we can do different things if the player killed him, or something else. We'll write this function on a script "myQuestScript" that we'll attach to our quest in the editor.
Scriptname myQuestScript extends Quest Conditional
ReferenceAlias Property QuestGiver Auto
ReferenceAlias Property QuestGiverBackup Auto
int Property QuestGiverKiller auto conditional ;0 = unset, 1 = player, -1 not the player
function HandleQuestGiverDeath(Actor Killer, ReferenceAlias DeadActorAlias)
if DeadActorAlias == QuestGiver
QuestGiver.ForceRefTo(QuestGiverBackup.GetReference())
SetStage(110)
if Killer == Game.GetPlayer()
QuestGiverKiller = 1
else
QuestGiverKiller = -1
endIf
endif
endFunction
A new keyword was introduced in the above example. You'll notice the keyword "Conditional" on both the Scriptname line, as well as the line declaring the QuestGiverKiller property. This allows the QuestGiverKiller property to be found by the GetVMQuestVariable condition, so that you can test the value of this property at run time to filter dialogue stacks to get appropriate dialogue, conditionalize package stacks on actors, etc. Having variables like this that track the specifics about what happened is also helpful for debugging as well. (To see the values of scripts at run time see: ShowQuestVars and ShowVars)
So let's look at the function line. Here you see "function/EndFunction" pair that delineates the section of the script that is in our function. "HandleQuestGiverDeath" is the name we are giving our function. And everything in between the parentheses () are the parameters we expect any call to "HandleQuestGiverDeath" to pass in to the function. In this case we are requiring a "Killer" parameter of the type Actor, and a ReferenceAlias that we are calling "DeadActorAlias."
Let's break down this line:
QuestGiver.ForceRefTo(QuestGiverBackup.GetReference())
Here we are calling the functionForceRefTo which will force a reference into the the alias, so it can take on the dialogue and packages of that alias. The ForceRefTo function takes a Reference as a parameter. It just so happens that GetReference returns a reference, the reference inside that alias. So here were are asking the QuestGiverBackup alias "hey, what's the reference that is currently in you?" and then taking that result and passing it into the ForceRefTo function we are calling on QuestGiver alias, so that the QuestGiver alias now has in it the same reference that is in the QuestGiverBackupAlias.
Now what's missing is the script we attach to the QuestGiverAlias:
Scriptname myQuestGiverAliasScript extends ReferenceAlias
Event OnDeath(Actor akKiller)
(GetOwningQuest() as myQuestScript).HandleQuestGiverDeath(akKiller, Self)
EndEvent
Here we see that during the OnDeath event, we will be calling the "HandleQuestGiverDeath" function on the myQuestScript script we attached to the quest. Here again is the common shortcut "(GetOwningQuest() as TheNameOfYourQuestScript)" syntax so we can append the .FunctionName() to it. This works because the script we are doing this in is "owned" by the quest (all Aliases are said to be owned by the quest they are created in)
Notice that we are simply "passing in" the akKiller that the event gives us, as the "Killer" actor parameter on our function, and we are passing in "self" as the parameter for the DeadActorAlias ReferenceAlias parameter. We can use "Self" to mean, "the object this script is attached to" which in this case, is a ReferenceAlias, the type our function is looking for it's DeadActorAlias parameter. (Astute readers may ask "Why do are we bothering to pass in the DeadActorAlias, can't we assume it's the quest giver?" We could. And probably would. But I wanted to give you an example that show cased some of these other concepts such as passing in "Self" as a parameter value)
And there's one more thing to complete your basic understanding of the scripting language, and that is your custom functions can return a value just like native functions do.
For example, we could change the above script to this:
Scriptname myQuestScript extends Quest Conditional
ReferenceAlias Property QuestGiver Auto
ReferenceAlias Property QuestGiverBackup Auto
int Property QuestGiverKiller auto conditional ;0 = unset, 1 = player, -1 not the player
function HandleQuestGiverDeath(Actor Killer, ReferenceAlias DeadActorAlias)
if IsQuestGiver(DeadActorAlias)
QuestGiver.ForceRefTo(QuestGiverBackup.GetReference())
SetStage(110)
if Killer == Game.GetPlayer()
QuestGiverKiller = 1
else
QuestGiverKiller = -1
endIf
endif
endFunction
bool function IsQuestGiver(ReferenceAlias RefAliasToCheck)
if RefAliasToCheck == QuestGiver
Return True
else
Return False
endif
endFunction
Here we put the check testing if the alias in question is the quest giver into it's own function "IsQuestGiver" that returns a value (in this case a bool) that we can then use in our comparison check. (Again, this is probably more complex than you would need for a simple function like this, but it illustrates the point that you can return your own values. You could also write a function that returns an actor, a quest, or any other Script Object. This can be a big boon in more complex scripting situations.)
Que lire ensuite ?[edit | edit source]
Once you grasp the basic concepts on this page, next places to go from here:
- Read this page again, and follow the links to get more information about the particulars that might have been glossed over.
- Look over the documentation for the language here: Papyrus Language Reference category
- Examine the pages in the Script Objects category and see what kinds of functions are available to you for different objects.
- Look over some of the other informational pages and tutorials at the top of the Papyrus category page
- Poke around in the editor, and deconstruct things.
Langues : | English • français • русский |
---|