We can create crafting systems to allow your players to be productive members of the games society. We can design combat skills to enhance the experience of battle. We can create useful and powerful spells to aid allies, or to hinder enemies. In short, those elements which define your game will be created by you through the scripting language.
We will begin with the basic structure of scripts, and progress to creating some interesting effects and abilities and placing them into your game. Those with a basic understanding of programming and scripting may still wish to review the early parts of this guide, as there will be some useful information related to the structure of the language, and some of the limitations and their work around.
Scripting Example
Every scripting language needs some structure to tell the software how to process the instructions. Without proper structure, the instructions become unrecognizable to the script processor, resulting in errors. Here we will look at the structure of a script. This is the script for the "heal self" spell.
// Script for the heal self ability
// Restores 5-10 health to the actor
Function Main()
Player = Actor()
// Mana check
ManaLevel = Attribute(Player, "Mana")
If (ManaLevel < 1)
Output(Player, "Insufficient mana - spell failed!")
Return()
EndIf
SetAttribute(Player, "Mana", ManaLevel - 1)
// Particles and casting animation
AnimateActor(Player, "Magic Cast", 0.05, 1)
CreateEmitter(Player, "HealthSpell", 15, 1000)
DoEvents(1000)
// Add health
Health = Attribute(Player, "Health") + Rand(5, 10)
Max = MaxAttribute(Player, "Health")
If (Health > Max)
Health = Max
EndIf
SetAttribute(Player, "Health", Health)
// Done
Return()
End Function
The first thing we see is two forward slash characters (//) followed by some text. This character combination is used to place text inside of the script which is not processed by the script processor. These are called comments, and are used to place information inside of the script to give clearly understandable information to anyone who may be working within the script. Well commented scripts will be much easier to understand and debug than uncommented scripts. This is especially true when a script begins to get very large and complex.
Next we see the line Function Main(). At the end of the script you will see the line End Function. Everything between these two lines will be within the function named Main. Main is the default function for all scripts and should be included in every script you create. We will discuss function in greater depth later, but for now, all you need to remember is that these lines are required for every script.
The next line Player = Actor() assigns the output from the Actor() command to the variable Player. From this point forward we can use the name Player to refer to the Actor of this script. In simple terms, the output of the Actor() command is the handle (a unique numeric identifier) of the actor (Player or NPC) who is running the script.
Next we see another comment informing us that the next section of code is doing a “Mana Check”. This section of code checks the current value of the players Attribute “Mana” and assigns it to the variable ManaLevel. If the Players “Mana” is “less than” 1, the result of the check is True and the instructions between the If and EndIf commands are executed. This results in a message to the Player stating that there is insufficient mana to cast the spell. If, however, the players Mana level is not “less than” 1, every instruction within the “If statement” is ignored and the next section of code is executed.
The next line is Return(). This command is used to end a function and optionally return some value to the function from which the current function was called. In this instance, there is no calling function, since this piece of code resides within the Main() function. This results in this script ending at this point, preventing any further script execution, which happens to be the desired result if the player does not have enough mana to cast the spell.
The next line SetAttribute(Player, “Mana”, ManaLevel – 1) will only run if the result of the previous If statement is False. This code will “Set” the players Mana attribute to 1 less than the value of ManaLevel, which was the variable assigned earlier to hold the value of the players current “Mana” attribute value.
The next section of code plays the characters casting animation (named “Magic Cast”), places a short particle effect using the CreateEmitter() command and pauses the script for 1 second (1000 milliseconds).
Next we see the part of the script which actually does the “Healing” for which the script is named. By this time you should be able to follow the flow and understand what is happening here. A random integer between 5 and 10 is added to the players current “Health” attribute and assigned to the variable Health. The Maximum “Health” for the player is also checked and assigned to the variable Max.
The If statement checks to see if Health is greater than Max. If the result is True, then Health is set to the Max value for the players “Health” attribute. This prevents the player from being healed for more than he should have been healed.
And finally, the players “Health” is increased and the script ends with the Return() command.
In the tutorial section, we will create a script called “A_Heal Other” and create an ability in the Game Editor which will allow a character to learn the ability to “Heal Others”.
We have just reviewed one script used in the game. What we have seen is how a few of the scripting commands are utilized, how variables can be used, how the flow of a script can be controlled using If .. EndIf, and how to increase and decrease player attributes using simple arithmetic operators.
At this point lets look at the assignment operator. An assignment operator is used to assign a value to a variable. We saw an example of this in the A_Heal Self script every time a variable was initiated. The format for assigning a value to a variable is extremely simple and won't require much space to discuss.
The most basic assignment is as simple as A = 1. This will cause future occurrences of the variable A to be used as the integer 1 in any operation within the function in which it is defined. Another example, A = Actor(), assigns the output of a script command to a variable. The above examples assign a numerical value to a variable. If we wish to compare values in variables, we use a double = sign (==) to check for equality. Using a single = will result in assigning the values.
We can also assign text to a variable using the assignment operator and enclosing the text within double quotation marks, for example: A = “Text” will assign the word Text to all coinsurances of the variable A within the function.
Commonly, any text is referred to as a String when dealing with programming or scripting. We will be using the term String in this document from this point forward when referring to non-numerical values.
When dealing with strings, there are some specific operators which we must use when comparing or modifying strings. When adding strings, or concatenating, we use the $+ sign. This will result in the value to the Right of the $+ sign being appended to the value on the Left side (“Hello “ $+ “World!” results in “Hello World!”). When comparing strings we use the $= operator, this compares the string value on the Left side of the operator to the Right side, and returns 1 if true, or 0 if false (“Hello” $= “World” results in a false result). Strings are assigned to variables in the same manner as numerical values with the string value enclosed within double quotes. (A = “World”).
Basic Scripting Control Structures
At this time, we will go a little more in depth into some of the methods for controlling the flow of a script, including a few more commands, and expanding more on the If ... EndIf structure. We will also be introducing the GoTo() command and the use of labels for control purposes. Last but not least, we will discuss the use of Functions for organizing code and controlling the flow of a script.
We saw examples of If ... EndIf controls in the “A_Heal Self” script. What if we want to have multiple levels of checks within a script, or we want to have multiple alternative checks. Luckily for us, there is much more to the If statement than a simple one time check.
At this time we will look at the Else and ElseIf commands which allow us to check for multiple values sequentially within an If statement. Lets look at an example in pseudo code.
If(Value1 < 2) // Our first condition statement
Code to be run
ElseIf(Value1 > 2) // If the first condition is false check this condition
Code to be run
Else // If the above 2 values are both false Run the next code
Code to be run
EndIf
This is a great way to check one value against multiple possible values. If, however we want to check for multiple conditions before running a segment of code, we would place If statements within If statements. This is generally referred to as “Nesting” the If statements, or as “Nested If's”. An example in pseudo code follows.
If(Value1 > 2) // If condition1 is met
If(Value2 < 10) // Check to see if condition2 is also met
Code to be executed // If so, run this code
Else
Execute alternate code // If only condition1 is true, execute this code
EndIf
Else
Code to be executed // If neither value is true, execute this code
EndIf
As you can see using the If ()... ElseIf() ... Else ... EndIf commands we can create some pretty complex checks of our data before executing a section of code. An example of this can be seen in the “Blacksmith” script found within the default project. This script also uses another useful form of control, which we will discuss later when we begin working with Player Dialogs.
Functions are useful for organizing our code into smaller useful segments and can also be useful for controlling the flow of our scripts. We have already seen the use of a function within the above script. The Main() function is the primary entry function within a script. It is possible, however, to have as many functions within a script as you feel are necessary.
An example of the basic structure of a function is found below:
Function Two()
A = 1 + 1
Output(Actor(), A)
Return()
End Function
Admittedly, this function doesn't seem to be overly useful, since it doesn't do anything but add 1 + 1, assign the value to the variable A and display the result to the player via the chat window. However, we will expand on this below and begin to demonstrate the usefulness of functions.
// Basic Function Example
// This script will perform a simple calculation within a function
// and return the result to the calling function
Function Main()
A = 2
B = 3
// Sends the values of A and B to the function Add()
// and place the results of that function into the variable Result
Result = Add(A, B)
// Outputs the result to the players chat window
Output(Actor(), “The Sum of “ $+ A $+ “ and “ $+ B $+ “ is “ $+ Result $+ “.”)
Return()
End Function
// Here is where we create our Add function and do the dirty work
// The values passed to Add() from the main function are placed in the
// variables C and D
Function Add(C, D)
// Sum is the result of C + D
Sum = C + D
// The value stored in Sum is sent back to the calling function Main()
Return(Sum)
End Function
The above is a very simple illustration of how to use functions and pass values to and from functions. This concept can be used to create some very complex scripts, while keeping the script organized, compartmentalized and easy to read and understand.
Those of you who are familiar with other scripting and/or programming languages will likely be wondering when the For...Next and While type loops will be covered. At the time of this writing, these structures don't exist within the scripting language. There is, however, the ability to create loops and control them in a similar fashion using the GoTo() statement.
The GoTo() statement will send the currently executing section of code to the label specified within the parenthesis of the GoTo() statement. The following code example will illustrate the basic concept.
// GoTo example
Function Main()
A = 10
.Loop // Note there is a period before the label name
Output(Actor(), A)
A = A – 1
If(A > 0)
GoTo(Loop)
EndIf
Return()
End Function
The above code will continue to subtract 1 from the value of A until A is no longer greater than 0. With some adjustments to the If statement and placement of the GoTo() command within or outside of the If statement, virtually any form of loop can be created with just a few lines of code.
This covers the basic structure of scripts. Next we will create a new script which will heal a targeted actor.
Create a document called “A_Heal Other” and we can begin.We will begin by adding some comments to let others know exactly what our script will be doing. We will also create our Main function and associated commands.
Lets begin with our comments and our Main function structure. Type the following text into your document.
// script for the heal other ability
// Restores 5-10 health to a targeted actor
Function Main()
Return()
End Function
This is the basic structure from which we will create every script. We also know that within this script we will need to access the Players health and mana attributes and a Targets Health attribute. Lets begin by assigning some variables to make reading the code easier.
// script for the heal other ability
// Restores 5-10 health to a targeted actor
Function Main()
Player = Actor()
Target = ContextActor()
ManaLevel = Attribute(Player, “Mana”)
Health = Attribute(Target, “Health”)
MaxHealth = MaxAttribute(Target, “Health”)
Return()
End Function
What we have done here is to designate the Player variable to the actor who is running the script (Actor()), the Target variable to the actor who the Player has targeted (ContextActor()), the ManaLevel variable to the current amount of Mana available to the Player, the Health variable to the amount of Health the Target currently has, and MaxHealth to the maximum value which the Target can have in their health attribute.
We now have all of the data necessary to begin writing our script. First we will want to verify that our Player has enough Mana available to cast our heal spell. We do this within an If structure as we discussed earlier
// script for the heal other ability
// Restores 5-10 health to a targeted actor
Function Main()
Player = Actor()
Target = ContextActor()
ManaLevel = Attribute(Player, “Mana”)
Health = Attribute(Target, “Health”)
MaxHealth = MaxAttribute(Target, “Health”)
If(ManaLevel < 1)
Output(Player, “Insufficient Mana to cast this spell”)
Return()
EndIf
Return()
End Function
Now, if the player does not have enough mana to cast our spell, the spell will end and let the player know it failed.
By eliminating the possibility of an improper cast early in the script, we have reduced the chances of an error when an attribute becomes negative.We have also shortened the amount of time the server will be processing this script in the event the player does not meet the requirements to cast.
We could also have checked to verify that the player has a sufficient level of the Magic Skill, if it were present in our game, or verified that the target met requirements, such as a high enough rating with a given faction, or was a member of a given faction. However, we are keeping this script simple.
Now we need to include the code which will run if the player has sufficient Mana to cast the spell.
// script for the heal other ability
// Restores 5-10 health to a targeted actor
Function Main()
Player = Actor()
Target = ContextActor()
ManaLevel = Attribute(Player, “Mana”)
Health = Attribute(Target, “Health”)
MaxHealth = MaxAttribute(Target, “Health”)
If(ManaLevel < 1)
Output(Player, “Insufficient Mana to cast this spell”)
Return()
EndIf
SetAttribute(Player, “Mana”, ManaLevel – 1)
DoEvents(1000)
Health = Health + Rand(5,10)
If(Health > MaxHealth)
Health = MaxHealth
EndIf
SetAttribute(Target, “Health”, Health)
Return()
End Function
Our script is now complete, if a little boring visually. We have first reduced the Players mana by 1 and paused the script function for 1 second. We next updated the Health variable by increasing its value by a random integer between 5 and 10.
Next we used an If structure to be certain that the new Health value was not greater than the players maximum health value, and set it to the Max value if it was indeed higher.
And finally, we actually increased the players health value to reflect the action of the spell.
We can also include some visual elements to liven up the game and as visual feedback that the spell is actually working. We should have some animations for the casting, as well as some visual effects to see our target getting healed.
At the time of this writing, we do not yet have a casting animation defined for the included models, so will use the Yawn animation for the purposes of this script. This document will be updated at a later date once we have the correct animations in place.
We will now add the visual elements and our script will be complete.
// script for the heal other ability
// Restores 5-10 health to a targeted actor
Function Main()
Player = Actor()
Target = ContextActor()
ManaLevel = Attribute(Player, “Mana”)
Health = Attribute(Target, “Health”)
MaxHealth = MaxAttribute(Target, “Health”)
If(ManaLevel < 1)
Output(Player, “Insufficient Mana to cast this spell”)
Return()
EndIf
SetAttribute(Player, “Mana”, ManaLevel – 1)
// Particles and casting animation
// Note at the time of this writing AnimateActor is not functioning
// CreateEmitter is also not displaying correctly
AnimateActor(Player, "Yawn", 0.5, 1)
CreateEmitter(Target, "HealthSpell", 15, 1000)
DoEvents(1000)
Health = Health + Rand(5,10)
If(Health > MaxHealth)
Health = MaxHealth
EndIf
SetAttribute(Target, “Health”, Health)
Return()
End Function
The AnimateActor() command and the CreateEmitter() commands are explained in detail in the script command reference, so I'll just briefly cover their use here. What we have done is to play a predefined animation sequence which is defined in the Animations tab in the Game Editor, and to place a particle effect on our Target. The particle effect HealthSpell is one of the included particle effects in the engine. Any defined particle effect may be used.
The script is now complete.
Items can have scripts attached to them which are activated with a right click.
We will now procede to create a ring which is truly worthy of a great magician. Every magician could use a little more Mana, so we will create a script which will add an effect on the player which will increase his Mana pool for a short period of time when it is right clicked in the inventory screen. An Icon will be displayed at the top of the players screen to indicate when the effect is active, and disappear when the effect is removed.
The script we will be using is posted below. I have used the name I_Magicians Ring. The I_ prepended to the script name is to identify this as an Item script.
// Magicians Ring right click script
// One time use Mana Buff which lasts for 5 minutes
Function Main()
Player = Actor()
If(HasItem(Player, "Magician's Ring", 1))
GiveItem(Player, "Magician's Ring", -1)
AddActorEffect(Player, "Mana Buff", "Mana", +30, 300, 10)
Else
Output(Player, “This script should not be running!”)
EndIf
Return()
End Function
This script first checks to see if the player does in fact have the Magician's Ring item. This seems as if it is an unnecessary check, since it will be assigned to the Magicians Ring item, but errors can always be made, and this will help to prevent them from progressing.
Next we Give the player a negative amount of the Magician's Ring item. When given a negative value, the item will actually be removed from the players inventory. Remember we're making this a one time use item. This is the same sort of process which would be used to create a potion, or any other consumable item.
Now that we have removed the item, we can add the effect onto the player. Effects are very useful tools for designing any sort of attribute increase or decrease which will take place for a limited amount of time. In this instance we have added 30 points of Mana to the players Mana Pool. We have assigned a texture to be presented as the Icon for the effect on the players screen.
In this instance, since we are working within what is available in the default project, we have assigned an image from the particles directory, just as we used an image from the particles directory for the Heal Other ability icon.
We have also set this effect to last for a period of 300 seconds, or 5 minutes, after which, it will be removed and the players mana pool returned to normal.
To run when the item is used, this script needs to be assigned to the actual item in the Game Editor.
Once this is done, there is now a magical ring for players to find and use in the game.
(This is not an actual item in the game. It's just an example.)
Required Scripts
If you have seen the scripts used in the game, you will likely have seen this line in 3 of the scripts.
// You may alter this script however you like, but DO NOT RENAME OR DELETE IT
This line appears in the Death ,EquipChange and the LevelUp scripts. This indicates that these scripts must be present for the game to function properly, even if they are empty.
These scripts are triggered at certain events in the course of a game, and are used to cause specific actions at the time of these events. We will briefly cover these scripts, and their functions. We will then view some examples of how these scripts might be used to customize your game.
Lets begin with the Death script. This script is run anytime a Played Character dies in the course of their adventures. At this time we will look at the text of the default death script, and make a few small modifications.
// Default death script for player characters
// You may alter this script however you like, but DO NOT RENAME OR DELETE IT
// The entry function for this script is always "Main"
// The context actor for the script is set to whoever killed the player
Function Main()
// Death animation
Output(Actor(), "You have died...")
Death = Rand(1, 3)
If (Death == 1)
AnimateActor(Actor(), "Death 1", 0.2, 0)
ElseIf (Death == 2)
AnimateActor(Actor(), "Death 2", 0.2, 0)
Else
AnimateActor(Actor(), "Death 3", 0.2, 0)
EndIf
// Wait
DoEvents(1000)
// Restore some health and take away some gold
SetAttribute(Actor(), "Health", 50)
ChangeGold(Actor(), -10)
// Warp back to area start
Warp(Actor(), ActorZone(Actor()), "Start")
// Done
Return()
End Function
This script, upon a players death, will randomly select and play one of 3 predefined death animations. After 1 second, the player is partially healed, and a death penalty applied, in the form of gold removed from his inventory. Next the player is ported to the start portal of the current zone.
This is a perfectly fine death script, but since this is a scripting tutorial, lets look at customizing this a little. To begin with, lets remove the death tax, and instead incur a penalty to player experience (XP).
We simply need to change this line:
// Restore some health and take away some gold
SetAttribute(Actor(), "Health", 50)
ChangeGold(Actor(), -10)
To this:
GiveXP(Actor(), -100)
Now, our players can recover from death, and avoid taxes. If we wished, we could also transport them to another zone entirely, place negative effects on them or any of a number of other scriptable actions.
The next required script is the Equip Change script. This script is executed everytime a player equips an equipable item. There are many possible uses for this script, from performing checks to ascertain if the specified class has the ability to wield the item, to checking if a specific item remains equipped.
Our task in this tutorial is to apply a specific effect when a specific enchanted item is equipped. For this we will use the Magician's Ring we enhanced earlier. Rather than invoking the script we placed in our ring in the earlier exercise, we will be applying an effect directly. When the ring is no longer equipped, we will immediately remove the effect.
The code of our new Equip Change is located below:
// Default equipped items change script for player characters
// You may alter this script however you like, but DO NOT RENAME OR DELETE IT
// The entry function for this script is always "Main"
// The context actor for the script is null
Function Main()
Player = Actor()
Weapon = ItemName(ActorWeapon(Player))
If(Weapon $= "Razor Mace")
AddActorEffect(Player, "HP Boost", "Health", +30, 6000, 15)
Else
DeleteActorEffect(Player, "HP Boost")
EndIf
// Done
Return()
End Function
This was a very simple script intended to only show the functionality of the Equip Change script. There are several checks which could have been made, every equipable slot should have been checked, and either a function called on a slot basis, or a ThreadExecute() command used to run a specific script per slot.
This should give you an idea of the potential for this required script.
Next we will look at the LevelUp script. This script will execute everytime our player gains a level in the game. Possible uses for this script is to check the ActorLevel() and award certain bonuses or items when leveling milestones are reached. The CreateEmitter can also be used to generate a visual signal for every increase in level. The possibilities are virtually endless, and limited only by your imagination.
Below is a simple example of a slightly modified LevelUp script.
// Default level up for all characters
// You may alter this script however you like, but DO NOT RENAME OR DELETE IT
// The entry function for this script is always "Main"
// There is no context actor for this script
Function Main()
Player = Actor()
Output(Player, "You advanced to level " $+ ActorLevel(Player) $+ "!")
Output(Player, "You have been awarded a Razor Mace!")
GiveItem(Player, "Razor Mace", 1)
Return()
End Function
This script will let the player know every time the player gains a level, and will award a Razor Mace to the player.
This covers the required scripts, however there a few other default scripts which do not fall into the required category, but can be very useful and will function in much the same way.
These are the Login, Startup and Attack scripts. The Login script will run whenever a player logs into your game. The Startup script will run when your server boots up, and the Attack script is used if the option for custom combat script is selected in the Game Editor. The Login and Startup scripts should not require more explanation.
Comments (0)
You don't have permission to comment on this page.