VScript Fundamentals: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
 
(35 intermediate revisions by 21 users not shown)
Line 1: Line 1:
{{otherlang2
{{LanguageBar}}
|noborder=true
|zh-cn=Vscript Fundamentals:zh-cn
}}


{{back|VScript}}
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 for different scenarios. Below is a list of approaches for the respective Source engine, with a few examples:
{{confirm|Is Source 2's method of loading scripts very similar to Source 1's?}}


This article aims to describe fundamental concepts and uses of [[VScript]] scripting.
=== Source ===
{| class=standard-table
! Concept !! !! Description
|-
|rowspan=2| Console Command
| {{ent|script}}
| Runs the inputted text as code. Will all be treated as on one line.
|- id="script_execute"
| {{ent|script_execute}}
| The script with the given name (and path) will be executed from <code><game>/scripts/vscripts/</code>.
|- style=background-color:transparent
|rowspan=3| Hammer [[Inputs and Outputs|Inputs]]
| <code>RunScriptCode</code>
| 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>
| Start running a script of the same given name. Identical to <code>script_execute</code> command.
|-
| <code>CallScriptFunction</code>
| Looks up the local function with the name specified by the parameter, then calls it with no arguments.
|-
|rowspan=2| VScripts executed<br>automatically
| <code><game>/scripts/vscripts/''mapspawn.nut''</code>
| Scripts of this name run once when a map is loaded for the first time. Can conflict with other scripts. {{note|This script is executed before any entities exist so it needs to be delayed most of the time.}}
|-
| <code>left4dead2/scripts/vscripts/''mapspawn_addon.nut''</code> {{only|{{l4d2}}}}
| Identical to the above, except multiple scripts of the same name do not conflict with each other and all run together. {{note|Multiple<code>mapspawn_addon</code>all write to the same scope, overwriting any identical non-local functions or variable!}}
|}


