A to Z Scripting: Scriptobjects: Difference between revisions
Created page with "== Scriptobjects == Scriptobjects are sets of scripts that are assigned to individual entities. They're mainly used for making entities with scripted behaviours (i.e. a heali..." |
mNo edit summary |
||
(16 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
== Scriptobjects == | == Scriptobjects == | ||
Scriptobjects are sets of scripts that are assigned to individual entities. They're | Scriptobjects are sets of scripts that are assigned to individual entities. They're used for making entities with scripted behaviours (i.e. a healing fountain) and for entities that carry out scripted effects (i.e. atdm:target_setteam, which changes the team of targeted AIs when triggered). | ||
They allow many entities to carry the same, complex scripts without conflicting, since each entity has its own values for the variables. They are also well suited for taking spawnargs into account, allowing mappers to change how the scripts play out for each entity. An entity can only have one scriptobject. | They allow many entities to carry the same, complex scripts without conflicting, since each entity has its own values for the variables. They are also well suited for taking spawnargs into account, allowing mappers to change how the scripts play out for each entity. An entity can only have one scriptobject. | ||
A scriptobject typically has these 3 sections: | |||
* The object definition, where all | * The object definition (mandatory), where all scripts must be defined and variables can be defined | ||
* The init() script, which is called when the entity spawns. Used for initial setup, such as determining what script to call when triggered | * The init() script (optional), which is called when the entity spawns. Used for initial setup, such as determining what script to call when triggered | ||
* The | * The scripts that perform the main function of the scriptobject. | ||
Optional, but recommended, is to create an entity definition. This allows you and others to easily create entities in DR that have your scriptobject and all relevant spawnargs, including tooltips. | Optional, but recommended, is to create an entity definition. This allows you and others to easily create entities in DR that have your scriptobject and all relevant spawnargs, including tooltips. | ||
A further option is to create a RestoreScriptObject() script in the scriptobject, which gets called whenever a savegame is loaded. | |||
.script files with scriptobjects should be stored in the /script folder and #included in tdm_custom_scripts.script. This is because saving/loading will no longer work if the map .script contains scriptobjects. | |||
* All | === Part 1: object definition === | ||
* All scripts have to be defined in advance here. You may also create variables here if they should be accessible to all the scriptobject's scripts. Note that it's not possible to assign values to variables or run script events yet. | |||
Example: | Example: | ||
Line 22: | Line 24: | ||
{ | { | ||
//define the scripts | //define the scripts | ||
void init(); // | void init(); //for performing initial setup | ||
void heal_player(); //the script that does the healing | void heal_player(); //the script that does the healing | ||
//define all | //define any variables here if they should be common to all the object's scripts, or if their value should persist | ||
//my preference is to | //my preference is to also list spawnargs that get used in the scripts | ||
//SPAWNARGS | //SPAWNARGS | ||
//heal_amount //the fountain will heal the player by this amount | |||
//cooldown //fountain will need this amount of time to recharge | |||
//snd_heal //healing sound; no need to define as a variable, because sound events are started directly from the spawnarg | //snd_heal //healing sound; no need to define as a variable, because sound events are started directly from the spawnarg | ||
//snd_inactive //sound played when player frobs the fountain during the cooldown period | //snd_inactive //sound played when player frobs the fountain during the cooldown period | ||
// | //NON-SPAWNARGS | ||
float active; //whether the fountain will heal the player when frobbed | float active; //whether the fountain will heal the player when frobbed | ||
float last_used; //time on the game clock when the fountain was last used; for cooldown period | float last_used; //time on the game clock when the fountain was last used; for cooldown period | ||
Line 41: | Line 43: | ||
=== Part 2: init() === | |||
* This script is called when the entity spawns and is used to perform initial setup. That includes retrieving values of spawnargs or setting up the entity to respond to trigger events. | * This script is called when the entity spawns and is used to perform initial setup. That includes retrieving values of spawnargs or setting up the entity to respond to trigger events. | ||
* Alternatively you can retrieve spawnarg values later in the actual scripts each time they run, if you'd like to allow mappers to change spawnargs after the map has started. | * Alternatively you can retrieve spawnarg values later in the actual scripts each time they run, if you'd like to allow mappers to change spawnargs after the map has started. | ||
* Since scriptobjects are entity-specific, all events are called on self by default, so you can just write i.e. getFloatKey("cooldown"); | * Since scriptobjects are entity-specific, all events are called on self by default, so you can just write i.e. getFloatKey("cooldown"); | ||
void healing_fountain::init() | void healing_fountain::init() //init() function of the healing_fountain scriptobject | ||
{ | { | ||
ResponseAdd(STIM_FROB); //setup a response to frobbing that calls "heal_player" and enable it | |||
ResponseSetAction(STIM_FROB,"heal_player"); | |||
ResponseEnable(STIM_FROB,1); | |||
active = 1; //assign a starting value to this variable | |||
active | |||
} | } | ||
=== Part 3: the actual scripts === | |||
*From now on this is almost like regular scripting and can be as simple or complex as you like. There is some added convenience: | *From now on this is almost like regular scripting and can be as simple or complex as you like. There is some added convenience: | ||
** all events are called on self by default, and you can use self as an input parameter. | ** all events are called on self by default, and you can use self as an input parameter. | ||
Line 68: | Line 68: | ||
elseif (!active) //if the fountain is inactive, check if enough time has passed to reactivate it... | elseif (!active) //if the fountain is inactive, check if enough time has passed to reactivate it... | ||
{ | { | ||
if ( (last_used + cooldown) > sys.getTime() ) active = 1; | if ( (last_used + getFloatKey("cooldown")) > sys.getTime() ) active = 1; | ||
} | } | ||
if (active) //if the fountain is (now) ready, heal the player | if (active) //if the fountain is (now) ready, heal the player | ||
{ | { | ||
health_target = $player1.getHealth() + heal_amount; | health_target = $player1.getHealth() + getFloatKey("heal_amount"); //calculate how much health the player will have | ||
if(health_target > 100) health_target = 100; //limit the new health to max 100 (not strictly necessary for setHealth()) | if(health_target > 100) health_target = 100; //limit the new health to max 100 (not strictly necessary for setHealth()) | ||
Line 87: | Line 88: | ||
{ | { | ||
startSound("snd_inactive", SND_CHANNEL_ANY, false); //play inactive sound | startSound("snd_inactive", SND_CHANNEL_ANY, false); //play inactive sound | ||
ResponseEnable(STIM_FROB,0); //disable the frob response for 3s to prevent sound spamming | |||
sys.wait(3); | |||
ResponseEnable(STIM_FROB,1); | |||
return; //abort | return; //abort | ||
} | } | ||
} | } | ||
=== Recommended: the entity definition === | |||
Scriptobjects are made to be used by entities, so it definitely makes sense to create a premade entity with all the correct spawnargs already set by making an entity definition. | |||
The entity definition allows you to: | |||
*Define tooltips and types for any spawnarg. If you define a spawnarg as a "model" spawnarg, it'll have a "Choose model..." button. | |||
*Set default values for spawnargs, making them show up in "inherited properties". | |||
*Set values for spawnargs in the same way as a mapper would. This makes them show up even if "inherited properties" are hidden. | |||
You can find example entity definitions near the bottom of this article as well as in tdm_defs01.pk4. | |||
== Calling individual scripts of a scriptobject == | |||
You can directly call a script that's part of a scriptobject in one of the following ways: | |||
* calling callFunction() on the entity carrying the scriptobject, i.e. $healing_fountain_1.callFunction("heal_player") | |||
* [[Atdm:target callobjectfunction]] entity, targeting the entity carrying the scriptobject and with a "call" spawnarg referencing the script, i.e. in the case of the healing fountain "call" "heal_player". The spawnargs "pass_self" and "pass_activator" allow passing entities. | |||
You may even want to create special scripts within the scriptobject explicitly for being called externally in this way, for example if you want to force the healing fountain in the above example to become "active" immediately. | |||
== Accessing variables of a scriptobject in a global script == | |||
If you want to access the variables of this scriptobject from a global script, i.e. if you want to check whether a specific fountain is "active" or not, you will need to make the variables visible in some way: | |||
* The easiest way is to let the scriptobject set custom spawnargs on its entity, i.e. setKey("active", "1"). Any other script can access that. | |||
* Alternatively, you can use the approach seen earlier for accessing "AI flags". You can create a variable where the the fountain is defined as a subcategory of entity named after the scriptobject. Example: | |||
healing_fountain fountain = $fountain_1; //define $fountain_1 as a variable of type "healing_fountain" (identical to the scriptobject's name | |||
Then: | Then: | ||
if ( fountain.active ) sys.println(fountain + " is ready to heal the player."); | if ( fountain.active ) sys.println(fountain + " is ready to heal the player."); | ||
* A further option is to create scripts in the scriptobject that return the current value of a specific variable, similar to a script event. See [[A to Z Scripting: Utility scripts]] for more on using "return" for this purpose. Example: | |||
float healing_fountain::isActive() | |||
{ | |||
return active; | |||
} | |||
== Inheriting from an existing scriptobject == | |||
Just like entities, scriptobjects can inherit from other scriptobjects. This is useful for building on an existing scriptobject without duplicating much of the code, for example if you already had a spider and now wanted to make a jumping spider with additional scripts controlling its behaviour. | |||
This would be the method for letting a new scriptobject inherit from an existing one, i.e. defiled_fountain from healing_fountain: | |||
object defiled_fountain : healing_fountain | |||
{ | |||
... | |||
}; | |||
It should also be possible to define a DerivedInit() on the parent scriptobject that gets called by all children scriptobjects when their entities spawn, but this guide doesn't have a method for this at this time. | |||
== Example: atdm:target_unbind, for unbinding targeted entities when triggered == | |||
This will create a small box-like entity for use in DR. When it's triggered by something, it'll run a script on all its targets that unbinds them from whatever they're bound to. It's similar to i.e. atdm:target_setteam, which changes the team of all targeted AIs when it's triggered. | This will create a small box-like entity for use in DR. When it's triggered by something, it'll run a script on all its targets that unbinds them from whatever they're bound to. It's similar to i.e. atdm:target_setteam, which changes the team of all targeted AIs when it's triggered. | ||
=== The script === | |||
As an example, this script could be stored in tdm_target_unbind.script in the script folder, and #included in tdm_custom_scripts.script. | As an example, this script could be stored in tdm_target_unbind.script in the script folder, and #included in tdm_custom_scripts.script. | ||
Line 132: | Line 157: | ||
void unbind_targets(); //performs the unbinding | void unbind_targets(); //performs the unbinding | ||
// | //SPAWNARGS | ||
//delay //wait this amount before performing the unbind; value comes from a spawnarg | |||
}; //semicolon needed after the closing curly bracket of a scriptobject definition | }; //semicolon needed after the closing curly bracket of a scriptobject definition | ||
void target_unbind::init() //initial setup; gets called automatically when this entity spawns | void target_unbind::init() //initial setup; gets called automatically when this entity spawns | ||
{ | { | ||
sys.onSignal(SIG_TRIGGER, self, "unbind_targets"); //when receiving a trigger signal on this entity, call the script unbind_targets() (contained in this scriptobject) | sys.onSignal(SIG_TRIGGER, self, "target_unbind::unbind_targets"); //when receiving a trigger signal on this entity, call the script unbind_targets() (contained in this scriptobject) | ||
} | } | ||
void target_unbind::unbind_targets() //performs the unbinding | void target_unbind::unbind_targets() //performs the unbinding | ||
{ | { | ||
sys.wait(delay); | sys.wait(getFloatKey("delay")); //wait for "delay" seconds before running the script | ||
float i; //used for cycling through all targets of this entity with for() | |||
entity target; //a target of this entity | |||
for (i = 0; i < numTargets() ; i++) //repeat for every target of this entity | for (i = 0; i < numTargets() ; i++) //repeat for every target of this entity | ||
Line 155: | Line 180: | ||
} | } | ||
=== The entity definition === | |||
An entity definition allows you to get an entity carrying this scriptobject listed in DR's "Create entity" menu, allowing you and others to easily use it in your map. Create a .txt file in the def folder and change the extension to .def, the name doesn't matter. | An entity definition allows you to get an entity carrying this scriptobject listed in DR's "Create entity" menu, allowing you and others to easily use it in your map. Create a .txt file in the def folder and change the extension to .def, the name doesn't matter. | ||
Line 182: | Line 207: | ||
Your entity is now ready to be used in DR, performing unbind() on all its targets when triggered. | Your entity is now ready to be used in DR, performing unbind() on all its targets when triggered. | ||
== See also == | |||
[[Writing Script Objects]] | |||
== Next / previous article == | == Next / previous article == | ||
*Next article: [[A to Z Scripting: | *Next article: [[A to Z Scripting: Script addons for players]] | ||
*Previous article: [[A to Z Scripting: | *Previous article: [[A to Z Scripting: Utility scripts]] | ||
*Table of contents: [[A to Z Scripting]] | *Table of contents: [[A to Z Scripting]] | ||
[[Category:Scripting]] |
Latest revision as of 07:52, 11 March 2021
Scriptobjects
Scriptobjects are sets of scripts that are assigned to individual entities. They're used for making entities with scripted behaviours (i.e. a healing fountain) and for entities that carry out scripted effects (i.e. atdm:target_setteam, which changes the team of targeted AIs when triggered).
They allow many entities to carry the same, complex scripts without conflicting, since each entity has its own values for the variables. They are also well suited for taking spawnargs into account, allowing mappers to change how the scripts play out for each entity. An entity can only have one scriptobject.
A scriptobject typically has these 3 sections:
- The object definition (mandatory), where all scripts must be defined and variables can be defined
- The init() script (optional), which is called when the entity spawns. Used for initial setup, such as determining what script to call when triggered
- The scripts that perform the main function of the scriptobject.
Optional, but recommended, is to create an entity definition. This allows you and others to easily create entities in DR that have your scriptobject and all relevant spawnargs, including tooltips.
A further option is to create a RestoreScriptObject() script in the scriptobject, which gets called whenever a savegame is loaded.
.script files with scriptobjects should be stored in the /script folder and #included in tdm_custom_scripts.script. This is because saving/loading will no longer work if the map .script contains scriptobjects.
Part 1: object definition
- All scripts have to be defined in advance here. You may also create variables here if they should be accessible to all the scriptobject's scripts. Note that it's not possible to assign values to variables or run script events yet.
Example:
object healing_fountain //scriptobject for a fountain that heals the player when frobbrd { //define the scripts void init(); //for performing initial setup void heal_player(); //the script that does the healing //define any variables here if they should be common to all the object's scripts, or if their value should persist //my preference is to also list spawnargs that get used in the scripts //SPAWNARGS //heal_amount //the fountain will heal the player by this amount //cooldown //fountain will need this amount of time to recharge //snd_heal //healing sound; no need to define as a variable, because sound events are started directly from the spawnarg //snd_inactive //sound played when player frobs the fountain during the cooldown period //NON-SPAWNARGS float active; //whether the fountain will heal the player when frobbed float last_used; //time on the game clock when the fountain was last used; for cooldown period float health_target; //how much health the player will heal to };
Part 2: init()
- This script is called when the entity spawns and is used to perform initial setup. That includes retrieving values of spawnargs or setting up the entity to respond to trigger events.
- Alternatively you can retrieve spawnarg values later in the actual scripts each time they run, if you'd like to allow mappers to change spawnargs after the map has started.
- Since scriptobjects are entity-specific, all events are called on self by default, so you can just write i.e. getFloatKey("cooldown");
void healing_fountain::init() //init() function of the healing_fountain scriptobject { ResponseAdd(STIM_FROB); //setup a response to frobbing that calls "heal_player" and enable it ResponseSetAction(STIM_FROB,"heal_player"); ResponseEnable(STIM_FROB,1); active = 1; //assign a starting value to this variable }
Part 3: the actual scripts
- From now on this is almost like regular scripting and can be as simple or complex as you like. There is some added convenience:
- all events are called on self by default, and you can use self as an input parameter.
- Any script can call any other script, regardless of which one is written first. This is because you've already named them all in the object definition.
void healing_fountain::heal_player() { if ($player1.getHealth == 100) return; //do nothing if the player is at full health elseif (!active) //if the fountain is inactive, check if enough time has passed to reactivate it... { if ( (last_used + getFloatKey("cooldown")) > sys.getTime() ) active = 1; } if (active) //if the fountain is (now) ready, heal the player { health_target = $player1.getHealth() + getFloatKey("heal_amount"); //calculate how much health the player will have if(health_target > 100) health_target = 100; //limit the new health to max 100 (not strictly necessary for setHealth()) $player1.setHealth(health_target); //heal the player startSound("snd_heal", SND_CHANNEL_ANY, false); //play a heal sound on the fountain, using the "snd_heal" spawnarg on the fountain active = 0; //fountain is now inactive until it has recharged last_used = sys.getTime(); //keep track of when this fountain was last used } else //fountain is not ready; play inactive sound and abort { startSound("snd_inactive", SND_CHANNEL_ANY, false); //play inactive sound ResponseEnable(STIM_FROB,0); //disable the frob response for 3s to prevent sound spamming sys.wait(3); ResponseEnable(STIM_FROB,1); return; //abort } }
Recommended: the entity definition
Scriptobjects are made to be used by entities, so it definitely makes sense to create a premade entity with all the correct spawnargs already set by making an entity definition.
The entity definition allows you to:
- Define tooltips and types for any spawnarg. If you define a spawnarg as a "model" spawnarg, it'll have a "Choose model..." button.
- Set default values for spawnargs, making them show up in "inherited properties".
- Set values for spawnargs in the same way as a mapper would. This makes them show up even if "inherited properties" are hidden.
You can find example entity definitions near the bottom of this article as well as in tdm_defs01.pk4.
Calling individual scripts of a scriptobject
You can directly call a script that's part of a scriptobject in one of the following ways:
- calling callFunction() on the entity carrying the scriptobject, i.e. $healing_fountain_1.callFunction("heal_player")
- Atdm:target callobjectfunction entity, targeting the entity carrying the scriptobject and with a "call" spawnarg referencing the script, i.e. in the case of the healing fountain "call" "heal_player". The spawnargs "pass_self" and "pass_activator" allow passing entities.
You may even want to create special scripts within the scriptobject explicitly for being called externally in this way, for example if you want to force the healing fountain in the above example to become "active" immediately.
Accessing variables of a scriptobject in a global script
If you want to access the variables of this scriptobject from a global script, i.e. if you want to check whether a specific fountain is "active" or not, you will need to make the variables visible in some way:
- The easiest way is to let the scriptobject set custom spawnargs on its entity, i.e. setKey("active", "1"). Any other script can access that.
- Alternatively, you can use the approach seen earlier for accessing "AI flags". You can create a variable where the the fountain is defined as a subcategory of entity named after the scriptobject. Example:
healing_fountain fountain = $fountain_1; //define $fountain_1 as a variable of type "healing_fountain" (identical to the scriptobject's name
Then:
if ( fountain.active ) sys.println(fountain + " is ready to heal the player.");
- A further option is to create scripts in the scriptobject that return the current value of a specific variable, similar to a script event. See A to Z Scripting: Utility scripts for more on using "return" for this purpose. Example:
float healing_fountain::isActive() { return active; }
Inheriting from an existing scriptobject
Just like entities, scriptobjects can inherit from other scriptobjects. This is useful for building on an existing scriptobject without duplicating much of the code, for example if you already had a spider and now wanted to make a jumping spider with additional scripts controlling its behaviour.
This would be the method for letting a new scriptobject inherit from an existing one, i.e. defiled_fountain from healing_fountain:
object defiled_fountain : healing_fountain { ... };
It should also be possible to define a DerivedInit() on the parent scriptobject that gets called by all children scriptobjects when their entities spawn, but this guide doesn't have a method for this at this time.
Example: atdm:target_unbind, for unbinding targeted entities when triggered
This will create a small box-like entity for use in DR. When it's triggered by something, it'll run a script on all its targets that unbinds them from whatever they're bound to. It's similar to i.e. atdm:target_setteam, which changes the team of all targeted AIs when it's triggered.
The script
As an example, this script could be stored in tdm_target_unbind.script in the script folder, and #included in tdm_custom_scripts.script.
object target_unbind //create a new scriptobject { //Name the scripts belonging to this scriptobject void init(); //mandatory; performs initial setup, mainly to make the entity respond to trigger signals by calling unbind_targets() void unbind_targets(); //performs the unbinding //SPAWNARGS //delay //wait this amount before performing the unbind; value comes from a spawnarg }; //semicolon needed after the closing curly bracket of a scriptobject definition void target_unbind::init() //initial setup; gets called automatically when this entity spawns { sys.onSignal(SIG_TRIGGER, self, "target_unbind::unbind_targets"); //when receiving a trigger signal on this entity, call the script unbind_targets() (contained in this scriptobject) } void target_unbind::unbind_targets() //performs the unbinding { sys.wait(getFloatKey("delay")); //wait for "delay" seconds before running the script float i; //used for cycling through all targets of this entity with for() entity target; //a target of this entity for (i = 0; i < numTargets() ; i++) //repeat for every target of this entity { target = getTarget(i); //get the next target of this entity target.unbind(); //run the unbind() script event on the target } }
The entity definition
An entity definition allows you to get an entity carrying this scriptobject listed in DR's "Create entity" menu, allowing you and others to easily use it in your map. Create a .txt file in the def folder and change the extension to .def, the name doesn't matter.
entityDef atdm:target_unbind { "inherit" "atdm:entity_base" //inherit from a basic entity "spawnclass" "idTarget" //use a generic spawnclass without too much inbuilt coding "scriptobject" "target_unbind" //assign this scriptobject "editor_displayFolder" "Targets" //entity will be listed in the Targets folder in DR "editor_color" "0.3 0.1 0.6" //borrow the appearance from the trigger_relay entity (purple box) "editor_mins" "-8 -8 -8" //dimensions (bottom left corner) "editor_maxs" "8 8 8" //dimensions (top right corner) "editor_material" "textures/common/trigrelay" "editor_usage" "Unbinds all targeted entities when triggered." //description, shown in the Create entity menu "editor_usage1" "Multi-line descriptions..." "editor_usage2" "... are done like this." "editor_float delay" "Optional. Delay action by this amount in seconds." //tell DR that "delay" is a float and the tooltip "delay" "0" //delay will inherit a value of 0 (shown in inherited properties) //"editor_setKeyValue delay" "0" //alternatively, delay will automatically be assigned a value of 0 as if a mapper had done it }
Your entity is now ready to be used in DR, performing unbind() on all its targets when triggered.
See also
Next / previous article
- Next article: A to Z Scripting: Script addons for players
- Previous article: A to Z Scripting: Utility scripts
- Table of contents: A to Z Scripting