A to Z Scripting: More scripting basics: Difference between revisions
Created page with "== More scripting basics == === General === * Scripting is case-sensitive. * The scripting engine reads from top to bottom. That makes the order in which scripts and variables..." |
mNo edit summary |
||
(29 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
== | == Working tidily == | ||
=== General | Rule #1 is to keep your scripts tidy and well-commented so they're easy to follow. That includes making liberal use of the 'tab' key, which lets you quickly align your lines. | ||
Comparison 1: script for reading spawnargs that the mapper has set on a func_mover entity | |||
//bad | |||
vector mover_translate = $wardrobe.getVectorKey("translate"); | |||
vector mover_rotate = $wardrobe.getVectorKey("rotate"); | |||
float mover_time = $wardrobe.getFloatKey("move_time"); | |||
//better | |||
vector mover_translate = $wardrobe.getVectorKey("translate"); | |||
vector mover_rotate = $wardrobe.getVectorKey("rotate"); | |||
float mover_time = $wardrobe.getFloatKey("move_time"); //time taken to move | |||
Comparison 2: script for constantly checking if an AI can see the player | |||
//bad | |||
void watch_for_player() | |||
{ | |||
while($player1.getLocation() == $master_bedroom) | |||
{ | |||
if($lord_marlow.canSee($player1)) | |||
{ | |||
$lord_marlow.bark("snd_i_see_you"); | |||
return; | |||
} | |||
sys.wait(0.5); | |||
} | |||
} | |||
//better | |||
void watch_for_player() | |||
{ | |||
while($player1.getLocation() == $master_bedroom) //for as long as the player is in the master bedroom... | |||
{ | |||
if($lord_marlow.canSee($player1) //check if Lord Marlow can see the player | |||
{ | |||
$lord_marlow.bark("snd_i_see_you"); //if yes, make Marlow say something to the player | |||
return; //then end this script | |||
} | |||
sys.wait(0.5); //wait 0.5s before checking again | |||
} | |||
} | |||
== General tips == | |||
* Scripting is case-sensitive. | * Scripting is case-sensitive. | ||
* The scripting engine reads from top to bottom. That makes the order in which scripts and variables are arranged quite important. You can't call a script or use a variable if the engine hasn't seen it defined yet. | * The scripting engine reads from top to bottom. That makes the order in which scripts and variables are arranged quite important. You can't call a script or use a variable if the engine hasn't seen it defined yet. | ||
* The $ sign instructs the engine to look for an entity ingame with that name. If there's no $ sign, the engine will instead look for a variable with that name in the scripts. | * The $ sign instructs the engine to look for an entity ingame with that name. If there's no $ sign, the engine will instead look for a variable with that name in the scripts. | ||
* Commenting can be done either by putting // in front of every line of comment, or by putting /* at the top and */ at the bottom. The latter is useful for multi-line comments or temporarily disabling a section of your script. | * Commenting can be done either by putting // in front of every line of comment, or by putting /* at the top and */ at the bottom. The latter is useful for multi-line comments or temporarily disabling a section of your script. | ||
== Data types == | |||
Data type indicates whether something is a text (string), number (float), vector etc. You will always want to be aware of this, since i.e. a script designed to apply a force vector won't work if you give it a string instead of a vector as input. | Data type indicates whether something is a text (string), number (float), vector etc. You will always want to be aware of this, since i.e. a script designed to apply a force vector won't work if you give it a string instead of a vector as input. | ||
Line 16: | Line 58: | ||
| String || A string of letters/numbers, in other words plain text. It must always have double quotation marks. Example: “Text message 123” | | String || A string of letters/numbers, in other words plain text. It must always have double quotation marks. Example: “Text message 123” | ||
|- | |- | ||
| Float || A float is a single number, decimals are allowed. Example: 15.2 | | Float || A float (floating point number) is a single number, decimals are allowed. Example: 15.2 | ||
|- | |- | ||
| Vector || A vector consists of 3 numbers, often used for 3D movements (x z | | Vector || A vector consists of 3 numbers, often used for 3D movements (x y z) or colors (red green blue). It must always have single quotation marks. Example: '0 90 0' | ||
|- | |- | ||
| Boolean || Can be true or false. Easily replaced by floats (0 or 1). Example: TRUE | | Boolean || Can be true or false. Easily replaced by floats (0 or 1). Example: TRUE | ||
|- | |- | ||
| Void || No type. Commonly used for scripts or script events that have no | | Void || No type. Commonly used for scripts or script events that have no output. | ||
|- | |- | ||
| Entity || Indicates an entity, usually using the name as seen in DR. If it's a specific entity, instead of a variable, then a $ sign is required. Example: $player1 | | Entity || Indicates an entity, usually using the name as seen in DR. If it's a specific entity, instead of a variable, then a $ sign is required. Example: $player1 | ||
|- | |- | ||
| Subcategories of “entity” || You can be more specific and define an entity as, for example, an ai, light or tdm_elevator. These are names of scriptobjects, and by defining an entity like this you gain access to that scriptobject's internal variables and scripts. For an "ai", useful internal variables are i.e. whether the AI is alerted or knocked out. (see "AI flags" for more) | | Subcategories of “entity” || You can be more specific and define an entity as, for example, an ai, light or tdm_elevator. These are names of scriptobjects, and by defining an entity like this you gain access to that scriptobject's internal variables and scripts. For an "ai", useful internal variables are i.e. whether the AI is alerted or knocked out. (see [[A to Z Scripting: Scripting with...]] - "AI flags" for more) | ||
|} | |} | ||
DoomScript doesn't have integers as a data type. See [[A to Z Scripting: Basic maths]] for turning a float into an integer. | |||
== Variables == | |||
Variables are a | Variables are an important part of scripting. Each variable stores a piece of data, such as an entity's name or a position, which can then be modified or used by a script event. They allow a single script to have various effects, depending on the values of the variables. | ||
=== Creating new variables === | |||
The first time the engine finds a variable in a script it needs to be given a data type. Afterwards you use just the name. | The first time the engine finds a variable in a script it needs to be given a data type. Afterwards you use just the name. | ||
Example: | Example: | ||
vector teleportation_destination; //create a new vector variable called "teleportation_destination" | |||
... | |||
$player1.setOrigin(teleportation_destination); | |||
You may also want to assign an initial value. Otherwise, floats will default to 0, strings to "", vectors to '0 0 0' | You may also want to assign an initial value. Otherwise, floats will default to 0, strings to "", vectors to '0 0 0', entities to $null_entity and booleans to false. | ||
Example: | Example: | ||
vector teleportation_destination = '44 68 122'; | |||
Another option is to | |||
Another option is to create a variable using the output of a script event. | |||
3 Examples: | 3 Examples: | ||
vector | vector teleportation_destination = $func_static_1.getOrigin(); | ||
float can_see = $guard_westwing_1.canSee($player1); | |||
float | float sound_duration = $entity1.startSound("snd_move", SND_CHANNEL_ANY, false); //stores the duration of the sound that's been started | ||
=== Where to create variables === | |||
It's important whether you create a variable inside of a script or outside. | |||
If it's created inside of a script (locally), only that script can use it. Also, every time you run the script the variable will discard its old value because it gets recreated in the script. | |||
Example: | |||
void count_targets() //counts how many targets there are on a relay entity | |||
{ | |||
float number_of_targets = $trigger_relay_1.getNumTargets(); | |||
} | |||
If it's created outside of a script (globally), all scripts that come afterwards will be able to use it (remember that the engine reads from top to bottom). Note that you can't run script events outside of scripts. | |||
Example: | |||
vector target_position = '48 93 222'; | |||
void move_to_position() | |||
{ | |||
$func_mover_1.speed(30); | |||
$func_mover_1.moveToPos(target_position); | |||
} | |||
A 3rd option is to create variables inside the input brackets of a script. That's discussed in [[A to Z Scripting: Ways of calling a script]] and [[A to Z Scripting: Utility scripts]]. | |||
== #defining names == | |||
Naming important values makes it easier to keep track of what they stand for. For example, the number 60 can be defined as a "minute": | |||
#define MINUTE 60 //define a name "MINUTE", which stands for "60". No semicolon needed, since it's not an instruction. | |||
sys.wait(5 * MINUTE); //let the script wait for 5 * 60 = 300s, so 5 minutes | |||
Another example is sound channels: each sound channel has a number, and the TDM team has used #define to name each of them. This allows scripters to use either a name or a float in sound script events: | |||
#define SND_CHANNEL_VOICE 1 //define "SND_CHANNEL_VOICE" as standing for "1" | |||
$func_static_1.stopSound(SND_CHANNEL_VOICE); //stop sounds playing in channel 1, aka SND_CHANNEL_VOICE | |||
A major advantage of using names is that they make it very easy to change a commonly used value: simply change the value of the name, rather than tracking down every instance in the script where the value was used. | |||
== Example: puzzle with max 3 attempts, using variables and names == | |||
Say you wanted to give a player 3 attempts to solve | Say you wanted to give a player 3 attempts to solve a puzzle, with an option to reset his number of attempts: | ||
float attempts_left = | #define MAX_ATTEMPTS 3 //give the name "MAX_ATTEMPTS" to the number 3 | ||
float attempts_left = MAX_ATTEMPTS; //set the initial value of the variable "attempts_left" to: MAX_ATTEMPTS, or 3. | |||
void attempt_failed() //called every time the player fails an attempt | |||
void attempt_failed() //this script is called every time the player fails an attempt | |||
{ | { | ||
attempts_left = attempts_left - 1; // | attempts_left = attempts_left - 1; //new value of attempts_left is: current value - 1 | ||
if (attempts_left == 0) $puzzle.setFrobable(0); //if no more attempts, make the puzzle entity unfrobable | if (attempts_left == 0) $puzzle.setFrobable(0); //if no more attempts, make the puzzle entity unfrobable | ||
} | } | ||
void reset_attempts() //calling this resets the player's attempts | void reset_attempts() //calling this script resets the player's attempts | ||
{ | { | ||
attempts_left = | attempts_left = MAX_ATTEMPTS; //reset attempts_left to whatever number has been defined as MAX_ATTEMPTS | ||
$puzzle.setFrobable(1); //also make sure the puzzle entity is frobable | $puzzle.setFrobable(1); //also make sure the puzzle entity is frobable | ||
Line 79: | Line 164: | ||
== Using the TDM Script Reference == | |||
The [[TDM Script Reference]] is an essential wiki resource for scripting, listing all available script events for the current version of TDM. All script events are listed twice: the first time they're sorted alphabetically, the second time they're grouped based on which kinds of entities they can be called on. By far not all script events have received manual descriptions, so sometimes you may need to experiment with them to properly figure them out. | |||
=== Example: bob() === | |||
scriptEvent void bob(float speed, float phase, vector distance); | |||
Initiates a translation back and forth along the given vector with the given speed and phase. | |||
Spawnclasses responding to this event: idMover | |||
Interpretation: | |||
* void means this script event has no output (unlike, for example, a script event that checks the health of an AI) | |||
* the script event's name is bob | |||
* when you use this event, you need to put 3 things into the input brackets: 2 floats and a vector | |||
** all 3 have informative names to tell you what their purpose is. There's also a description on the next line. | |||
* This script event will only work on func_movers. | |||
So, the below line would make a mover called func_mover_1 start bobbing with a speed of 10 units per second, in phase 0.2 (i.e. start at 20% of the way through the first movement cycle) and over a distance of 30 units: | |||
$func_mover_1.bob(10, 0.2, 30); | |||
=== Example: canSee() === | |||
scriptEvent float canSee(entity ent); | scriptEvent float canSee(entity ent); | ||
Line 95: | Line 198: | ||
Interpretation: | Interpretation: | ||
* the | * unlike the previous event, this one produces a float as output. canSee() is a yes or no question, so this float will probably return 0 if no or 1 if yes. | ||
* an entity | * the input brackets require you to specify an entity, in addition to the one that the event is being called on. So the event checks if one entity can see another entity. | ||
* the script event | * no description in this case. | ||
* the script event only works if called on an AI (but you're probably allowed to check if an AI can see non-AI entities). | |||
Line 106: | Line 210: | ||
You can store the result as a variable for later use: | |||
float guard_sees_player = $guard_westwing_1.canSee($player1); | |||
== Next / previous article == | == Next / previous article == | ||
*Next article: [[A to Z Scripting: | *Next article: [[A to Z Scripting: Basic maths]] | ||
*Previous article: [[A to Z Scripting: Anatomy of a script]] | *Previous article: [[A to Z Scripting: Anatomy of a script]] | ||
*Table of contents: [[A to Z Scripting]] | *Table of contents: [[A to Z Scripting]] | ||
[[Category:Scripting]] |
Latest revision as of 11:35, 4 February 2021
Working tidily
Rule #1 is to keep your scripts tidy and well-commented so they're easy to follow. That includes making liberal use of the 'tab' key, which lets you quickly align your lines.
Comparison 1: script for reading spawnargs that the mapper has set on a func_mover entity
//bad vector mover_translate = $wardrobe.getVectorKey("translate"); vector mover_rotate = $wardrobe.getVectorKey("rotate"); float mover_time = $wardrobe.getFloatKey("move_time");
//better vector mover_translate = $wardrobe.getVectorKey("translate"); vector mover_rotate = $wardrobe.getVectorKey("rotate"); float mover_time = $wardrobe.getFloatKey("move_time"); //time taken to move
Comparison 2: script for constantly checking if an AI can see the player
//bad void watch_for_player() { while($player1.getLocation() == $master_bedroom) { if($lord_marlow.canSee($player1)) { $lord_marlow.bark("snd_i_see_you"); return; } sys.wait(0.5); } }
//better void watch_for_player() { while($player1.getLocation() == $master_bedroom) //for as long as the player is in the master bedroom... { if($lord_marlow.canSee($player1) //check if Lord Marlow can see the player { $lord_marlow.bark("snd_i_see_you"); //if yes, make Marlow say something to the player return; //then end this script } sys.wait(0.5); //wait 0.5s before checking again } }
General tips
- Scripting is case-sensitive.
- The scripting engine reads from top to bottom. That makes the order in which scripts and variables are arranged quite important. You can't call a script or use a variable if the engine hasn't seen it defined yet.
- The $ sign instructs the engine to look for an entity ingame with that name. If there's no $ sign, the engine will instead look for a variable with that name in the scripts.
- Commenting can be done either by putting // in front of every line of comment, or by putting /* at the top and */ at the bottom. The latter is useful for multi-line comments or temporarily disabling a section of your script.
Data types
Data type indicates whether something is a text (string), number (float), vector etc. You will always want to be aware of this, since i.e. a script designed to apply a force vector won't work if you give it a string instead of a vector as input.
String | A string of letters/numbers, in other words plain text. It must always have double quotation marks. Example: “Text message 123” |
Float | A float (floating point number) is a single number, decimals are allowed. Example: 15.2 |
Vector | A vector consists of 3 numbers, often used for 3D movements (x y z) or colors (red green blue). It must always have single quotation marks. Example: '0 90 0' |
Boolean | Can be true or false. Easily replaced by floats (0 or 1). Example: TRUE |
Void | No type. Commonly used for scripts or script events that have no output. |
Entity | Indicates an entity, usually using the name as seen in DR. If it's a specific entity, instead of a variable, then a $ sign is required. Example: $player1 |
Subcategories of “entity” | You can be more specific and define an entity as, for example, an ai, light or tdm_elevator. These are names of scriptobjects, and by defining an entity like this you gain access to that scriptobject's internal variables and scripts. For an "ai", useful internal variables are i.e. whether the AI is alerted or knocked out. (see A to Z Scripting: Scripting with... - "AI flags" for more) |
DoomScript doesn't have integers as a data type. See A to Z Scripting: Basic maths for turning a float into an integer.
Variables
Variables are an important part of scripting. Each variable stores a piece of data, such as an entity's name or a position, which can then be modified or used by a script event. They allow a single script to have various effects, depending on the values of the variables.
Creating new variables
The first time the engine finds a variable in a script it needs to be given a data type. Afterwards you use just the name. Example:
vector teleportation_destination; //create a new vector variable called "teleportation_destination" ... $player1.setOrigin(teleportation_destination);
You may also want to assign an initial value. Otherwise, floats will default to 0, strings to "", vectors to '0 0 0', entities to $null_entity and booleans to false.
Example:
vector teleportation_destination = '44 68 122';
Another option is to create a variable using the output of a script event.
3 Examples:
vector teleportation_destination = $func_static_1.getOrigin(); float can_see = $guard_westwing_1.canSee($player1); float sound_duration = $entity1.startSound("snd_move", SND_CHANNEL_ANY, false); //stores the duration of the sound that's been started
Where to create variables
It's important whether you create a variable inside of a script or outside.
If it's created inside of a script (locally), only that script can use it. Also, every time you run the script the variable will discard its old value because it gets recreated in the script. Example:
void count_targets() //counts how many targets there are on a relay entity { float number_of_targets = $trigger_relay_1.getNumTargets(); }
If it's created outside of a script (globally), all scripts that come afterwards will be able to use it (remember that the engine reads from top to bottom). Note that you can't run script events outside of scripts. Example:
vector target_position = '48 93 222'; void move_to_position() { $func_mover_1.speed(30); $func_mover_1.moveToPos(target_position); }
A 3rd option is to create variables inside the input brackets of a script. That's discussed in A to Z Scripting: Ways of calling a script and A to Z Scripting: Utility scripts.
#defining names
Naming important values makes it easier to keep track of what they stand for. For example, the number 60 can be defined as a "minute":
#define MINUTE 60 //define a name "MINUTE", which stands for "60". No semicolon needed, since it's not an instruction. sys.wait(5 * MINUTE); //let the script wait for 5 * 60 = 300s, so 5 minutes
Another example is sound channels: each sound channel has a number, and the TDM team has used #define to name each of them. This allows scripters to use either a name or a float in sound script events:
#define SND_CHANNEL_VOICE 1 //define "SND_CHANNEL_VOICE" as standing for "1" $func_static_1.stopSound(SND_CHANNEL_VOICE); //stop sounds playing in channel 1, aka SND_CHANNEL_VOICE
A major advantage of using names is that they make it very easy to change a commonly used value: simply change the value of the name, rather than tracking down every instance in the script where the value was used.
Example: puzzle with max 3 attempts, using variables and names
Say you wanted to give a player 3 attempts to solve a puzzle, with an option to reset his number of attempts:
#define MAX_ATTEMPTS 3 //give the name "MAX_ATTEMPTS" to the number 3 float attempts_left = MAX_ATTEMPTS; //set the initial value of the variable "attempts_left" to: MAX_ATTEMPTS, or 3. void attempt_failed() //this script is called every time the player fails an attempt { attempts_left = attempts_left - 1; //new value of attempts_left is: current value - 1 if (attempts_left == 0) $puzzle.setFrobable(0); //if no more attempts, make the puzzle entity unfrobable } void reset_attempts() //calling this script resets the player's attempts { attempts_left = MAX_ATTEMPTS; //reset attempts_left to whatever number has been defined as MAX_ATTEMPTS $puzzle.setFrobable(1); //also make sure the puzzle entity is frobable }
Using the TDM Script Reference
The TDM Script Reference is an essential wiki resource for scripting, listing all available script events for the current version of TDM. All script events are listed twice: the first time they're sorted alphabetically, the second time they're grouped based on which kinds of entities they can be called on. By far not all script events have received manual descriptions, so sometimes you may need to experiment with them to properly figure them out.
Example: bob()
scriptEvent void bob(float speed, float phase, vector distance); Initiates a translation back and forth along the given vector with the given speed and phase. Spawnclasses responding to this event: idMover
Interpretation:
- void means this script event has no output (unlike, for example, a script event that checks the health of an AI)
- the script event's name is bob
- when you use this event, you need to put 3 things into the input brackets: 2 floats and a vector
- all 3 have informative names to tell you what their purpose is. There's also a description on the next line.
- This script event will only work on func_movers.
So, the below line would make a mover called func_mover_1 start bobbing with a speed of 10 units per second, in phase 0.2 (i.e. start at 20% of the way through the first movement cycle) and over a distance of 30 units:
$func_mover_1.bob(10, 0.2, 30);
Example: canSee()
scriptEvent float canSee(entity ent); no description Spawnclasses responding to this event: idAI
Interpretation:
- unlike the previous event, this one produces a float as output. canSee() is a yes or no question, so this float will probably return 0 if no or 1 if yes.
- the input brackets require you to specify an entity, in addition to the one that the event is being called on. So the event checks if one entity can see another entity.
- no description in this case.
- the script event only works if called on an AI (but you're probably allowed to check if an AI can see non-AI entities).
It might take some experimentation to figure out which entity the script event should be called on and which entity should go into the input brackets.
In the end it could look something like this:
$guard_westwing_1.canSee($player1);
You can store the result as a variable for later use:
float guard_sees_player = $guard_westwing_1.canSee($player1);
Next / previous article
- Next article: A to Z Scripting: Basic maths
- Previous article: A to Z Scripting: Anatomy of a script
- Table of contents: A to Z Scripting