VScript Fundamentals

From Valve Developer Community
Revision as of 10:43, 26 April 2021 by Darnias (talk | contribs) (Note to mapspawn.nut)
Jump to: navigation, search
简体中文

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

  • 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 scriptcommand.
    • RunScriptFile - Input
    • - Start running a script of the same given name. Identical to script_executecommand.
  • Scripts named in a specific manner
    • mapspawn.nut
    • - Scripts of this name run once when a map is loaded for the first time. Can conflict with other scripts.
Note.png Note: This script is executed before any entities exist so it needs to be delayed most of the time.
    • 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.png Note: Multiplemapspawn_addonall write to the same scope, overwriting any identical non-local functions or variable!

Script Scopes

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. Take a provided function below for the respective Source version, put it into its own.nut<Squirrel Language>/.luascript file, then usescript_execute with it.

Source

Edited function that originates from <Left 4 Dead 2>'ssm_utilities.nutscript. By default, functions and class instances within a table that its printing will not show its content. You can re-enable some disabled code to make it do so.


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 theCEntitiesobject. (Examples below)

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

<Squirrel Language> Squirrel


Lua

[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 two inputs for VScripts:RunScriptFileandRunScriptCodeinput. InputRunScriptFilewill automatically start inscripts/vscripts/, then find the file with the given argument as a name / filepath; InputRunScriptCoderuns 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.

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 withRunScriptCodeinput.
Tip.png Tip: TeamSpen210's Hammer Addons include a workaround for this - Backticks ( ` ) are swapped to quotes during compile, allowing strings to be passed.

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.


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

To do: 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.png Note: The input name is case sensitive, and uses the CamelCase format.
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 References

<Squirrel Language> Squirrel

Lua

See Also