VScript Fundamentals: Difference between revisions
(added link to HL Alyx's pages) |
(Added Sections: "Running Code", "Script Scopes" (Replaces "Tables and Script Scopes"); Condensed "Entity Scripts" section; Gave "API References" section a makeover. Some sections are not touched yet.) |
||
Line 1: | Line 1: | ||
{{otherlang2 | {{otherlang2 | ||
|zh-cn=Vscript Fundamentals:zh-cn | |zh-cn=Vscript Fundamentals:zh-cn | ||
}} | }} | ||
This article aims to describe fundamental concepts, and common uses of [[VScript]] scripting. It will not go into all concepts related to the language itself, so things such as explaining what are variables or data types will be ignored. | |||
== Running Code == | |||
VScripts are loaded in a variety of ways, but there are 3 main notable approaches, with a few examples: | |||
===Source=== | |||
* Console commands | |||
**<code>script</code> | |||
**- Runs the inputted text as code. Will all be treated as on one line. | |||
**<code>script_execute</code> | |||
**- The script identical to the given name will be executed, so it starts running. | |||
* Hammer [[Inputs_and_Outputs|I/O system]] | |||
**<code>RunScriptCode</code> - Input | |||
**- Runs the given text as code in one line, but double quotation marks cannot be used as it will corrupt your VMF, so you can't pass strings. Otherwise is identical to <code>script</code>command. | |||
**<code>RunScriptFile</code> - Input | |||
**- Start running a script of the same given name. Identical to <code>script_execute</code>command. | |||
* Scripts named in a specific manner | |||
== Script Scopes == | |||
=== Source === | |||
The scripting environment consists of [https://en.wikipedia.org/wiki/Associative_array associative arrays], or ''tables'', nested inside each other. Thus, when a script is loaded, it is placed inside a table (referred as its ''script scope''), and any code inside the script is executed. All variables, functions and classes in the script persist in the script scope after the code has finished executing. | |||
As they're just arrays / tables, you can iterate through them to view every data within the scopes. The provided function below (taken from {{l4d2}}) will let you do this; Just put it into its own<code>.nut</code>script file, then use<code>script_execute</code> with it. | |||
{{ExpandBox| | |||
<syntaxhighlight lang=js> | |||
// This function is a slightly edited variant of the one in L4D2 (g_ModeScript.DeepPrintTable()) | |||
// so Valve did the most work here (Kerry specifically?) | |||
// ++ NOTES ++ | |||
// Use these methods when using a specific value type as the first argument, else you get an iteration error! | |||
// Functions - function.getinfos() method [Squirrel v3 and above only, so CS:GO wouldn't have this, but L4D2 would] | |||
// Instances - instance.getclass() method | |||
function DeepPrintTable( debugTable, prefix = "" ) | |||
{ | |||
if (prefix == "") | |||
{ | |||
printl("{") | |||
prefix = " " | |||
} | |||
foreach (idx, val in debugTable) | |||
{ | |||
if ( typeof(val) == "table" ) | |||
{ | |||
printl( prefix + idx + " = \n" + prefix + "{") | |||
DeepPrintTable( val, prefix + " " ) | |||
printl(prefix + "}") | |||
} | |||
else if ( typeof(val) == "string" ) | |||
printl(prefix + idx + "\t= \"" + val + "\"") | |||
else | |||
printl(prefix + idx + "\t= " + val) | |||
} | |||
if (prefix == " ") | |||
printl("}") | |||
} | |||
DeepPrintTable( getroottable() ) | |||
</syntaxhighlight> | |||
}} | |||
== Script Handles == | == Script Handles == | ||
Interaction with in-game entities is done through ''script handles'', which are objects that reference | Interaction with in-game entities is done through ''script handles'', which are custom objects that keep a reference specific entity, by using their entity index. These objects contain methods for getting / setting an entity's properties. Some entities belong to a certain "class", which comes with its own additional unique methods. All server-side entities currently in play can be fetched through methods available from the<code>Entities</code>instance;inheriting the<code>CEntities</code>object. (Examples below) | ||
All server-side entities currently in play can be | |||
See the API reference of the respective game for your project to know all available methods. | |||
===Source=== | |||
{{ExpandBox| | |||
<source lang=js> | |||
// Use Entities.FindByClassname to get all "prop_physic" entities | |||
local ent = null | |||
while( ent = Entities.FindByClassname(ent, "prop_physics") ) | |||
{ | |||
printl(ent) | |||
}</source>}} | |||
== Entity Scripts == | == Entity Scripts == | ||
A common VScript feature is to augment the features of entities using Entity Scripts, which are the specified scripts in the<code>vscripts</code>[[KeyValues|keyvalue]], or ones that are created by other scripts in engine runtime. In Hammer, the [[Inputs_and_Outputs|I/O system]] provides inputs to run code for these entity scripts (the<code>RunScriptCode</code>input), while in other VScripts, CBaseEntity class's method functions<Code>.GetScriptScope()</Code>and<code>.ValidateScriptScope()</code>allows creations or manipulation of Entity Scripts at runtime. | |||
A common VScript feature is to augment the features of entities using Entity Scripts | |||
Entity Scripts also get access to various features, such as "Input Hooks" or "Think Functions". This is only a quick introduction; See the [[Entity Scripts|main article]] for full info. | |||
== [[Inputs_and_Outputs|I/O system]] interaction == | == [[Inputs_and_Outputs|I/O system]] interaction == | ||
=== Firing | === Firing Inputs === | ||
If available in the game API, scripts can use the <code>EntFire()</code> and <code>DoEntFire()</code> functions to fire outputs to map entities. More functions may be available depending on game. | If available in the game API, scripts can use the <code>EntFire()</code> and <code>DoEntFire()</code> functions to fire outputs to map entities. More functions may be available depending on game. | ||
Line 63: | Line 97: | ||
=== Connecting Outputs === | === Connecting Outputs === | ||
Using the <code>CBaseEntity::ConnectOutput(string ''output'', string ''function'')</code> method, an entity Output can be connected to a function in the script scope of the entity. | Using the <code>CBaseEntity::ConnectOutput(string ''output'', string ''function'')</code> method, an entity Output can be connected to a function in the script scope of the entity. | ||
For the duration of the function call, the variables <code>activator</code> and <code>caller</code> are set to the handles of the activating and calling entities, allowing for example for an easy way to find which player triggered something. | For the duration of the function call, the variables <code>activator</code> and <code>caller</code> are set to the handles of the activating and calling entities, allowing for example for an easy way to find which player triggered something. | ||
Line 80: | Line 114: | ||
self.ConnectOutput( "OnPlayerUse", "IgniteSelf" ) | self.ConnectOutput( "OnPlayerUse", "IgniteSelf" ) | ||
</source> | </source> | ||
=== Input Hooks === | === Input Hooks === | ||
When an entity receives an input, the game code will attempt to call a script function of the format <code>Input<Name of Input>()</code> in the receiving Entity Script. If the function returns <code>false</code>, the input is prevented from firing. | When an entity receives an input, the game code will attempt to call a script function of the format <code>Input<Name of Input>()</code> in the receiving Entity Script. If the function returns <code>false</code>, the input is prevented from firing. As with connected output functions, the variables <code>activator</code> and <code>caller</code> are set to the handles of the activating and calling entities. | ||
{{note|The input name is case sensitive, and uses the CamelCase format.}} | {{note|The input name is case sensitive, and uses the CamelCase format.}} | ||
{{note|Unlike for output functions, these are set correctly in {{l4d2}}.}} | {{note|Unlike for output functions, these are set correctly in {{l4d2}}.}} | ||
Example Squirrel code: | Example Squirrel code: | ||
<source lang=cpp> | <source lang=cpp> | ||
// The script of a door or button. Intercepts the Unlock input, | // The script of a door or button. Intercepts the Unlock input, | ||
// and doesn't allow the door/button to unlock until 5 inputs have been received. | // and doesn't allow the door/button to unlock until 5 inputs have been received. | ||
Line 103: | Line 132: | ||
{ | { | ||
UnlockCounter-- | UnlockCounter-- | ||
if( UnlockCounter <= 0 ) | if( UnlockCounter <= 0 ) | ||
{ | { | ||
return true // Allows the unlock | return true // Allows the unlock | ||
} | } | ||
return false // Discards the input | return false // Discards the input | ||
} | } | ||
Line 116: | Line 145: | ||
== Glossary == | == Glossary == | ||
;Entity handle | ;Entity handle | ||
:An opaque entity reference passing a C++ [[CHandle#EHANDLE|EHANDLE]]. Can only be compared with other handles or passed to API functions expecting them. Only used rarely. | :An opaque entity reference passing a C++ [[CHandle#EHANDLE|EHANDLE]]. Can only be compared with other handles or passed to API functions expecting them. Only used rarely. | ||
;Script handle | ;Script handle | ||
:An entity instance with accessors and mutators to the C++ entity object. Represented as a HSCRIPT typedef in C++ code. | :An entity instance with accessors and mutators to the C++ entity object. Represented as a HSCRIPT typedef in C++ code. | ||
Line 123: | Line 152: | ||
== API | == API References == | ||
=== {{sq}} Squirrel === | |||
[[List of L4D2 Script Functions]] | * {{l4d2}} [[List of L4D2 Script Functions]] | ||
* {{portal2}} [[List of Portal 2 Script Functions]] | |||
[[List of Portal 2 Script Functions]] | * {{csgo}} [[List_of_Counter-Strike:_Global_Offensive_Script_Functions| List of CS:GO Script Functions]] | ||
* {{asrd}} [[List of Reactive Drop Script Functions]] | |||
[[List_of_Counter-Strike:_Global_Offensive_Script_Functions| List of CS:GO Script Functions]] | * {{nd}} [[List_of_ND_Script_Functions|List of Nuclear Dawn Script Functions]] | ||
* {{con}} [[List of Contagion Script Functions]] | |||
[[List of Contagion Script Functions]] | === Lua === | ||
* {{GMOD}} [https://wiki.facepunch.com/gmod/Global.AccessorFunc Facepunch Wiki] | |||
[[Dota_2_Workshop_Tools/Scripting/API| Dota 2 Scripting API]] | * {{dota2}} [[Dota_2_Workshop_Tools/Scripting/API| Dota 2 Scripting API]] | ||
* {{HL:A}} [[Half-Life_Alyx_Scripting_API| Half:Life: Alyx Scripting API]] | |||
== See Also == | == See Also == | ||
* [[VScript]] | * [[VScript]] |
Revision as of 14:09, 25 April 2021
Template:Otherlang2 This article aims to describe fundamental concepts, and common uses of VScript scripting. It will not go into all concepts related to the language itself, so things such as explaining what are variables or data types will be ignored.
Running Code
VScripts are loaded in a variety of ways, but there are 3 main notable approaches, with a few examples:
Source
- Console commands
script
- - Runs the inputted text as code. Will all be treated as on one line.
script_execute
- - The script identical to the given name will be executed, so it starts running.
- Hammer I/O system
RunScriptCode
- Input- - Runs the given text as code in one line, but double quotation marks cannot be used as it will corrupt your VMF, so you can't pass strings. Otherwise is identical to
script
command. RunScriptFile
- Input- - Start running a script of the same given name. Identical to
script_execute
command.
- Scripts named in a specific manner
Script Scopes
Source
The scripting environment consists of associative arrays, or tables, nested inside each other. Thus, when a script is loaded, it is placed inside a table (referred as its script scope), and any code inside the script is executed. All variables, functions and classes in the script persist in the script scope after the code has finished executing.
As they're just arrays / tables, you can iterate through them to view every data within the scopes. The provided function below (taken from ) will let you do this; Just put it into its own
.nut
script file, then usescript_execute
with it.
// This function is a slightly edited variant of the one in L4D2 (g_ModeScript.DeepPrintTable())
// so Valve did the most work here (Kerry specifically?)
// ++ NOTES ++
// Use these methods when using a specific value type as the first argument, else you get an iteration error!
// Functions - function.getinfos() method [Squirrel v3 and above only, so CS:GO wouldn't have this, but L4D2 would]
// Instances - instance.getclass() method
function DeepPrintTable( debugTable, prefix = "" )
{
if (prefix == "")
{
printl("{")
prefix = " "
}
foreach (idx, val in debugTable)
{
if ( typeof(val) == "table" )
{
printl( prefix + idx + " = \n" + prefix + "{")
DeepPrintTable( val, prefix + " " )
printl(prefix + "}")
}
else if ( typeof(val) == "string" )
printl(prefix + idx + "\t= \"" + val + "\"")
else
printl(prefix + idx + "\t= " + val)
}
if (prefix == " ")
printl("}")
}
DeepPrintTable( getroottable() )
Script Handles
Interaction with in-game entities is done through script handles, which are custom objects that keep a reference specific entity, by using their entity index. These objects contain methods for getting / setting an entity's properties. Some entities belong to a certain "class", which comes with its own additional unique methods. All server-side entities currently in play can be fetched through methods available from theEntities
instance;inheriting theCEntities
object. (Examples below)
See the API reference of the respective game for your project to know all available methods.
Source
// Use Entities.FindByClassname to get all "prop_physic" entities
local ent = null
while( ent = Entities.FindByClassname(ent, "prop_physics") )
{
printl(ent)
}
Entity Scripts
A common VScript feature is to augment the features of entities using Entity Scripts, which are the specified scripts in thevscripts
keyvalue, or ones that are created by other scripts in engine runtime. In Hammer, the I/O system provides inputs to run code for these entity scripts (theRunScriptCode
input), while in other VScripts, CBaseEntity class's method functions.GetScriptScope()
and.ValidateScriptScope()
allows creations or manipulation of Entity Scripts at runtime.
Entity Scripts also get access to various features, such as "Input Hooks" or "Think Functions". This is only a quick introduction; See the main article for full info.
I/O system interaction
Firing Inputs
If available in the game API, scripts can use the EntFire()
and DoEntFire()
functions to fire outputs to map entities. More functions may be available depending on game.
If arguments for activator
and caller
are available in the functions, they take a script handle and can be used to fire an output to an entity using the "!self" or "!activator" keyword, even without having to know its name, as long as the handle is available.
Arbitrary VScript code can be run from the I/O system, using the RunScriptCode
input available in all entities. The code will run in the calling entities script scope.

RunScriptCode
.
Example Squirrel code:
// Sets the health of the entity owning the script scope to 500.
DoEntFire( "!self", "SetHealth", "500", 0, self, self )
Connecting Outputs
Using the CBaseEntity::ConnectOutput(string output, string function)
method, an entity Output can be connected to a function in the script scope of the entity.
For the duration of the function call, the variables activator
and caller
are set to the handles of the activating and calling entities, allowing for example for an easy way to find which player triggered something.


Example Squirrel code:
// Lights a prop on fire when it's used by the player.
function IgniteSelf()
{
DoEntFire( "!self", "Ignite", "", 0, self, self )
}
// Connects the OnPlayerUse output to the above function.
self.ConnectOutput( "OnPlayerUse", "IgniteSelf" )
Input Hooks
When an entity receives an input, the game code will attempt to call a script function of the format Input<Name of Input>()
in the receiving Entity Script. If the function returns false
, the input is prevented from firing. As with connected output functions, the variables activator
and caller
are set to the handles of the activating and calling entities.

Example Squirrel code:
// The script of a door or button. Intercepts the Unlock input,
// and doesn't allow the door/button to unlock until 5 inputs have been received.
UnlockCounter <- 5 // Counter local to the entity script scope.
// Called when the Unlock input is received.
function InputUnlock()
{
UnlockCounter--
if( UnlockCounter <= 0 )
{
return true // Allows the unlock
}
return false // Discards the input
}
Glossary
- Entity handle
- An opaque entity reference passing a C++ EHANDLE. Can only be compared with other handles or passed to API functions expecting them. Only used rarely.
- Script handle
- An entity instance with accessors and mutators to the C++ entity object. Represented as a HSCRIPT typedef in C++ code.
- Script scope
- Execution context of a script. A table where the variables, functions and classes of a VScript are placed.
API References
Squirrel
List of L4D2 Script Functions
List of Portal 2 Script Functions
List of CS:GO Script Functions
List of Reactive Drop Script Functions
List of Nuclear Dawn Script Functions
List of Contagion Script Functions