Hotspot Scripting

From Wolfire Games Wiki
Revision as of 23:56, 25 August 2017 by Merlyn (talk | contribs) (Init function)
Jump to: navigation, search

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

TODO: Document the hotspot XML format

TODO: Docuemnt how to save a transform for a hotspot (scale, mostly)

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"

How to add a hotspot to the spawner menu

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()

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() { }
// This 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()
// 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 other object.
//   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()
// 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)
// 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.

HandleEventItem function

void HandleEventItem(string event, ItemObject @obj)
// 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.

ReceiveMessage function

void ReceiveMessage(string) { }
// 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) { }
// 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() { }
// 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() { }
// 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() {    

}