Functions

From HEWIKI
Jump to: navigation, search

Contents

For a directory of commonly used functions, see Function Index. For a quick reference guide of how the scripting language works, see HSL for programmers.

Functions are discrete sections of HSL code, and, along with class methods, the basic building blocks of HeroScript. Scripts are composed of either functions or methods. Within the functions and methods are HSL commands, or calls to other functions and methods. For a complete list of possible commands, see the Commands Syntax Reference.

It is important to keep in mind that not all functions are available to all scripts. The functions that can be called, will be dependent on where the script is running. Scripts can run on one of several different servers, or they can run on the client on an individual user's computer.

Creating a function

The typical format of a function is:

For example, this private function will take a noderef and an integer, and return a string.

function DoStuff(wand as noderef, spell as integer) as string
  waveWand(wand)
  return "The wand was waved!  Spell " + spell + " was cast."
.

Then when it is called, the syntax would be:

  output_string = DoStuff(wandNode,234)
  println(output_string)

or the function could even be embedded in another function call:

  println(DoStuff(wandNode,234))

Function declaration

See also: Signature

The basic format of the declaration structure is:

[modifier(s)] function <name>([<parameters>]) as <returnType>
  // section of code goes here
.

The first line of the structure declares the function's:

Public, Private, Remote, Untrusted and Shared Functions

Main page: Function/method modifiers

Functions may have prepended keyword modifiers, which indicate where the functions exist, and what can call them.

When a script is compiled, the compiler checks all of the called functions in the compiled script, to ensure that they exist, no matter which other files that they may be in. The compiler also checks that the argument list (the signature) is correct. For public functions, this is simple because the location of the called function is coded directly into the function call. However, sometimes the location is not specified at compile time, since a scriptref variable is used instead. In those cases, the name of the script does not have to be specified, but the function that is being called must be a shared function. The compiler maintains a list of signatures of all known shared functions.

Function modifiers

Functions may have the following additional modifiers:

Examples

public function SwordAttack(sword_node as noderef, target_node as noderef)

shared function DealDamage(player_node as noderef, damage as integer)

Function names

Every function must be declared with a <name>. By convention, names should be alpha-numeric with no special characters, including underscores. Also by convention, function names should follow CamelCase formatting, which has each word uppercased.

A script can contain only one function of a given name, regardless of its type modifier.


Function signatures

A function's signature is the portion of the declaration line that follows the function's name. So in the case of the shared IsHotSpotItemChecked() function:

shared function IsHotSpotItemChecked(item as NodeRef of Class GUIControl) as Boolean
// do stuff
.

The signature part is:

(item as NodeRef of Class GUIControl) as Boolean

Specifically, the signature incorporates two pieces: the function's parameters, and its return type.

Other (non-signature) elements on the line include the function's name, and its Modifier (in this case, the keyword "Shared").

Calling a function

The syntax for calling a function, depends on its location relative to the calling script. There are four different possible locations for the function that is being called:

For the above two options, a function can be invoked simply by using its name:

       DoStuff(n_wand, 27)


In this case, the script name must be prepended to the function name. For example, suppose that in script a.hsl, exists a GetString() function. In script b.hsl exists a ReadText() function. If a line in script B is calling GetString(), the syntax would be:

       a:GetString(buffer)


If the function is on a remote server, it must be accessed via a "Remote Function Call", which involves initializing a data structure with detailed information explaining where it is located, and what to do if it can't be found. For more information, please see the section on Remote Call.

Checking if a function exists

When calling a function in another script, it is essential to know the name of the function and script ahead of time -- they have to be hardcoded in.

There is one exception to this, which is a "shared" function. In these cases, a special built-in function called hasFunction can be used to determine whether or not a particular function exists in a given script. For example, if something in the game is about to cause damage to a character, it might be desired to first check to see if the character already has a damage handler function in the script on the character, even if the calling script does not initially know the name of that script. This could be done with the following code:

  if hasFunction(playerScript, "DamageHandler") 
    // the function exists
  .


Function Call examples

Assume that there is a script called FunctionLibrary which contains functions aFunc, bFunc, and cFunc:

// FunctionLibrary.hsl
 
function aFunc()
  stuff
.
 
public function bFunc()
  stuff
.
 
shared function cFunc()
  stuff
.


And then another script WorkScript wants to call functions in FunctionLibrary:

// WorkScript.hsl
 
function main()
  scriptVariable as scriptref
 
  FunctionLibrary:aFunc()    // Will not work, since it's private to the library
 
  FunctionLibrary:bFunc()    // will work
 
  scriptVariable=FunctionLibrary
 
  scriptVariable:bFunc()     // will not work (see below for why)
 
  scriptVariable:cFunc()     // will work
.

In order for the above script to be able to use scriptVariable:bFunc(), the bFunc function would have to be declared as a shared function, instead of a public one. This would also be necessary for the function to be findable via hasFunction().

