Advanced IO Scripting
Functions
Declare a function at the top level of a script with function. Functions can take any number of parameters and don’t declare a return type:
function distanceFromSelf(target)
{
return target;
}
var d = distanceFromSelf(_activator);
A function called as its own full statement can wait() inside it. A function called as part of a larger expression (like var d = distanceFromSelf(_activator); above) must run synchronously.
Vectors
vec3/vec2 values come from entity members like .position or .velocity, or by constructing one directly:
var offset = vec3(0, 32, 0);
var landing = _self.position + offset;
_self.position = landing;
Vector arithmetic supports component-wise +/- between two vectors, and *// between a vector and a number. Access components with .x/.y/.z (and .x/.y for vec2), or index them numerically: landing[1] is the same as landing.y. Indexing can be useful for loops.
Checking Types with is
if (someValue is not vec3)
{
print("Expected a position!");
stopchain();
}
Valid right-hand names: number, string, bool, null, vec3, vec2, array, and entity.
is entity tells you a value is an entity, but to check for a specific class compare .classname instead:
if (_activator != null && _activator.classname == "FuncDoor") { ... }
Pass Variables and chain
chain is an accessor to the pass variables directly, for the connection currently executing, exposed as a key-indexable object.
Any entity input that returns pass variables — most commonly GetValue — does two things in the same call: it merges whatever it returns into chain, and it hands you that value back directly as the call’s result.
var current = _self.GetValue("ammo");
if (current < 1)
{
print("Out of ammo!");
}
Reading from chain by key matters when you want a variable that arrived some other way — say, a pass variable set by an earlier connection in the same chain, or one you wrote yourself:
chain["source"] = "trap_trigger";
Anything placed in chain, whether by an input call or by writing to it directly, is visible to every connection still to come in this same output’s chain; that’s what “pass variable” means: it passes forward.
chain values persist only for the duration of this one output’s chain of connections — they don’t survive between separate output firings. For that, use globals or mapglobals instead, which use the same indexer syntax.
Finding and Querying Entities
Beyond resolving a placed entity by name, a handful of engine-side functions let you query the world dynamically:
var nearby = entitiesinradius(_self.position, 10);
for (var i = 0; i < nearby.length; i = i + 1)
{
if (nearby[i].classname == "Player")
{
nearby[i].addcondition("flame");
}
}
For line-of-sight or “what’s in front of me” checks, use phystrace/bsptrace:
var hit = phystrace(_self.position, _self.forward, 500);
if (hit.hit && hit.entity != null && hit.entity.classname == "Player")
{
_self.moveto(hit.entity);
}
phystrace hits dynamic physics objects and entities; bsptrace hits only static world geometry, and its .entity is always null.
AI Control
.moveto() and .ispathcompleted() only work on entities with an AI-driven controller, calling them on anything else throws a runtime error. You can guard with .classname, or just be aware of what entity you call it on:
grunt.moveto(_activator);
Debugging Your Script
print() and the debugpoint/debugline functions are your best tools for figuring out why a script isn’t doing what you expect, without needing to rebuild anything:
print("chain reached this point, activator was", _activator);
debugpoint(_self.position);
debugline(_self.position, _self.position + _self.forward * 5);
Where to Go From Here
These guides should hopefully give you enough to have fun with scripting. For the exhaustive list of every global function, entity method, and type, see the IO Scripting Reference.