Other great resources: Official JS API docs, Scripting Forum
Common Mistakes
A summary of common issues you *will* run in to whilst developing against the JS API...
Error exceptions
Many of the Functions will throw an exception (generic Error object) if not properly used. If you don't trap the exception, your script could crash.
Wherever possible, this guide indicates which functions might throw an error (and also which functions will never throw an error – for example, include()) in the "Return value" section of the function's documentation page.
For more information on errors and handling them, see:
Error Handling – short guide to creating and dealing with errors
Javascript Error object – a great article on the Mozilla Developer Network (MDN)
try ... catch statements – how to trap and handle errors, again on the MDN
Global variables
When a game is saved, Warzone attempts to persist all properties on the global object in to the save game file, with the following exceptions:
Constants are not persisted
Variables that directly reference a function are not persisted
Variables that reference objects containing functions will break
For example:
const MY_CONST = 1; // this will not be saved
var foo = "bar"; // this will be saved
var myObj = {foo:"bar",baz:"boz"}; // this will be saved
var broken = {foo:function(){}}; // this will break
var myFunc = function(){}; // this will not be saved
function anotherFunc(){}; // this will not be savedIf you want to have some transient variables, just internal or temporary stuff that shouldn't get saved, you can store them as properties on a function, like this:
// functions aren't persisted in save games
function temp() {};
// so we can safely store whatever we want there
temp.foo = "bar" // this won't get savedNote that there is no implementation of localStorage or sessionStorage in the Warzone environment. However, it might be good practice if you create your own sessionStorage object and discipline yourself to store persistable data there:
// create our fake sessionStorage container
global.sessionStorage = {};
// if you want anything saving, put it in sessionStorage
sessionStorage.foo = "bar";
// but remember it will break if you put functions in
sessionStorage.foo = function(){}; // this will break session storage!!
// and condition yourself to never store any vars on global!
var naughtyBoy = "don't store any other vars on global!"; // because they will add cruft to savegamesBy training yourself to store stuff in a sessionStorage object, your code will be more transparent and easier to understand because it will be obvious to you and others what you intend to go in to save game files.
You can test what actually gets stored by looking in Saved game files.
Case sensitivity for globals
Due to a bug that is not so easy to fix in the short term, variables defined in global must be case insensitive otherwise they may collide on savegame loading (the data format in saved game files is not case sensitive and it's non-trivial to fix that at the moment). This is only for variables defined in your script.
There is no need to maintain case insensitivity in regards to variables defined by the game itself, such as ’FACTORY’ (you can safely define your own ’factory’ variable) – the only exception to this is ’me’.
setTimer() and queue() functions
Both of these functions have a function parameter – but you can't pass an actual function as that parameter. This is because functions can't be persisted when saving the game. You have to pass the name of the function, as a string – and that function has to exist in the global scope, it can't be a method of an object.
function myFunc(){};
setTimer(myFunc,5000); // this will break!
setTimer("myFunc",5000); // this will work, so long as myFunc() is a global functionThe functions also have an optional object parameter – but you can't pass any old object in, you can only pass Game objects like a droid or structure object. When a game gets saved, only the object's .id and .player properties are stored in the save game file. If you pass any other type of object, array or value, things will generally break.
Working with lists returned by enumWhatever() functions
When you want to iterate through lists of objects returned by the various enumWhatever() functions (for example: enumDroid() or enumGroup()) don't use "for..in" loops - they won't work on the lists returned by those functions. Instead, use something like this:
var droidList = enumDroid(me,DROID_CONSTRUCT); // get a list of trucks
// basic way of iterating through the list:
for (var i = 0; i < droidList.length; i++) {
var droid = droidList[i];
// ... do stuff with the droid ...
}
// a much better approach IMHO:
var doStuffWithDroid = function(droid) {
// ... do stuff with the droid ...
}
droidList.forEach(doStuffWithDroid);The above code gets a list of all your construction droids (trucks), and iterates over them one by one.
Game Objects are snapshots
Game objects are just snapshots of the game data at a specific point in time. So, the properties of these objects won't update itself at all. For example, if you get a droid object it's .x and .y properties won't change if you move it across the map. You'll have to keep getting a fresh copy of the game object if you want to see up-to-date information.
Data synchronisation between players on a map
Most object states that are changed from the scripts are not in fact changed, but queued up to be synchronized among clients.
This means that you cannot check the state later in the same script invokation and expect it to have changed.
Instead, if for example you want to mark droids that have been ordered to do something, you can mark them by adding a custom property. Note that this property will not be remembered when it goes out of scope (eg. save then load game = properties will be gone).
Initial research (on starting/loading a game)
You cannot set research topics for research labs directly from eventStartLevel() Instead, queue() up a function call to set it at some later frame.
Cyborg constructions
Cyborg components are inter-linked, and cannot be passed in as lists as you can with ordinary droids, even though they might look that way.