There is no shortcut way to declare all functions within a given script as public or shared. If it is desired to do this, simply prepend the appropriate word to every function in the script.

Additional example

In the following case, the "takeDamage" function in the character's script would be a shared function.

function PlayerSteppedInLava(character as noderef)
 
  if hasFunction(character.script,"takeDamage")         // Might the character have some defensive ability
                                                        //  we need to be aware of?
    character.script:takeDamage(character,50)           // Here, let the character's script deal with the damage
  else
    character.life = character.life - 50    // Ow!
  .
.

In the above example, if takeDamage were a public function instead of a shared function, it could not be called in the above way, or even found. hasFunction can only be used on shared functions, and not on public functions.

Function-returned values

Many functions return a value such as a string, integer, or noderef. This is declared in the function signature line by adding the optional word as and the datatype which will be returned. Within the function, a RETURN statement will specify the data to be returned.

Typical syntax might be such things as:

function CreateNodeFromClass(classname as string) as noderef
  CreatedNode as noderef
 
     (stuff)
   return CreatedNode
.
 
function FindString(hay as string, needle as string) as string
   foundString as string
    (stuff)
   return foundString
.
 
 
function DoThings()
 
  n as noderef
  n = CreateNodeFromClass("container")
 
 
  pin as string
  pin = FindString(haystack,needle)
.


If a function declares that it returns a value but fails to do so, a Script Error is produced at runtime.

Functions can also be used as part of an expression. For example:

  OutputMsg("The number is "+itos(5))
 
  response_s = "Hi "+FindName(friendList,"spouse")+"!"

(Note: This is easier with HSL functions than built-in functions -- some built-in functions cannot yet be embedded)

Function Syntax

Functions have a signature which is made up of parameters and their types.

 
function <name>(<arg> <argdef> <type>, <arg> <argdef> <type>, ...) [as <type>]
 
.

Function Parameters

A function's parameters are a comma-delimited list of variables that the function requires from a call. In a signature, each parameter is defined exactly like any other variable is defined, and can be any of the allowed variable types. Take this example:

function DoStuff(num as Integer, text copies String, state references Boolean)
  //
.

Whenever a function is called, each parameter in its signature defines a variable with the specified name and type. So DoStuff() will define the integer variable "num", the string variable "text", and the boolean variable "state". Additionally, these variables will be set automatically, using the values that are passed in by the call.

Notice that each of the parameters in our example has a different keyword between the variable name and the type. Each causes unique behavior with respect to those variables.

<arg> 
One of the arguments to the function. Also called a parameter.
<argdef> 
This is a linking word in the parameter list which must be one of the following:
as 
the parameter is just passed in, but its value cannot be changed within the function
copies 
a copy is made of the variable that the function can change, but it doesn't change the value that was originally passed in.
references 
passes the variable as a pointer, so the value can be changed. This means that, within the function, you will be operating the data that is passed in directly, not a copy of that data. This is particularly useful when you have a function that needs to operate on (or return) large lists or classes.
<type> 
One of the possible Data Types, such as string, noderef, list of noderef, integer, enum <enum_list>, etc.
as <type> 
(optional) The return value of the function. If not specified, then the function does not return a value.

Only one function of a particular name can exist in a single script. However, there can be functions of the same name that exist in different scripts. Functions of the same name do not have to have the same signature unless they are Shared functions.

For more information on the syntax to call a particular script, please see "Calling a Function" above.

Bear in mind that variables of type NodeRef are special. While the above keywords still apply to them as it relates to their reference, the node itself is still subject to permanent change as it relates to its member fields. In other words, while a parameter like "pc as NodeRef of Class _playerCharacter" cannot be changed to reference another node, the fields on the node itself can still be changed, and those changes are permanent.

An additional hidden parameter also exists in many cases, which defines and sets the variable called "me". The "me" variable's type will always be a NodeRef of unspecified Class. For details on which this variable is set, see Me node.

Example

 function attack( target as noderef, maneuv as enum maneuver )

In this example, the parameter names are target and maneuv. Inside the function they are accessed by those names.

When calling the attack() function, values for the parameters (also called arguments) must be given. For example, if the attack() function resides in the same script as the function that is calling it, the syntax might be:

attack( creature, SpinKick )

If the attack() function were in a different script (and it had been declared as a Public or Shared function), then syntax might be:

varScript:attack( creature, SpinKick )

Changing parameters

Normally parameters are read-only, which means that they cannot be set like other variables. It is possible, however, to use the parameter like a variable and then in some cases use it to modify something else.

If it is desired to have the ability to modify a parameter within a function, it is necessary to tell the compiler whether to make the argument simply a local copy, or whether to change the argument passed when the function was called. This is done by replacing as with either copies or references.

As an example, with the as argdef in the above example, the target parameter is a node reference which can be used to set fields on the creature node. However, if it was desired to allow the attack function to change which node that target pointed to, then the references definition should have been used instead.