{{todo}}
=== Debugging ===
It is possible to attach a debugger to running code, which allows to create breakpoints, step line by line, and view the state of all variables during each step. To do that, a community made extension for Visual Studio Code can be used - [https://marketplace.visualstudio.com/items?itemName=LionDoge.vscript-debug VScript Debugger] It is meant for Source 1 games only. For debugging VScripts in games based on Source 2 there is [https://projects.eclipse.org/projects/tools.ldt Eclipse Lua Development Tools]


== Script Scopes ==
The scripting environment consists of [[w:associative array|associative array]]s, or ''tables'', nested inside each other.
One of these tables is the '''root table'''{{inline note|name=Correct for Lua?}}, containing every other table, function, variable and so on. It can be imagined like a tree hierarchy where the root table is the root, other tables represent nodes and everything else (variables, functions, ...) represents a leaf (containing no more than their "value").


== Tables and Script Scopes ==
Thus, when a script is loaded, it is placed inside one specific 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.
The scripting environment consists of [https://en.wikipedia.org/wiki/Associative_array associative arrays], or ''tables'', nested inside each other.  


When a script is loaded, it is placed inside a table called 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.  
=== Squirrel {{sq}} ===
{{Expand
|float=right
|title=Scope for <code style=color:#ddd>script.2v2.nut</code>, a script for {{csgo mode|Wingman}} in {{csgo}}
|<syntaxhighlight lang=js style=white-space:nowrap>
25715b862_script.2v2 =
{
    DispatchOnPostSpawn = (function : 0x09EEC980)
    self = ([129] logic_script: script.2v2)
    __vname = "25715b862_script.2v2"
    PrecacheCallChain = (instance : 0x09CBE850)
    OnPostSpawnCallChain = (instance : 0x09CBE800)
    EnableWingman = (function : 0x09EEC900)
    __vrefs = 1
    DispatchPrecache = (function : 0x09EEC940)
}
</syntaxhighlight>
}}


The root table is obtained with <code>getroottable()</code>.


== Script Handles ==
You can view every data within the scripting environment scopes using the function below. Save it as a <code>.nut</code> {{sq}} file, use <code>script_execute</code> on the script, then call the function with the root table as the argument using the <code>script</code> command.
Interaction with in-game entities is done through ''script handles'', which are objects that reference a specific entity. The script handles contain accessor and and mutator methods to read and modify the entity properties. What methods are available depend on the game and entity type. See the scripting API reference for each game for more information.


All server-side entities currently in play can be searched and iterated through with the <code>CEntities</code> class object, <code>Entities</code>.
{{Expand
|From the {{Left 4 Dead 2|4}} script named <code>sm_utilities.nut</code>:<syntaxhighlight lang=js>
// This function is a slightly edited variant of the one in L4D2 (g_ModeScript.DeepPrintTable())
//
// View class instances's functions using instance.getclass() [returns table]
// In Squirrel 3, view extra function information like argument count; use function.getinfos() / nativefunction.getinfos() [returns table]
::DeepPrintTable <- function( debugTable, prefix = "" )
{
if (prefix == "")
{
printl(prefix + debugTable)
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("}")
}


// Usage:
// script DeepPrintTable( getroottable() )
</syntaxhighlight>


== Entity Scripts ==
Example Output:
''Main article [[Entity Scripts]]''
<pre style="background:#444; border-color:#666; color:#ddd">
] script DeepPrintTable( getroottable().Entities.getclass() )
(class : 0x08800E40)
{
  FindByClassname    = (native function : 0x0A759D40)
  FindByName    = (native function : 0x0A759D88)
  FindByClassnameWithin    = (native function : 0x0A75A010)
  FindByTarget    = (native function : 0x0A759E18)
  FindByModel    = (native function : 0x0A759E60)
  CreateByClassname    = (native function : 0x0A759CF8)
  IsValid    = (native function : 0x0A759C20)
  FindByClassnameNearest    = (native function : 0x0A759EA8)
  FindByNameWithin    = (native function : 0x0A759EF0)
  FindByNameNearest    = (native function : 0x0A759F38)
  Next    = (native function : 0x0A759CB0)
  FindInSphere    = (native function : 0x0A759DD0)
  First    = (native function : 0x0A759C68)
}
</pre>
}}




A common VScript feature is to augment the features of entities using Entity Scripts.  
In {{portal2}} and {{csgo}} there is already the global VScript function <code>__DumpScope(0, ''<scope/table>'')</code> that does the job:
{{Expand
| Tested in {{csgo}}. <pre style="background-color:#444; border-color:#666; color:#ddd">
] script __DumpScope( 0, Entities.getclass() )
FindByClassname = (native function : 0x15067450)
FindByName = (native function : 0x150679F0)
FindByClassnameWithin = (native function : 0x15064AB0)
FindByTarget = (native function : 0x15066D00)
FindByModel = (native function : 0x15066D48)
CreateByClassname = (native function : 0x15065878)
IsValid = (native function : 0x15067690)
FindByClassnameNearest = (native function : 0x15065680)
FindByNameWithin = (native function : 0x150669E8)
FindByNameNearest = (native function : 0x150653F8)
Next = (native function : 0x15066E68)
FindInSphere = (native function : 0x15067B58)
First = (native function : 0x15067600)</pre>
}}


Adding a script to the <code>vscripts</code> (Entity Scripts) KeyValue of a server-side entity loads the script as an Entity Script. The script is executed when the entity spawns, and loads into a script scope made up of an unique identifier followed by the entity name or class name; <code>_<unique ID>_<entity name></code>, placed in the root table. If no script has been run on the entity, a script scope can be manually created by using the <code>CBaseEntity::ValidateScriptScope()</code> method.
== 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 the<code>Entities</code>instance; inheriting the <code>CEntities</code> object. Examples are found below.


{{note|Source 2 has both public and private script scopes. Entity Scripts get loaded into the private script scope.}}
See the API reference of the respective game for your project to know all available methods.
{|
|- style=vertical-align:top
|
=== {{sq}} Squirrel ===
<source lang=js>
// Use Entities.FindByClassname to get all "prop_physics" entities
local ent = null
while( ent = Entities.FindByClassname(ent, "prop_physics") )
{
  printl(ent)
}</source>
|
=== Lua ===
<source lang=lua>
-- Use Entities:FindByClassname to get all "func_button" entities
local ent = nil
while ent = Entities:FindByClassname(ent, "func_button") do
  print(ent:GetDebugName())
end</source>
{{confirm}}
|}


Additional scripts can be loaded by specifying multiple scripts in the <code>vscripts</code> keyvalue, or using the <code>RunScriptFile</code> Input. All scripts that are run on the same entity will load into the same script scope, overwriting any identical variables and functions.
== Entity Scripts ==
:''This is only a quick introduction; See the [[Entity Scripts|main article]] for full info.''


A think function can be set with the <code>thinkfunction</code> [[Keyvalue|KeyValue]] or by the <code>AddThinkToEnt()</code> function, the specified script function every 0.1 seconds. While it has the potential to become expensive, a programmer is able to limit the amount of code executed. Functions can also be manually called through the I/O system with the inputs <code>RunScriptCode ''function_name(argument, ...)''</code> or <code>RunScriptFunction ''function_name''</code>.
A common VScript feature is to augment the features of entities using Entity Scripts, which are either the specified scripts in an entity's<code>vscripts</code>[[KeyValues|keyvalue]], or ones that are appended into an entity by simply using VScripts in engine runtime. Entity Scripts also get access to various features, such as "Input Hooks" or "Think Functions".


An Entity Script has a <code>self</code> (Source 1) or <code>thisEntity</code> (Source 2) reference to the script handle of the entity owning it, allowing the script easy access to control the entity through its class methods.
In Hammer, the [[Inputs_and_Outputs|I/O system]] provides inputs to run code for these entity scripts (the<code>RunScriptCode</code>input). For VScripts itself, CBaseEntity class's methods<Code>.GetScriptScope()</Code>and<code>.ValidateScriptScope()</code>respectively gets or creates an Entity Script at runtime.
 
=== Predefined Hooks ===
Entities have the ability to call functions in their script scope from the C++ side. Common entity classes have predefined function calls programmed into them to occur at certain events, allowing scripts to execute code. For example, creating a function called <code>Precache()</code> in an entity script will call that function right after the entity spawns, allowing the script to precache any custom assets. These functions do not need to be registered, and are always called if if one with the right name exist. Please see the API documentation for your game to find out what hook functions are available for each class.
 
{{todo|Does Source 2 implement this?}}


== [[Inputs_and_Outputs|I/O system]] interaction ==
== [[Inputs_and_Outputs|I/O system]] interaction ==
=== Execute Code with Inputs ===
All entities provide three inputs for VScripts:<code>RunScriptFile</code>, <code>RunScriptCode</code> and <code>CallScriptFunction</code>.
* <code>RunScriptFile</code>will automatically start in<code>scripts/vscripts/</code>, then find the file with the given argument as a name / filepath.
* <code>RunScriptCode</code>runs the argument as a line of VScript code in the entity's own script scope, which is also often used to run functions of an entity's script at any given moment.
* <code>CallScriptFunction</code> looks up the function in the entity's own script scope with the name given by the argument, then calls it with no parameters. This was added to implement <code>ConnectOutput()</code>, but can be used manually as well. It is equivalent to using <code>RunScriptCode</code> to do the same, except that if the function is not present no error is produced.


=== Firing Outputs ===
{{warning|Never use double-quotation marks in any Hammer input, as it will corrupt the VMF. This means that strings cannot be passed with<code>RunScriptCode</code>input. In {{tf2}}, backticks ( ` ) will be replaced by double-quotation marks at runtime, allowing strings to be passed through RunScriptCode. {{TeamSpen|2}} make this workaround available in other games.}}
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 arguments for ''<code>activator</code>'' and ''<code>caller</code>'' 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.
=== Firing Inputs ===
 
If available in the game API, scripts can use the <code>EntFire()</code> and <code>EntFireByHandle()</code> functions to fire outputs to map entities. Both take <code>(target, input, param, delay, activator, caller)</code> parameters, but <code>EntFireByHandle()</code> expects a handle to a specific entity to fire at while <code>EntFire</code> takes a targetname/class, like in Hammer.
Arbitrary VScript code can be run from the I/O system, using the <code>RunScriptCode</code> input available in all entities. The code will run in the calling entities script scope.
<code>EntFire()</code> allows omitting the last four parameters if you want blank values, but <code>EntFireByHandle</code> does not.
{{warning|Never use double-quotation marks in any Hammer Output, since it will corrupt the map file. This means that strings cannot be passed with <code>RunScriptCode</code>.}}


The parameters ''<code>activator</code>'' and ''<code>caller</code>'' take a script handle or <code>null</code>, and specify the entities referred to by ''<code>!activator</code>'' and ''<code>!caller</code>''. This is useful if you want to pass along another specific entity to the target.


Example Squirrel code:
Example Squirrel code:
<source lang=cpp>
<source lang=cpp>
// Sets the health of the entity owning the script scope to 500.
// Sets the health of the entity owning the script scope to 500.
DoEntFire( "!self", "SetHealth", "500", 0, self, self )
EntFireByHandle( self, "SetHealth", "500", 0, self, self )
// Parents all entities with names starting with "to_move_" to a specific car.
EntFire("to_remove_*", "SetParent", "!activator", 0, the_car, self)
</source>
</source>


=== 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. This is equivalent to manually adding an output which runs <code>CallScriptFunction</code> on <code>!self</code>, but you can add/remove it easily at runtime.
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.
{{bug|In {{l4d2}}, these variables are not set correctly.}}
 


Example Squirrel code:
Example Squirrel code:
Line 81: Line 216:
self.ConnectOutput( "OnPlayerUse", "IgniteSelf" )
self.ConnectOutput( "OnPlayerUse", "IgniteSelf" )
</source>
</source>


=== Input Hooks ===
=== Input Hooks ===
 
{{todo|Migrate to Entity Scripts section, or only go with a basic overview here}}
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.}}
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|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 104: Line 234:
{
{
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
}
}
</source>
</source>


=== Accessing <code>!activator</code> and <code>!caller</code> ===
When the inputs <code>RunScriptCode</code>, <code>RunScriptFile</code>, <code>CallScriptFunction</code> and <code>InputXX</code> hooks are run, the variables <code>activator</code> and <code>caller</code> are set to the handles of the activating and calling entities before the function is run. After the function is finished, these are reset. This allows for example for an easy way to find which player triggered something.


== 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 124: Line 257:




== API Reference ==
== API References ==
 
=== {{sq}} Squirrel ===
[[List of L4D2 Script Functions]]
* {{l4d2}} [[Left 4 Dead 2/Script Functions|List of L4D2 Script Functions]]
 
* {{portal2}} [[List of Portal 2 Script Functions]]
[[List of Portal 2 Script Functions]]
* {{csgo}} [[List of CS:GO Script Functions]]
 
* {{tf2}} [[Team Fortress 2/Scripting/Script Functions|List of Team Fortress 2 Script Functions]]
[[List_of_Counter-Strike:_Global_Offensive_Script_Functions| List of CS:GO Script Functions]]
* {{asrd}} [[List of Reactive Drop 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/ Garry's Mod Scripting API (Facepunch Wiki, No VScript)]
* {{dota2}} [https://moddota.com/api/#!/vscripts Dota 2 Scripting API (ModDota Site)]
* {{HL:A}} [[Half-Life: Alyx Workshop Tools/Scripting API|Half-Life: Alyx Scripting API]]


[[Dota_2_Workshop_Tools/Scripting/API| Dota 2 Scripting API]]
== See also ==
 
 
== See Also ==
* [[VScript]]
* [[Squirrel]]
* [[Squirrel]]
* [[L4D2_Vscripts| Left 4 Dead 2 Scripting]]
* [[L4D2_Vscripts| Left 4 Dead 2 Scripting]]
* [[Dota_2_Workshop_Tools/Scripting| Dota 2 Scripting]]
* [[Dota 2 Workshop Tools/Scripting| Dota 2 Scripting]]
* [[Half-Life: Alyx Workshop Tools/Lua Scripting| Half-Life: Alyx Scripting]]
[[Category:Scripting]]


[[Category:Scripting]]
[[Category:Tutorials]]

Latest revision as of 21:02, 12 April 2025

English (en)中文 (zh)Translate (Translate)
VScript

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 for different scenarios. Below is a list of approaches for the respective Source engine, with a few examples:

Confirm:Is Source 2's method of loading scripts very similar to Source 1's?

Source

Concept Description
Console Command script Runs the inputted text as code. Will all be treated as on one line.
script_execute The script with the given name (and path) will be executed from <game>/scripts/vscripts/.
Hammer Inputs RunScriptCode 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 scriptcommand.
RunScriptFile Start running a script of the same given name. Identical to script_execute command.
CallScriptFunction Looks up the local function with the name specified by the parameter, then calls it with no arguments.
VScripts executed
automatically
<game>/scripts/vscripts/mapspawn.nut Scripts of this name run once when a map is loaded for the first time. Can conflict with other scripts.
Note.pngNote:This script is executed before any entities exist so it needs to be delayed most of the time.
left4dead2/scripts/vscripts/mapspawn_addon.nut (only in Left 4 Dead 2) Identical to the above, except multiple scripts of the same name do not conflict with each other and all run together.
Note.pngNote:Multiplemapspawn_addonall write to the same scope, overwriting any identical non-local functions or variable!

Debugging

It is possible to attach a debugger to running code, which allows to create breakpoints, step line by line, and view the state of all variables during each step. To do that, a community made extension for Visual Studio Code can be used - VScript Debugger It is meant for Source 1 games only. For debugging VScripts in games based on Source 2 there is Eclipse Lua Development Tools

Script Scopes

The scripting environment consists of associative arrays, or tables, nested inside each other. One of these tables is the root table[Correct for Lua?], containing every other table, function, variable and so on. It can be imagined like a tree hierarchy where the root table is the root, other tables represent nodes and everything else (variables, functions, ...) represents a leaf (containing no more than their "value").

Thus, when a script is loaded, it is placed inside one specific 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.

Squirrel Squirrel

Scope for script.2v2.nut, a script for CS:GO/CS2 Wingman Wingman in Counter-Strike: Global Offensive
25715b862_script.2v2 = 
{
    DispatchOnPostSpawn	= (function : 0x09EEC980)
    self	= ([129] logic_script: script.2v2)
    __vname	= "25715b862_script.2v2"
    PrecacheCallChain	= (instance : 0x09CBE850)
    OnPostSpawnCallChain	= (instance : 0x09CBE800)
    EnableWingman	= (function : 0x09EEC900)
    __vrefs	= 1
    DispatchPrecache	= (function : 0x09EEC940)
}

The root table is obtained with getroottable().

You can view every data within the scripting environment scopes using the function below. Save it as a .nut Squirrel file, use script_execute on the script, then call the function with the root table as the argument using the script command.

From the Left 4 Dead 2 Left 4 Dead 2 script named sm_utilities.nut:
// This function is a slightly edited variant of the one in L4D2 (g_ModeScript.DeepPrintTable())
//
// View class instances's functions using instance.getclass() [returns table]
// In Squirrel 3, view extra function information like argument count; use function.getinfos() / nativefunction.getinfos() [returns table]
::DeepPrintTable <- function( debugTable, prefix = "" )
{
	if (prefix == "")
	{
		printl(prefix + debugTable)
		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("}")
}

// Usage:
// script DeepPrintTable( getroottable() )

Example Output:

] script DeepPrintTable( getroottable().Entities.getclass() )
(class : 0x08800E40)
{
   FindByClassname    = (native function : 0x0A759D40)
   FindByName    = (native function : 0x0A759D88)
   FindByClassnameWithin    = (native function : 0x0A75A010)
   FindByTarget    = (native function : 0x0A759E18)
   FindByModel    = (native function : 0x0A759E60)
   CreateByClassname    = (native function : 0x0A759CF8)
   IsValid    = (native function : 0x0A759C20)
   FindByClassnameNearest    = (native function : 0x0A759EA8)
   FindByNameWithin    = (native function : 0x0A759EF0)
   FindByNameNearest    = (native function : 0x0A759F38)
   Next    = (native function : 0x0A759CB0)
   FindInSphere    = (native function : 0x0A759DD0)
   First    = (native function : 0x0A759C68)
}


In Portal 2 and Counter-Strike: Global Offensive there is already the global VScript function __DumpScope(0, <scope/table>) that does the job:

Tested in Counter-Strike: Global Offensive.
] script __DumpScope( 0, Entities.getclass() )
FindByClassname = (native function : 0x15067450)
FindByName = (native function : 0x150679F0)
FindByClassnameWithin = (native function : 0x15064AB0)
FindByTarget = (native function : 0x15066D00)
FindByModel = (native function : 0x15066D48)
CreateByClassname = (native function : 0x15065878)
IsValid = (native function : 0x15067690)
FindByClassnameNearest = (native function : 0x15065680)
FindByNameWithin = (native function : 0x150669E8)
FindByNameNearest = (native function : 0x150653F8)
Next = (native function : 0x15066E68)
FindInSphere = (native function : 0x15067B58)
First = (native function : 0x15067600)

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 theEntitiesinstance; inheriting the CEntities object. Examples are found below.

See the API reference of the respective game for your project to know all available methods.

Squirrel Squirrel

// Use Entities.FindByClassname to get all "prop_physics" entities
local ent = null
while( ent = Entities.FindByClassname(ent, "prop_physics") )
{
   printl(ent)
}

Lua

-- Use Entities:FindByClassname to get all "func_button" entities
local ent = nil
while ent = Entities:FindByClassname(ent, "func_button") do
   print(ent:GetDebugName())
end

[confirm]

Entity Scripts

This is only a quick introduction; See the main article for full info.

A common VScript feature is to augment the features of entities using Entity Scripts, which are either the specified scripts in an entity'svscriptskeyvalue, or ones that are appended into an entity by simply using VScripts in engine runtime. Entity Scripts also get access to various features, such as "Input Hooks" or "Think Functions".

In Hammer, the I/O system provides inputs to run code for these entity scripts (theRunScriptCodeinput). For VScripts itself, CBaseEntity class's methods.GetScriptScope()and.ValidateScriptScope()respectively gets or creates an Entity Script at runtime.

I/O system interaction

Execute Code with Inputs

All entities provide three inputs for VScripts:RunScriptFile, RunScriptCode and CallScriptFunction.

  • RunScriptFilewill automatically start inscripts/vscripts/, then find the file with the given argument as a name / filepath.
  • RunScriptCoderuns the argument as a line of VScript code in the entity's own script scope, which is also often used to run functions of an entity's script at any given moment.
  • CallScriptFunction looks up the function in the entity's own script scope with the name given by the argument, then calls it with no parameters. This was added to implement ConnectOutput(), but can be used manually as well. It is equivalent to using RunScriptCode to do the same, except that if the function is not present no error is produced.
Warning.pngWarning:Never use double-quotation marks in any Hammer input, as it will corrupt the VMF. This means that strings cannot be passed withRunScriptCodeinput. In Team Fortress 2, backticks ( ` ) will be replaced by double-quotation marks at runtime, allowing strings to be passed through RunScriptCode. TeamSpen's Hammer Addons TeamSpen's Hammer Addons make this workaround available in other games.

Firing Inputs

If available in the game API, scripts can use the EntFire() and EntFireByHandle() functions to fire outputs to map entities. Both take (target, input, param, delay, activator, caller) parameters, but EntFireByHandle() expects a handle to a specific entity to fire at while EntFire takes a targetname/class, like in Hammer. EntFire() allows omitting the last four parameters if you want blank values, but EntFireByHandle does not.

The parameters activator and caller take a script handle or null, and specify the entities referred to by !activator and !caller. This is useful if you want to pass along another specific entity to the target.

Example Squirrel code:

// Sets the health of the entity owning the script scope to 500.
EntFireByHandle( self, "SetHealth", "500", 0, self, self )
// Parents all entities with names starting with "to_move_" to a specific car.
EntFire("to_remove_*", "SetParent", "!activator", 0, the_car, 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. This is equivalent to manually adding an output which runs CallScriptFunction on !self, but you can add/remove it easily at runtime.

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

Todo: Migrate to Entity Scripts section, or only go with a basic overview here

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.

Note.pngNote:The input name is case sensitive, and uses the CamelCase format.
Note.pngNote:Unlike for output functions, these are set correctly in Left 4 Dead 2.

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
}

Accessing !activator and !caller

When the inputs RunScriptCode, RunScriptFile, CallScriptFunction and InputXX hooks are run, the variables activator and caller are set to the handles of the activating and calling entities before the function is run. After the function is finished, these are reset. This allows for example for an easy way to find which player triggered something.

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 Squirrel

Lua

See also