Hotspot Scripting
Contents
Overview
Hotspots are the most versatile game object in the Phoenix engine.
It is highly recommended to use hotspots for most of your game logic, when you can help it. They will only affect levels that they have been placed into. Level creators can manually place them inside their levels, and they can mix and match which hotspots they choose to place.
Hotspots can handle either local behavior (when a charater or item is inside the hotspot), or global behavior (since they have script functions that run on all updates, as long as they're placed inside the level). Hotspots can also be programmed to interact with each other.
Hotspots scripts can choose to remain invisible while playing, or can be made to be visible. They can even have fancy drawing, so they look like something that fits into the game, rather than some strange editor object. They can also be set up to draw differently while inside the editor.
The Overgrowth main campaign and Lugaru campaign both use hotspots extensively:
- The Overgrowth campaign checkpoint and goal system uses invisible hotspots
- The Lugaru and Overgrowth campaigns both use them to control music
- Kill boxes use hotspots which listen to "enter" events to kill characters that end up inside them
- Lava and water are both set up using hotspots, and are drawn with special shaders
Occasionally a hotspot is not the right tool for the job. This is especially true when you need to intercept messages that are only passed to level scripts, and that are not (by default) sent to hotspots. However, you can still (carefully) forward these messages to your hotspots by using a level script (http://wiki.wolfire.com/index.php/LevelScripts).
Since hotspots are opt-in, they won't work inside levels that they haven't been placed in. You can get around this by using a mod level hook script to spawn a hotspot inside a given set of levels, or spawn it inside all levels.
Documentation
Documentation of the functions, classes, and object instances that you can access inside a hotspot script are available in an automatically generated file on your hard drive after starting the game for the first time. Find the location for your operating system just below.
- Windows:
My Documents\Wolfire\Overgrowth\aslevel_docs.h
- Mac:
~/Library/Application Support/Overgrowth/aslevel_docs.h
- Linux:
~/.local/share/Overgrowth/aslevel_docs.h
The list of available functions, classes, and object instances can change with each version of the game, so keep checking back on this aslevel_docs.h
file to see the most up to date information.
How to create a hotspot
Hotspots require only a few files to work: The hotspot definition (.XML), and the hotspot script file (.AS).
Hotspot XML file
The XML file your hotspot should be placed inside the your mod's Data/Mods/your_mod_name/Data/Objects/Hotspots/your_mod_name directory. Make sure to add the final your_mod_name so that your hotspot's name won't conflict with other mods that might have a hotspot with the same name.
<?xml version="2.0" ?> <Type>generic</Type> <Hotspot> <BillboardColorMap>Data/UI/spawner/thumbs/Hotspot/empty.png</BillboardColorMap> <Script>hotspots/my_hotspot_script.as</Script> </Hotspot>
The BillboardColorMap value is the relative path (from the base directory) to an image file. This image will be visible inside the hotspot's bounding box while you have the editor active.
The Script value is the relative path (rooted at the Data/Scripts directory) to a script fie. This is the script that will provide the script logic to the hotspot. See the http://wiki.wolfire.com/index.php?title=Hotspots#Hotspot_script_hook_functions section below to see what functions you should make inside your script.
How to set a default scale for the hotspot
TODO: Document how to save a transform for a hotspot (scale, mostly)
Short version: You select the object, and choose Edit -> Save Selection
File path conventions
TODO: Document where to put your hotspot XML and script files
Data/Mods/<your_mod_name>/Objects/Hotspots/<your_mod_name>/my_hotspot.xml
Data/Mods/<your_mod_name>/Scripts/hotspots/<your_mod_name>/my_hotspot.as
<- Note, lowercase "Script/hotspots"
TODO: Document both the "Load" menu, and adding a hotspot to a spawner menu.
Hotspot script hook functions
All functions inside a hotspot script are optional, but will be called if they are present.
GetTypeString function
string GetTypeString() { return "MyHotspotType"; }
Optional
GetTypeString is called when a different script invokes hotspot_instance.GetTypeString()
This is useful for identifying specific hotspots by type in a different script.
You can find hotspots with this type from a different script using code like this:
int num_hotspots = GetNumHotspots(); for(int i=0; i < num_hotspots; ++i) { Hotspot@ hotspot = ReadHotspot(i); if(hotspot.GetTypeString() == "SomeCustomHotspotType") { // ...
This is useful when you want to send messages to the hotspot, to control or move it, to delete it, etc
Init function
void Init()
Optional
Init is Called when the hotspot is first loaded, upon level load.
It is also called when an undo/redo is fired in the editor, to reload script variables from that point in history.
Be careful, this may be called before some objects or script params are present in the level.
It is most useful for setting initial values for file-scope angelscript state.
SetParameters function
void SetParameters()
Optional
SetParameters is called when the hotspot is first loaded, and whenever the script parameter values are changed via the GUI.
It is also called when an undo/redo is fired in the editor, to reload script params from that point in history.
This function allows the hotspot script to work with script parameter values. You can use it to define, change, verify, protect (undo writes), or locally cache script parameter values.
You can also use this to work with game objects that you use hotspot script params to control.
Reset function
void Reset()
Optional
Reset is called whenever the level is reset. This does not happen when a level is first loaded, or when undo/redo are fired in the editor.
This happens when the player hits the "L" debug key to manually reset the level.
This also happens upon player respawn (but this is up to the level script, and modded level scripts might not do that).
Dispose function
void Dispose()
Optional
Dispose is called when the hotspot is destroyed, either by manually deleting it, or when the level is unloaded.
WARNING: Do not DeleteObjectID for other game objects from this function! If you try to, then the game will likely crash on level unload when calling Dispose() on the object you deleted. Also don't try to QueueDeleteObjectID() through Dispose, or it will really mess with undo/redo state.
Used to free any non-game-object resources that you have a handle to that are owned by this hotspot.
One example is Debug Draw objects with _persistent lifetime.
Update function
void Update()
Optional
Update is called regularly by the engine so you can perform work.
It may be useful to do initialization once in this function, if you need objects or level script params to be present.
HandleEvent function
void HandleEvent(string event, MovementObject @mo)
Optional
HandleEvent is triggered when a movement object (usually a character) interacts with this hotspot.
For the most part, this is event = "enter" or event = "exit", when a character enters or leaves the hotspot.
I cannot find any other Hotspot + MovementObject events at this time, but there may be more in the future. (-Merlyn)
HandleEventItem function
void HandleEventItem(string event, ItemObject @obj)
Optional
HandleEventItem is triggered when an item object interacts with this hotspot.
For the most part, this is event = "enter" or event = "exit", when an item enters or leaves the hotspot.
I cannot find any other Hotspot + ItemObject events at this time, but there may be more in the future. (-Merlyn)
ReceiveMessage function
void ReceiveMessage(string)
Optional
ReceiveMessage is called when messages have been sent to this hotspot by the engine, level, or objects in levels.
Objects in levels (such as characters or hotspots) can send this using hotspot.SendMessage("some message string");.
Parameters can be sent by separating them with spaces, and putting quotes around parameters that might contain spaces, then using the TokenIterator object.
PreDraw function
void PreDraw(float curr_game_time)
Optional
PreDraw serves as a before-update function for script-defined drawing. This lets you correct properties of the object before draw is called, so movement, etc, doesn't lag behind by a frame.
Draw function
void Draw()
Optional
Draw serves as an update function for script-defined drawing. This is called both while in editor, and while not in the editor
DrawEditor function
void DrawEditor()
Optional
DrawEditor serves as an update function for drawing. This is called only while the editor is active
Example hotspots
TODO: Update this to something more useful/complete. Multiple examples? Demo editor drawing, and in-game drawing. Demo using enter/exit events, and hotspots that don't use them.
TODO: Demo using a level script to insert a hotspot.
TODO: Demo using a level script fo find hotspots, and forwarding messages.
//Variables can be created outside any function, these will be available to all following functions. float force; int friendly; void Init() { //Inside the init function you can put something that only needs to happen once. Print("Initializing hotspot\n"); } //Inside the SetParameters function you can set declare variables that the user can change. By selecting the hotspot and press Ctrl + U the parameters will be available to the user. void SetParameters() { params.AddString("Upward force", "5.0"); //You can either assign the parameter to a variable straight away or fetch it when it is used. force = params.GetFloat("Upward force"); //Sliders can be usefull for the user to quickly change parameters. params.AddFloatSlider("Recovery Time", 1.0f, "min:0.0,max:50.0"); //You can add checkboxes for simple on/off functions. params.AddIntCheckbox("Friendly", false); friendly = (params.GetInt("Friendly") == 1); } //You can check for a character or an item to enter or exit the hotspot. void HandleEvent(string event, MovementObject @mo){ //Here we check if a character enters the hotspot. if(event == "enter"){ OnEnterChar(mo); } //The function "OnExitChar()" will be called if a character exits teh hotspot else if(event == "exit"){ OnExitChar(mo); } } //The same technique can be used for an item. Note: There is a difference between an ItemObject and an Object. An ItemObject is affected by gravity, such as weapons and collectables. An Object is static for example a hexcrete or joshua tree. void HandleEventItem(string event, ItemObject @obj){ if(event == "enter"){ OnEnterItem(obj); } if(event == "exit"){ OnExitItem(obj); } } void OnEnterItem(ItemObject @obj) { if(obj.GetType() == _collectable){ Print("A " + obj.GetType() + " item has entered the hotspot\n") } //An item can just like a character have it's properties changed via a hotspot. vec3 vel(0.0f,15.5f,0.0f); obj.SetLinearVelocity(vel); } void OnExitItem(ItemObject @obj) { } void OnEnterChar(MovementObject @mo) { //To check if the character is the user you can check if it is a controller MovementObject. if(mo.controlled){ Print("A user has entered the hotspot\n") //If you choose to retrieve the parameter as needed you can request by name. mo.velocity.y = params.GetFloat("Upward force"); } else{ Print("An NPC has entered the hotspot\n"); } //You can create effects with MakeParticle.The first parameter is the particle file to load. The second parameter is the position in the world to spawn this particle. The last parameter is the direction the particle is moving. The higher the values the faster it will move. MakeParticle("Data/Particles/fire.xml", mo.position,vec3(0.0f,15.0f,0.0f)); //Certain functions are only accessible if you go through the Execute() function. These can contain multiple functions and even parameters. mo.Execute("SetOnGround(false);DropWeapon();recovery_time = "+params.GetFloat("Recovery time")+";"); //The level itself can also receive various function level.SendMessage("loadlevel \"/Data/Levels/sea_cliffs.xml"); //To put some text on the screen in stead of the console you can use. level.SendMessage("displaytext Test Message"); //And to clear it. level.SendMessage("cleartext"); //You can also reset the level. level.SendMessage("reset"); //If you want to put an image onto the screen level.SendMessage("displayhud /Data/Textures/twisted_paths.png"); //And to remove this again use level.SendMessage("clearhud"); //To play a sound you use the PlaySound function. The second parameter to specify the location is optional. Make sure the audio file mono or else the positional effect won't work. PlaySound("Data/Sounds/fall_swoosh.wav", mo.position); //To get a random value you can use RangedRandomFloat. This will be usefull for example with particle effects. float direction = RangedRandomFloat(-100.0f,100.0f); //To spawn objects you can use the CreateObject function. This can be an Object, ItemObject or Character. int objectid = CreateObject("Data/Objects/rock.xml"); //The function return an id number which you can use to get the Object. Now the Object can be manipulated. Object@ objectid = ReadObjectFromID(obj_id); //If the object is character use the MovementObject type. MovementObject@ character = ReadCharacterID(objectid); //If the object is a plant use the EnvObject type. EnvObject@ plant = ReadEnvObjectID(objectid); //If the object is weapon or collectable use the ItemObject type. ItemObject@ weapon = ReadItemID(objectid); //The hotspot is also an object inside the game. You can also move and resize it. Object@ this_hotspot = ReadObjectFromID(hotspot.GetID()); } void OnExitChar(MovementObject @mo) { } //The Update() function will continue to execute as long as the hotspot is loaded. It does not need to be triggered. void Update() { }