Examples

 function another( target references noderef, maneuv copies enum maneuver)
   target = someOtherNode
   maneuv = LowThrust
 .
 
 
 function foo(str as string, nn as noderef of class item)    
    . . .
 .
 function scribble(str references string) as list of string
   . . .
 .

Return values

Functions may return a single variable to the calling script via the RETURN command, although this value may be a complex type, such as a class. This is done by declaring the return type in the function's signature, as shown here:

function ReturnValue() as Integer
  i as Integer = 5
  return i
.

However, functions are not required to return anything, in which case no return type is included in the signature, like so:

function ReturnNoValue()
  return
.

Function categories

For the purposes of most HSL scripters, all functions can be treated pretty much the same, however there are some subtle category differences which are defined here:

Client functions 
These are functions which run on the graphical client, such as HeroBlade. For example, a function which is simply controlling the way that the client GUI displays, would probably be a client function.
Server functions 
These are functions which run on a server process, instead of the client. Server functions implement the game rules.

Client and server functions are mutually exclusive, and cannot directly call each other. The only way to send messages back and forth is by building special packets that are sent via command channels. For more information, see the Command Handler and Remote call.

When creating a new script, it is necessary to know whether it's going to be a client script or a server script in order to ensure that it is working properly with the other scripts in that database; however, there is no difference in syntax between writing client scripts or server scripts.

It is possible to have an identical function name on both client and server, and even possible to have a function which has a different signature between client and server, but in general, any external function should operate identically to its client or server counterpart, if it is on both.

Pre-created functions

There are some pre-created functions for common tasks.:

For example, there exists a special script called ExternalFunctions which uses the external keyword to classify these external functions:

external function println(text as string)

The definitions in the script are a reference to the compiler, to communicate the signature of each of the external (aka exposed) functions.

The external keyword is for use in this script only, and cannot be used in other scripts.

Plugins

Main page: HeroScript Extension Plugin

HeroEngine supports a plug-in architecture on both client and server, that allows you to add new functions to HSL that exercise the capabilities of the given plugin.

Example function

Go through all the items in a backpack and see how many are flasks.

function flaskcounter(backpack as noderef) as integer
 
  flaskcount as integer
  x as list of association
 
  X = QueryAssociation(backpack, "in" , 0)    // X is all the things with backpack "in" association
  foreach item in x loop                           // Check each node in X, setting ITEM in turn
    tgt as noderef
    tgt = item.target                         // Set TGT to a target node of the X association
    where tgt is exactly container                 // Check its class
      if (item.target.name = "flask") 
        flaskcount = (flaskcount + 1)
      .
    .
  . 
  OutputMsg("Flasks so far: " + flaskcount)  
 
.


Difference between functions and methods

On first glance, functions and methods are both blocks of HSL code. The main differences between them are:

Which form you use, depends entirely on your own design. You can use either functions, or methods, or a combination of both, depending on what works best with your game.

Functions

Functions are always prefixed with the keyword "function"

In order to use a node in a function, you would have to pass a NodeRef in the argument list when the function was called.

The location of a function (which script it was in), might be determined by the script property of a node, or via some other creative means.

function Add(l as Integer, r as Integer) as Integer
  return l + r
.

Function scripts

Function scripts can include only functions. There is no strict convention on how function scripts are named.

If an attempt is made to add a method to a function script (meaning one that is not named as a ClassMethods script), the script will not compile.

Methods

Main page: Class methods

Methods are always prefixed with the keyword "method"

Methods are always associated with a node, and are declared in the class methods script associated with a class on that node.

When a method is called, the Me object will be set to the node on which the method was called:

method exampleMethod()
  me.examplefield = 5    // me is a valid nodeRef and points to the node on which this method was called
.

Class method scripts

Methods are always stored in a script, which is associated with a class. Each class may have a method script, which is named based on the class. For example, if a node has three classes, AA, BB, CC. For the class "AA", the expected script name would be AAClassMethods. There might or not be class method scripts for BB and CC.

A class method script could include functions. However, a function script can not include methods (it wouldn't compile).

There is no concern about duplication of methods between class method scripts on a node, as it is not possible to have duplicated methods in the first place. As soon as an attempt is made to glom a class onto a node, where the class's classMethods script has duplicated methods to what already exist on the node, an error will be generated.

Example

For an example of the differences between functions and methods: if a GUI Control is being used, and a check is being made to see if a GUI Event has been triggered. This might result in a call to a method, or to a function, depending on what is available.

If the GUI Control node has any classes with associated class methods scripts, then HeroEngine will check each script to see if it needs to call a relevant method. If it cannot find a method, or the Control does not have any methods scripts, HeroEngine will then call the relevant function, looking for it in the script referenced by the Script Property of the GUI Control node.

More information

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox