Variables are named identifiers that contain a value. If you’ve ever done algebra, you’ve used a variable. Variables are a basic building block of script.
Type
Variable type describes the kind of data the variable contains, and how it will behave when interacting with logic expressions. One of the fundamental differences between C and OpenBOR Script is that variables in OpenBOR Script are weakly typed as opposed to strong typed. By weak typed, we mean the engine handles typing for you, and will assign or reassign variable types automatically.
The obvious advantage to this model is not having to worry about type, but it does mean you need to be wary of what’s going on. Take a look at this example.
int i;
i = 0;
i = i + " is a string";
In C, this would never work. You’d get a nasty typing error and the application would not compile. In OpenBOR Script, the engine reassigns i
to a string type, and i
is now “0 is a string”. It’s simple to work with at first, but be careful you don’t let it ruin your logic expressions.
In any case, there may be times when you want to know what type OpenBOR has assigned to a variable and act accordingly. To do so, use the typeof()
function as follows.
int type = typeof(mixed <variable>);
The typeof()
function will return a constant indicating the variable type.
Type | Constant | Declaration | Notes |
---|---|---|---|
Float | openborconstant("VT_DECIMAL") | float x = 0.1; | Floating point decimal. In OpenBOR script you must fully qualify a float. In other words, 0.1 is a valid floating decimal, .1 is not. Floating values have a precision of seven decimals and a value range of -3.4E+38 to 3.4E+38 . |
Integer | openborconstant("VT_INTEGER") | int x = 1; | A whole numeric value. Probably the most commonly used type. Integer values can range from −2,147,483,648 to 2,147,483,647 (signed) or 0 to 4,294,967,295 (unsigned). Native integers are signed unless noted otherwise. All user defined integers are signed. Some native integer values may be limited to a specific list of constants. See the individual properties for details. |
Null | openborconstant("VT_EMPTY") | mixed x = NULL(); | A variable that is undefined or has no value at all. |
Pointer | openborconstant("VT_PTR") | void x = <object>; | Pointer to a memory address. Pointers are typically used to reference structured objects (ex. an entity) or collections of items. |
String | openborconstant("VT_STR") | char x = "string"; | Any arbitrary collection of characters. This sentence is a string. |
Note that once a variable is typed to a string, it is never auto typed into anything else. You should always avoid using strings in logical expressions as they are extremely sub-optimal compared to other types (this is not an OpenBOR caveat, take this advice for any other engine or coding). Moreover, if it isn’t obvious, you can’t do any sort of math on a string at all.
There are cases where you might end up with numeric values typed as a string. To force them back to integer or float type, use the following functions:
float x = string_to_float(<string>)
int x = string_to_int(<string>)
Example:
/* This number is typed as a string, so we can't do much with it. */
char string_value = "5";
/* Now we have an integer we can add, subtract, multiply, etc. */
int integer_value = string_to_int(string_value);
Scope
Scope refers to the visibility of a variable throughout a function, its parent scripts, and so on. It’s best practice to use the most narrow scope possible for a given task (this again is true for all coding, not just OpenBOR Script).
Function
Function variables are variables you define inside a given function without specifying the scope. They are unique to that function and only that function. Function vars are destroyed as soon as the function is finished. In other languages, these are sometimes called local variables, and that’s the correct terminology here too. We just call them function variables to avoid confusion with Local Vars.
function somefunction() {
int name = value;
}
Global
mixed var = setglobalvar(mixed <identifier>, mixed <value>);
getglobalvar(mixed <identifier>);
As their name implies, global vars occupy the global scope. Once defined, they are available to all functions in all scripts at all times. Global vars are even persistent through game saves, so turning off the engine is not always enough to get rid of them. This later caveat can be very powerful if used correctly, but if misunderstood can also introduce bugs into your scripts.
Tip: Avoid global vars whenever possible They are very powerful, but also easily misused and extremely difficult to debug. It is best practice to limit yourself to the scope you absolutely need for a given task, and save global vars for the jobs only they can do.
Entity
setentityvar(void <entity>, mixed <identifier>, mixed <value>)
mixed var = getentityvar(void <object>, mixed <identifier>)
Entity variables are similar to global variables. They occupy the global scope, and so are available to any function, anywhere, anytime. The difference is that entity vars include a second key to identify them as belonging to a specific entity. Entity vars remain in memory until the associated entity is killed.
function somefunction() {
void entity = getlocalvar("self");
setentityvar(entity, "birthdate", "2004-01-01");
}
Local
setlocalvar(mixed <identifier>, mixed <value>);
mixed var = getlocalvar(mixed <identifier>);
Local vars can be a little confusing to understand, but they are immensely powerful once mastered. A local variable is accessible to all functions within a given individual script instance, and remains persistent until the script is destroyed. For those familiar with object oriented programming, local vars could be considered analogous to class variables.
For example, if you declare a local var in an entity’s animation script, that variable is now available to all functions in that specific entity’s animation script. Animation scripts stay active as long as the entity does, so the local var remains persistent until the entity is killed. When the entity is killed and its animation script destroyed, the local var is destroyed along with it. This means unlike global vars, you don’t need to worry about clean up since the engine will do it for you.
Local vars are ideal for transferring information between functions in a script without using tons of parameters or global level variables. They also are perfect for temporary storage of information across multiple runs of a function(s).
main() {
/* Set local var. */
setlocalvar("test_localvar", "Hello world!");
/* Print local var to log with another function. */
print_log();
}
function print_log() {
char test;
/* Get the local var */
test = getlocalvar("test_localvar");
/* Print text to log. */
log(test);
}
Strings
As noted in types, strings are any arbitrary collection of characters.
- Maximum string length is 127 characters. Any characters exceeding the limit are truncated.
- You may use the
+
operator to concatenate a string with any other variable (the result is always a string).
In addition, the following functions are included to assist in string work.
strinfirst()
char haystack = "Hello World";
char needle = "llo";
char result = strinfirst(haystack, needle);
// result = "llo World";
Accepts a string (haystack) and a search string (needle). Returns string starting from the first occurrence of substring. If substring is not found, returns -1
.
strinlast()
char haystack = "Hello World";
char needle = "o";
char result = strinlast(haystack, needle);
// result = "orld";
Accepts a string (haystack) and a search string (needle). Returns string starting from the last occurrence of substring. If substring is not found, returns -1
.
strleft()
char haystack = "Hello World";
char length = 3;
char result = strleft(haystack, length);
// result = "Hel";
Accepts a string and a length. Returns length characters of string starting from the left.
strright()
char haystack = "Hello World";
char length = 3;
char result = strright(haystack, length);
// result = "rld";
Accepts a string and a length. Returns length characters of string starting from the right.
strlength()
char haystack = "Hello World";
int result = strlength(haystack);
// result = 11;
Accepts a string and returns the number of characters.
strwidth()
char haystack = "Hello World";
int font = <font index>;
int result = strwidth(haystack, font);
Accepts a string and a font index. Returns the total width in pixels. For example, if the font’s character width is 10 pixels and the string has 11 characters, the width returned is 110 pixels.
Arrays
OpenBOR supports indexed and keyed arrays. In actuality, an OpenBOR array is a defined object occupying the global space, so you will need to treat it as such for purposes of initializing, referencing, and cleanup (see free()).
Tips:
- You can use indexed or literal keys, but not both. Mixing key types in the same array can cause undefined behavior and may crash your game.
- Don’t forget indexed keys are 0 indexed.
- You can nest array pointers to create multidimensional data structures.
array()
int size = 1;
void array_pointer = array(size);
Initialize an array and returns reference pointer. You can initialize an array of size 0 and add elements later. However, it is more optimal to initialize with the correct number of elements if you know how many you will need.
size()
int size = size(array_pointer);
Returns number of elements in an array.
get()
mixed index = 0;
mixed value = get(array_pointer, index);
Returns value of array element by index. Index may be an integer or a string value.
set()
mixed index = 0;
mixed value = <some value>;
set(array_pointer, index, value);
Set value of an array element by index. Index may be an integer or string value.
delete()
mixed index = 0;
delete(array_pointer, index);
Removes array element by index, and resizes array accordingly. Index may be an integer or string value.
Tip: If you only want to delete the value and not the element, set a NULL() value instead.
add()
mixed index = 0;
mixed value = <some value>;
add(array_pointer, index, value);
Create a new element at index position (resizing array accordingly), and set its value. Index may be an integer or string value. In the case of a string index, the array becomes literal.
next()
next(array_pointer);
Literal arrays only. Move cursor to next element.
previous()
previous(array_pointer);
Literal arrays only. Move cursor to previous element.
reset()
previous(array_pointer);
Literal arrays only. Move cursor to first element.
value()
mixed value = value(array_pointer);
Literal arrays only. Returns value of current element.
key()
mixed value = key(array_pointer);
Literal arrays only. Returns value of current element.
islast()
int value = islast(array_pointer);
Literal arrays only. Returns true if cursor is at last element.
isFirst()
int value = isfirst(array_pointer);
Literal arrays only. Returns true if cursor is at first element.
isArray()
int value = isarray(variable);
Returns true if the supplied variable is a pointer to an array.
Cleanup
Deleting Variables
To delete any variable, assign it a NULL()
value. OpenBOR will destroy the variable and free the memory it used.
setglobalvar("some_name", NULL())
Freeing Objects
If the variable was pointing to an object of some type (i.e. a pointer), you may first want to delete the object it is pointing to. Otherwise, the object continues to exist in memory with no variable to reference it. To do this, use the free()
function. In the example below, we load a sprite into memory, then delete the sprite, and afterward delete the variable that pointed to it.
void load_sprite() {
/* Load sprite into memory and get its pointer so we can work with it. */
void sprite = loadsprite("data/some_folder/some_sprite.png");
/* Store the pointer into a localvar so other functions can use it. */
setlocalvar("sprite_name", sprite);
}
void free_sprite() {
/* Get the sprite pointer so we can work with it. */
void sprite = getlocalvar("sprite_name");
/* Remove the sprite from memory. */
free(sprite);
/* Delete the variable that held sprite pointer. */
setlocalvar("sprite_name", NULL());
}
Limitations
Variables In Use
There is no hard code limit for the number of variables you may have in use. It’s still a good idea to only allocate what you need and clean up what you don’t, since every active variable consumes memory.
Variable Length
Prior to release 4539, variables in OpenBOR Script were limited to 63 characters in length. Note that’s length, not value – so it really only matters for strings. Should the length exceed 63, the remaining characters are truncated and lost. As of 4539+ the limit is now 127.