User Tools

Site Tools


Sidebar

hpl3:engine:script

Script

Code style

The following is an overview of how the code should look like. For anybody working for Frictional Games, this is a must. For all else, see it as guidelines to have consistent code which is especially good if you want share your stuff.

Remember also to look at existing code and try and copy the looks.

Prefixes

System Hungarian notation is to be used for all variable names. These are the notations used:

Prefix Description Example
c a class class cMyClass {}
i an interface interface iMyInterface {}
f a float or double value. float fX;
l any integer (char, int, uint, etc) int lX;
p an angel script handle iPhysicsBody@ pBody;
s a string cString sWord;
v a vector or array int[] vNumbers; cVector3f vPoint
mtx a matrix cMatrixf mtxTransform;
q a quaternion cQuaternion qRotation;
a an argument void Func(int alX) {
m a member variable class cMyClass {
int mlMember;
}
g a global variable, defined outside of a class or function. int glMyGlobal;
class cMyClass {}

a variable name always starts with a lower case letter, so anything of type not specified must start with a lower case word. Example:

cMyOwnClass myClass;

If combining two prefixes, and one consist of more than one letter, then have an underscore between them. Example:

cMatrix m_mtxMemberMatrix;

Comments

Always comment the code you write; when scripting, it is always certain somebody else will work on it. Comment to make other people understand what it means and to make the code look nicer.

Function/Class comment
Use comment blocks for the main comment for each class/function, this so that the parser can show the correct info for code compleition/hints.

/**   
 * Change the light radius to the specified value.
 * 
 * @param tString asLight, name of the light to change radius of.
 * @param float afRadius, the radius in meters to set for the light.
 **/
void LightChangeRadius(const tString &in asLight, float afRadius)
{
 
}

Section comment
When starting a new section of some kind do a comment that explains what is going on.

////////////////////////////////////////////
// Update the monster damage
cMonster@ pMonster = CurrentHit();
float fDamage = RandomDamage();
fDamage -= pMonster->Defense();
if(fDamage<0) fDamage =0;
fDamage *= mfHitMultiplier;
pMonster->DecHealth(fDamage);

Single line comment
When commenting a single line

//The square of the distance will be enough
float fDistSqr = vDiff.x*vDiff.x +vDiff.y*vDiff.y;

Function/Class separation
Use this to make it easier to see functions and classes. Can also be used to encapsulate other data.

void Foo()
{
 
}
 
//-------------------------------------------
 
void Bar()
{
 
}

Containers

There are some containers that are not part of the angelscript language, but added as extension, these will be described here.

Array

Arrays are simple linear storage containers.

You can access elements using varname[index].

Syntax:

array<T> or T[]

Methods

void insertAt(uint, const T&in)
void removeAt(uint)
void insertLast(const T&in), void push_back(const T&in)
void insertBack(const T&in), void push_front(const T&in)
void removeFirst(), void pop_front()
void removeLast(), void pop_back()
uint length() const, uint size() const
void resize(uint)

User Classes

Script user classes are C++ classes that implement the iScriptUpdateableUserClassInterface interface.

The script for these classes must contain the code:

cUserClass@ mBaseObj;
void SetupBaseInterface(cUserClass@ aObj){@mBaseObj = aObj;}

Where “cUserClass” is the name of the implemented user class.

These classes can choose to implement the method:

void Init()

This method is called upon creation of the class object, but not on reload.

The constructor of the class is called on creation and on reload. However, note that the handle mBaseObj is not initialized when the constructor is called and hence may only be used in Init().

Script Classes

Script classes are whatever class that is created in script.

Handles

For any script that is to be saved (which means all scripts pretty much), handles to script classes can NOT be member variables. So a script file like:

class cMyClass {
 ...
}
class cMyOtherClass{
 ...
 cMyClass@ mpMyClass;
}

is NOT supported. The reason for this is that when a script is destroyed any handle to these classes are destroyed too and cannot be retrieved again!

It is however okay to to pass handles as arguments:

void MyFunc(cMyClass@ apArg) {
...
}

or use them as local or global variables.

cMyClass@ gpGlobalVar
void MyFunc() {
void MyFunc() {
 cMyClass@ pLocalVar
 ...
}

It is only when they are to be saved that problems arise!

Finally, having handles to engine types, ie

class cMyClass {
 iPhyiscsBody@ mpBody
}

is okay!

Script Callbacks

Calling script functions from scripts

If you want to run a script function/method from script using the Prepare, Execute, SetArg, etc you need to make sure that string arguments are NOT refences from C++ code. For example this is NOT allowed:

if(mObj.ScriptPrepare("void DoSomething(string &in asStr)") ){
SetArgString(0, mObj.GetName() );
ScriptExecture();
}

Where mObj is of class cMyObj implemented in C++:

cMyObj {
 ...
 tString& GetName();
}

What is wrong here is that the argument is “string &in”, an in reference. The reason why this is bad is because when the string sent to the callback function is a reference and the string returned from C++ is also this. What happens when a reference is returned from script is that the Script makes a copy of the class and then provides a reference to this. In the example above, the variable returned from GetName() only has a scope lasting the function call. So when we arrive at ScriptExecute, the reference is gone.

To fix this you can do one of two things. One is to make sure that the argument for the called function is not a reference, like:

if(mObj.ScriptPrepare("void DoSomething(string in asStr)") ){

Or you can simply save the variable as a script one and then set that one, like:

tString sName = mObj.GetName()
SetArgString(0, sName );
ScriptExecture();

In the last example, the variable (sName) is in scope until after ScriptExecute is called, and hence for the as long as needed.

Global Functions

Global functions make it easier to communicate between separate script modules. They are the slowest form of function calling but can also be very nice to use, just do not use it in performance sensitive functions (eg functions that get called many times in an update loop).

1) Before running the Global function, all the arguments need to be set up. Example:

cScript_SetGlobalArgBool(0, true);
cScript_SetGlobalArgVector3f(1, cVector3f(1,2,3));
cScript_RunGlobalFunc("MyClassObj", "cMyClass", "MyGlobalFunc");

2) The return value must be gotten before any other global function is called, else it goes away.
This is okay:

...
cScript_RunGlobalFunc("MyClassObj", "cMyClass", "MyGlobalFunc");
bool bReturnFromMyGlobalFunc = cScript_GetGlobalReturnBool()
cScript_RunGlobalFunc("MyClassObj", "cMyClass", "SomeOtherFunc");

This is NOT okay:

cScript_RunGlobalFunc("MyClassObj", "cMyClass", "MyGlobalFunc");
cScript_RunGlobalFunc("MyClassObj", "cMyClass", "SomeOtherFunc");
bool bReturnFromMyGlobalFunc = cScript_GetGlobalReturnBool()

(it would be getting the return value from SomeOtherFunc.)

3) When creating a function that is to be global, it must have void as return value and no arguments. Example:

