This section describes how level scripts can be implemented. The first things to know is how the game engine and the script interact with each other.
First, the game engine will call pre-defined functions on particular events. The script then can (but doesn't need to) implement these functions to implement some level-specific logic. Here's a brief overview of the functions:
Additionally, every level script has access to a global variable called gameControl that can be used wherever you want. Actually this variable represents an interface (a collection of methods) that can be called in Java-style syntax. Here are some examples for the most important methods:
Please refer to the JavaDoc of the interfaces in the moagg.model.object package to get an overview of the available interfaces that any game object may implement.
In addition to the gameControl variable, the file common.js will automatically be included to every level script. It contains different helper-functions to make the life of a script writer a bit easier.
To fully understand level scripting, you should take a look at the tutorial levels (or levels of the other missions).
The first tutorial level is quite simple. You just have to land the ship on the second platform.
<gamecontrol> <![CDATA[ function init() { addLandOnPlatformObjective(2); } function onObjectiveReached() { gameControl.finish(); } ]]> </gamecontrol>
The first thing that comes into view is the CDATA XML-block wrapping the actual script. It's sole purpose is that you don't need to escape special characters within the script.
When looking at the init() function, you can see that a helper-function is called to add a "land on platform" objective to the level. This is one of the helper-functions that is defined in common.js . The argument 2 refers to the id of a platform defined in the playground section of the level.
Finally, the onObjectiveReached() function just calls gameControl.finish() . That means, the level will finish after the ship has landed on the platform with the id 2.
In the next example tutorial, you have to pick up some crates and bring them back to the home platform.
function init() { for (var id = 10; id <= 15; id++) { addBringCrateToPlatformObjective(id, 1); } } function onObjectiveReached() { gameControl.finish(); }
The content of the level script is quite similar to the first example. The main difference however is that six "bring crate to platform" objectives with respective crate id s are created in the init() function using a for loop. That means, only after all six crates were brought back to the home platform (with the id 1) the level will finish.
This tutorial demonstrates how to connect switches to magnets and barriers so they are turned on/off when the switches are hit by a projectile.
function init() { for (var id = 10; id <= 13; id++) { addBringCrateToPlatformObjective(id, 1); } } function onSwitch(s) { switch (s.getID()) { case 30: gameControl.getObjectById(22).toggle(); break; case 31: gameControl.getObjectById(23).toggle(); break; case 32: gameControl.getObjectById(22).toggle(); gameControl.getObjectById(23).toggle(); gameControl.getObjectById(25).toggle(); break; case 33: gameControl.getObjectById(22).toggle(); gameControl.getObjectById(23).toggle(); gameControl.getObjectById(26).toggle(); break; case 34: gameControl.getObjectById(20).toggle(); gameControl.getObjectById(22).toggle(); break; case 35: gameControl.getObjectById(21).toggle(); gameControl.getObjectById(23).toggle(); break; } } function onObjectiveReached() { gameControl.finish(); }
Remember that the onSwitch() function is called whenever a switch is hit by a projectile. What then happens in the function body is just a simple switch/case decision block, where dependend on the id of the switch particular objects (magnets and barriers) are toggled. This is done by getting the objects by their id and calling the toggle() method on them, which takes care of changing the state of the object between "activated" and "deactivated".
The most important point to know about level scripting is, that the scripting language allows you to call any methods of any object you retrieved via gameControl.getObjectById() . Regarding best practices, it's recommended to use only methods from the interfaces defined in the moagg.model.object package, because these methods ought to be documented and stable.
This tutorial demonstrates how to create a new objective after the initial objectives are reached.
var state = 0; function init() { addDestroyTargetObjective(10); addDestroyTargetObjective(11); addDestroyTargetObjective(12); } function onObjectiveReached() { switch (state) { case 0: addLandOnPlatformObjective(2); state = 1; break; case 1: gameControl.finish(); break; } }
When playing through this level, the onObjectiveReached() function is called two times: when all three turrets are destroyed and when the ship lands on the target platform. The variable state is used to distinguish these two calls.
Question: Try to think about what would happen if the script looks like this:
function init() { addDestroyTargetObjective(10); addDestroyTargetObjective(11); addDestroyTargetObjective(12); addLandOnPlatformObjective(2); } function onObjectiveReached() { gameControl.finish(); }
Answer: The level can still be solved by destroying the turrets and landing on the target platform. But the behavior differs if you land on the platform before destroying the turrets. In this case, the level would finish after you destroyed the last turret. Since we want the player to land on the platform after the turrets are destroyed, the objectives must be split up like shown in the original example.
This tutorial demonstrates one possible way to create objectives one-by-one.
<gamecontrol> <![CDATA[ var crateIds = [10, 13, 11, 14, 15, 12]; var platformIds = [ 1, 1, 1, 1, 1, 1]; var deactivateIds = [23, 21, 24, 25, 22, -1]; var activateIds = [20, 23, 21, 24, 25, 22]; var currentObjective = 0; function initObjective() { addBringCrateToPlatformObjective( crateIds[currentObjective], platformIds[currentObjective]); } function init() { initObjective(); } function onCratePickup() { var id = deactivateIds[currentObjective]; if (id != -1) { gameControl.getObjectById(id).deactivate(); } } function onObjectiveReached() { gameControl.getObjectById(activateIds[currentObjective]).activate(); if (++currentObjective < 6) { initObjective(); } else { gameControl.finish(); } } ]]> </gamecontrol>
Remember that the onCratePickup() function is called whenever a crate is picked up. What happens here is that the laser barrier to the next crate is deactivated. As soon as the crate is brought home, the laser barrier that protected this crate is activated again and the next objective is created until all crates are brought home.
The important points of this script are:
There's no easy way to debug a level script similar to debugging Java code. However you can add logging statements to your level script using the global variable log in the same syntax as you would do in Java using the log4j Logger interface.
function onObjectiveReached() { log.trace("onObjectiveReached(): currentObjective = " + currentObjective); gameControl.getObjectById(activateIds[currentObjective]).activate(); if (++currentObjective < 6) { log.debug("onObjectiveReached(): initializing next objective"); initObjective(); } else { log.info("onObjectiveReached(): finishing level"); gameControl.finish(); } }
The only thing to keep in mind is that the SCRIPTING logger must be configured properly. The log4j.properties file already contains a commented out line for this.