Difference between revisions of "Vscript Fundamentals"

From Valve Developer Community
Jump to: navigation, search
(Added info on tables and script handles)
(added link to HL Alyx's pages)
 
(10 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 +
{{otherlang2
 +
|noborder=true
 +
|zh-cn=Vscript Fundamentals:zh-cn
 +
}}
 +
 +
 +
 
This article aims to describe fundamental concepts and uses of [[VScript]] scripting.
 
This article aims to describe fundamental concepts and uses of [[VScript]] scripting.
  
{{todo}}
 
  
  
Line 17: Line 23:
  
 
== Entity Scripts ==
 
== Entity Scripts ==
 +
''Main article [[Entity Scripts]]''
  
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.
 
  
A think function can be set with the <code>thinkfunction</code> [[Keyvalue|KeyValue]], 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 input <code>RunScriptCode ''function_name(argument, ...)</code>.
+
A common VScript feature is to augment the features of entities using Entity Scripts.  
  
An Entity Script has a <code>self</code> reference to their owning script handle, allowing the script easy access to control the entity through its class methods.  
+
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.
  
{{todo| <code>activator</code> and <code>caller</code> are also available, but always seem to reference the same thing as <code>self</code>.}}
+
{{note|Source 2 has both public and private script scopes. Entity Scripts get loaded into the private script scope.}}
  
 +
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. When multiple scripts are loaded from the <code>vscripts</code> keyvalue, the <code>Precache()</code> and <code>OnPostSpawn()</code> will be chained so that eventual versions present in either or both scripts are called.
  
The script can be reloaded with console command <code>ent_fire <name of entity> runscriptfile ''<relative vscript path>''</code>. This is useful for quick script reloading.
+
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>.
  
 +
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.
 +
 +
=== 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 ==
If available in the game API, scripts can use the <code>EntFire()</code> and <code>DoEntFire()</code> functions to fire outputs to map entities.  
+
 
 +
=== Firing Outputs ===
 +
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.
 
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.
  
 
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.
 
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.
{{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>.}}
+
{{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>.}}
 +
{{tip|[https://github.com/TeamSpen210/HammerAddons TeamSpen210's Hammer Addons] include a workaround for this - backticks ( ` ) are swapped to quotes during compile, allowing strings to be passed.}}
 +
 
 +
 
 +
Example Squirrel code:
 +
<source lang=cpp>
 +
// Sets the health of the entity owning the script scope to 500.
 +
DoEntFire( "!self", "SetHealth", "500", 0, self, self )
 +
</source>
 +
 
 +
=== 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.
 +
 
 +
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:
 +
<source lang=cpp>
 +
// 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" )
 +
</source>
 +
 
 +
 
 +
=== 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.
 +
 
 +
{{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}}.}}
 +
 
 +
 
 +
Example Squirrel code:
 +
<source lang=cpp>
 +
// 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
 +
}
 +
</source>
  
  
 
== Glossary ==
 
== Glossary ==
 
;Entity handle
 
;Entity handle
:Also known as EHANDLE. Only used in a few places. {{todo|Looks like some sort of pointer reference to an entity.}}
+
: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. Also known as HScript.
+
:An entity instance with accessors and mutators to the C++ entity object. Represented as a HSCRIPT typedef in C++ code.
 
;Script scope
 
;Script scope
:The table where the variables, functions and classes of a VScript are placed.
+
:Execution context of a script. A table where the variables, functions and classes of a VScript are placed.
  
  
Line 60: Line 135:
 
[[Dota_2_Workshop_Tools/Scripting/API| Dota 2 Scripting API]]
 
[[Dota_2_Workshop_Tools/Scripting/API| Dota 2 Scripting API]]
  
 
+
[[Half-Life_Alyx_Scripting_API| Half:Life: Alyx Scripting API]]
 
== See Also ==
 
== See Also ==
[[VScript]]
+
* [[VScript]]
 
+
* [[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]]

Latest revision as of 06:44, 31 May 2020

简体中文


This article aims to describe fundamental concepts and uses of VScript scripting.


Tables and Script Scopes

The scripting environment consists of 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.


Script Handles

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 CEntities class object, Entities.


Entity Scripts

Main article Entity Scripts


A common VScript feature is to augment the features of entities using Entity Scripts.

Adding a script to the vscripts (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; _<unique ID>_<entity name>, placed in the root table. If no script has been run on the entity, a script scope can be manually created by using the CBaseEntity::ValidateScriptScope() method.

Note.png Note: Source 2 has both public and private script scopes. Entity Scripts get loaded into the private script scope.

Additional scripts can be loaded by specifying multiple scripts in the vscripts keyvalue, or using the RunScriptFile Input. All scripts that are run on the same entity will load into the same script scope, overwriting any identical variables and functions. When multiple scripts are loaded from the vscripts keyvalue, the Precache() and OnPostSpawn() will be chained so that eventual versions present in either or both scripts are called.

A think function can be set with the thinkfunction KeyValue or by the AddThinkToEnt() 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 RunScriptCode function_name(argument, ...) or RunScriptFunction function_name.

An Entity Script has a self (Source 1) or thisEntity (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.

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

To do: Does Source 2 implement this?

I/O system interaction

Firing Outputs

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.

Warning.png Warning: Never use double-quotation marks in any Hammer input, as it will corrupt the VMF. This means that strings cannot be passed with RunScriptCode.
Tip.png Tip: TeamSpen210's Hammer Addons include a workaround for this - backticks ( ` ) are swapped to quotes during compile, allowing strings to be passed.


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.

Bug.png Bug: In <Left 4 Dead 2>, these variables are not set correctly.


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.

Note.png Note: The input name is case sensitive, and uses the CamelCase format.

As with connected output functions, the variables activator and caller are set to the handles of the activating and calling entities.

Note.png Note: 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
}


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 Reference

List of L4D2 Script Functions

List of Portal 2 Script Functions

List of CS:GO Script Functions

List of Contagion Script Functions

Dota 2 Scripting API

Half:Life: Alyx Scripting API

See Also