void MyGlobalFunc()

4) The implemented global function must get all arguments using

void MyGlobalFunc() {
bool bArg0 = cScript_GetGlobalArgBool(0);
cVector3f vArg1 = cScript_GetGlobalArgVector3f(1);

5) When calling a global function, it is okay to have one or more asterix in the object name and then several object will be called. For example “My*test” would call: “MyNiceTest” and “MyBadTest”. However, this will be a bit slower, so be careful when using it (eg not during things called every update)

Saving

To skip saving a variable, you must use metadata keyword [nosave] in front of the the variable name. Examples:

[nosave] float mfT;
[nosave] cMyClass@ mHandle;

If you only want to save the actual pointer for a handle, but not any data, then use the prefix [nodatasave] Example:

[nodatasave] cMyClass@ mNoSaveHandle;

Also, there is a the prefix [volatile], this will do the same as a [nosave] but will also make sure that variable is not even saved during script reload. This should be used on handles where you are never sure if saved variable can invalid, for instance cSoundEntity and cParticleSystem (that can be deleted by the engine).

[volatile] cSoundEnity@ mpCurrentSoundEntity;

In case you have an array of handles where none of the properties should be saved, this is NOT possible. Instead save names of IDs of the objects in the array.

Important notes

C++ scriptable classes with classes in can not be abstract ones. It must always be the top class in the hierarchy that is used and saved.

hpl3/engine/script.1333533832.txt.gz · Last modified: 2012/04/04 10:03 by jens