Built-in Functions
Wirescript provides built-in functions that map directly to Brickadia wire graph gates. Each function is either pure (returns a value, no exec context needed) or exec (requires exec context and chains into the current execution flow).
Contents
- Notation
- Math / Trigonometry (Pure)
- Bitwise (Pure)
- Vector (Pure)
- Rotation / Quaternion (Pure)
- Color (Pure)
- Stateful Exec Values
- Select / Swap (Pure)
- Edge / Change Detectors
- Logical XOR (
^^) - String Operations (Pure)
- String Formatting (Pure)
- Array Methods (Exec)
- Player Input (Exec)
- Controller / Character Conversions (Exec)
- Camera / Aim (Exec)
- Display (Exec)
- Entity Getters (Exec)
- Entity Manipulation (Exec)
- Gamemode (Exec)
- Character (Exec)
- Controller (Exec)
- Broadcast Messaging (Exec)
- Audio (Exec)
- Entity Tags (Exec)
- Misc (Pure / Exec)
- Gate config properties
- Clock (Event)
- ChatCommand (Event)
- Custom Events
- Prefab Spawning (Exec)
- Raycasting (Exec)
- Random (Exec)
- Sleep / Delay (Pure)
- Exec Override
- Newer builtins
- Generic type syntax
- Maps (
var m: Map<K, V>) - Exec-flow gates (Union / Branch)
- Callable gate builtins
Notation
- Pure functions are expressions – they produce a value and can be used anywhere.
- Exec functions require an active exec context (inside an
onhandler). They are called as statements or in exec expressions. - Parameters marked with
?are optional.
Receiver Method Syntax
Many functions support receiver method syntax, where the first parameter is written before the dot instead of as a positional argument. Both forms are equivalent:
// Receiver form (preferred)
entity.SetLocation(pos)
// Traditional form
SetLocation(entity, pos)
Functions that support receiver syntax show both forms in the documentation below.
Math / Trigonometry (Pure)
All trig functions take and return float. Angles are in radians unless converted.
| Function | Signature | Description |
|---|---|---|
sin(x) | (x: float) -> float | Sine |
cos(x) | (x: float) -> float | Cosine |
tan(x) | (x: float) -> float | Tangent |
asin(x) | (x: float) -> float | Arc sine |
acos(x) | (x: float) -> float | Arc cosine |
atan(x) | (x: float) -> float | Arc tangent |
atan2(y, x) | (y: float, x: float) -> float | Two-argument arc tangent |
sinh(x) | (x: float) -> float | Hyperbolic sine |
cosh(x) | (x: float) -> float | Hyperbolic cosine |
tanh(x) | (x: float) -> float | Hyperbolic tangent |
asinh(x) | (x: float) -> float | Inverse hyperbolic sine |
acosh(x) | (x: float) -> float | Inverse hyperbolic cosine |
atanh(x) | (x: float) -> float | Inverse hyperbolic tangent |
exp(x) | (x: float) -> float | e^x |
ln(x) | (x: float) -> float | Natural logarithm |
sign(x) | (x: float) -> float | Sign (-1, 0, or 1) |
abs(x) | (x: float) -> float | Absolute value |
sqrt(x) | (x: float) -> float | Square root |
pow(x, exponent) | (x: float, exponent: float) -> float | Power |
clamp(x, min, max) | (x: float, min: float, max: float) -> float | Clamp to range |
round(x) | (x: float) -> float | Round to nearest integer |
floor(x) | (x: float) -> float | Round down |
ceil(x) | (x: float) -> float | Round up |
min(a, b) | (a: float, b: float) -> float | Minimum of two values |
max(a, b) | (a: float, b: float) -> float | Maximum of two values |
log(x, base) | (x: float, base: float) -> float | Logarithm with arbitrary base |
lerp(a, b, t) | (a: T, b: T, t: float) -> T | Linear interpolation; T is any math variant (see Easing/Tween) |
fmod(a, b) | (a: float, b: float) -> float | Floored modulo |
Deg2Rad(x) | (x: float) -> float | Degrees to radians |
Rad2Deg(x) | (x: float) -> float | Radians to degrees |
let angle = atan2(dy, dx)
let clamped = clamp(value, 0.0, 1.0)
let dist = sqrt(dx * dx + dy * dy)
let radians = Deg2Rad(90.0)
Bitwise (Pure)
| Function | Signature | Description |
|---|---|---|
BitCount(x) | (x: int) -> int | Count set bits (popcount) |
BitNand(a, b) | (a: int, b: int) -> int | Bitwise NAND (same as ~(a & b)) |
BitNor(a, b) | (a: int, b: int) -> int | Bitwise NOR (same as ~(a | b)) |
Note: ~(a & b) and ~(a | b) are automatically fused into single NAND/NOR gates by the compiler.
let bits = BitCount(flags)
Vector (Pure)
| Function | Signature | Description |
|---|---|---|
Vec(x, y, z) | (x: float, y: float, z: float) -> vector | Construct a vector |
Dot(a, b) | (a: vector, b: vector) -> float | Dot product |
Cross(a, b) | (a: vector, b: vector) -> vector | Cross product |
Normalize(v) | (v: vector) -> vector | Normalize to unit length |
Magnitude(v) | (v: vector) -> float | Length of vector |
MagnitudeSq(v) | (v: vector) -> float | Squared length (avoids sqrt) |
Distance(a, b) | (a: vector, b: vector) -> float | Distance between two points |
DistanceSq(a, b) | (a: vector, b: vector) -> float | Squared distance (avoids sqrt) |
ScaleVec(v, s) | (v: vector, scalar: float) -> vector | Scale vector by scalar |
RotToDir(rot) | (rot: vector) -> vector | Convert rotation to direction |
v.SplitVec() | (v: vector) -> {x, y, z: float} | Decompose vector (receiver on vector) |
Vector Receiver Methods
DistanceSq, MagnitudeSq, and RotToDir support receiver syntax on vector:
// Receiver form
let dsq = a.DistanceSq(b)
let msq = v.MagnitudeSq()
let dir = rot.RotToDir()
// Traditional form
let dsq = DistanceSq(a, b)
let msq = MagnitudeSq(v)
let dir = RotToDir(rot)
let pos = Vec(1.0, 2.0, 3.0)
let dir = Normalize(target - origin)
let dist = Distance(posA, posB)
let scaled = ScaleVec(velocity, 0.5)
Rotation / Quaternion (Pure)
Two rotation types: rotator is euler (pitch/yaw/roll, used by entity rotation),
quat is a quaternion produced by the conversion gates. Methods use the concise
receiver form.
| Function | Signature | Description |
|---|---|---|
Rotation(pitch, yaw, roll) | (float, float, float) -> rotator | Construct an euler rotator |
r.ToEuler() | (rotator) -> {Pitch, Yaw, Roll: float} | Split a rotator into components |
dir.ToRotation() | (vector) -> quat | Quaternion that points along dir |
q.ToDirection() | (quat) -> vector | Forward direction of q |
v.Rotate(q) | (vector, quat) -> vector | Rotate a vector by a quaternion |
q.Invert() | (quat) -> quat | Inverse rotation |
from.RotationTo(to) | (vector, vector) -> quat | Quaternion rotating from onto to |
a.AngleTo(b) | (quat, quat) -> float | Angle between two quaternions |
a.Slerp(b, alpha) | (quat, quat, float) -> quat | Spherical interpolation |
axis.RotationByAngle(angle) | (vector, float) -> quat | Quaternion from axis + angle (radians) |
q.ToAxisAngle() | (quat) -> {Axis: vector, Angle: float} | Decompose into axis + angle |
Quat(x, y, z, w) | (float, float, float, float) -> quat | Construct a quaternion from raw components |
q.SplitQuat() | (quat) -> {X, Y, Z, W: float} | Decompose into raw components |
a.QuatDot(b) | (quat, quat) -> float | Quaternion dot product |
let q = forward.ToRotation()
let spun = velocity.Rotate(q)
let mid = a.Slerp(b, 0.5)
let r = Rotation(0.0, 90.0, 0.0) // euler rotator
let yaw = r.ToEuler().Yaw
Color (Pure)
| Function | Signature | Description |
|---|---|---|
Color(r, g, b, a?) | (r: float, g: float, b: float, a?: float) -> color | Construct a color (linear RGBA, 0-1 range) |
ColorSRGB(r, g, b, a) | (int, int, int, int) -> color | Construct from sRGB bytes (0-255) |
ColorHex(hex) | (string) -> color | Construct from a hex string ("#ff8800") |
c.SplitColor() | (c: color) -> {r, g, b, a: float} | Decompose into linear components |
c.ToSRGB() | (color) -> {R, G, B, A: int} | Decompose into sRGB bytes |
c.ToHex() | (color) -> string | Hex string |
a.ColorBlend(b, alpha) | (color, color, float) -> color | Blend two colors (colour-space aware) |
SplitColor, ToSRGB, ToHex, and ColorBlend support receiver syntax on color.
Blend is a different gate – the math blend (an alias for lerp), which takes colours
as one of its variants but has no colour-space selection.
let red = Color(1.0, 0.0, 0.0)
let orange = ColorSRGB(255, 128, 0, 255)
let hex = orange.ToHex()
let parts = red.SplitColor() // parts.r = 1.0, parts.g = 0.0, ...
let mixed = red.ColorBlend(orange, 0.5)
Stateful Exec Values
| Function | Signature | Description |
|---|---|---|
Cycle(count) | (count: int) -> int exec | Returns 0,1,…,count-1 advancing each exec pulse |
Toggle() | () -> bool exec | Flips between false/true each exec pulse |
Select / Swap (Pure)
| Function | Signature | Description |
|---|---|---|
Select(cond, a, b) | (cond: bool, a: any, b: any) -> any | Returns a if false, b if true |
Swap(cond, a, b) | (cond: bool, a: any, b: any) -> {Output, OutputB: any} | Conditionally swap two values |
let bigger = Select(x > y, y, x)
let result = Swap(shouldSwap, left, right)
// result auto-unwraps to Output; result.Output and result.OutputB
// are swapped if shouldSwap is true
Edge / Change Detectors
| Function | Signature | Description |
|---|---|---|
Edge(input) | (input: bool) -> {Rising, Falling: bool} | Bool pulses on boolean transitions |
EdgeExec(input) | (input: float) -> {Rising, Falling: exec} | Exec pulses when a value rises/falls |
Changed(input) | (input: any) -> bool | Bool pulse when the input changes |
Change(input) | (input: any) -> any | Pulse the input value through when it changes |
Edge and Changed are pure: they produce a one-tick bool pulse (Rising on
false→true, Falling on true→false; Changed on any change). EdgeExec and
Change are their exec-flavored siblings — EdgeExec’s outputs fire exec
chains directly (use with on/await, like Timer(...).Expired), and
Change pulses the new value through whenever the input changes.
let edges = Edge(button)
on edges.Rising { count = count + 1 }
let health = EdgeExec(hp)
on health.Falling { ctrl.ShowStatusMessage("taking damage!") }
Logical XOR (^^)
The ^^ operator is boolean XOR — returns true if exactly one operand is true.
let either = a ^^ b // true if a or b but not both
Note: !(a && b) and !(a || b) are automatically fused into single NAND/NOR gates.
String Operations (Pure)
| Function | Signature | Description |
|---|---|---|
All string functions support receiver syntax on string: |
| Function | Signature | Description |
|---|---|---|
s.Length() | (s: string) -> int | String length |
s.Contains(search, caseSensitive?) | (s: string, search: string, caseSensitive?: bool) -> bool | Check if string contains substring |
s.StartsWith(prefix, caseSensitive?) | (s: string, prefix: string, caseSensitive?: bool) -> bool | Check prefix |
s.EndsWith(suffix, caseSensitive?) | (s: string, suffix: string, caseSensitive?: bool) -> bool | Check suffix |
s.Find(search, caseSensitive?, start?) | (s: string, search: string, caseSensitive?: bool, start?: int) -> int | Find substring index (-1 if not found) |
s.Substring(start, length) | (s: string, start: int, length: int) -> string | Extract substring |
s.Replace(search, replacement, caseSensitive?, maxReplacements?, start?) | (s: string, search: string, replacement: string, caseSensitive?: bool, maxReplacements?: int, start?: int) -> string | Replace occurrences |
s.Split(delimiter, occurrence?, caseSensitive?) | (s: string, delimiter: string, occurrence?: int, caseSensitive?: bool) -> {Left, Right: string, Found: bool} | Split at the delimiter |
s.ToLower() | (s: string) -> string | Convert to lowercase |
s.ToUpper() | (s: string) -> string | Convert to uppercase |
s.Trim() | (s: string) -> string | Remove leading/trailing whitespace |
s.ParseInt() / ParseInt(s) | (s: string) -> int | Parse an integer from text |
s.ParseNumber() / ParseNumber(s) | (s: string) -> float | Parse a number from text |
let name = "Hello World"
let len = name.Length() // 11
let has = name.Contains("World") // true
let low = name.ToLower() // "hello world"
let sub = name.Substring(6, 5) // "World"
let parts = name.Split(" ") // parts.Left = "Hello", parts.Right = "World"
String Formatting (Pure)
| Function | Signature | Description |
|---|---|---|
Fmt(format, a?, b?, c?, d?, e?, f?, g?) | (format: any, a-g?: any) -> string | Format text with placeholders |
The Fmt function wraps the FormatText gate. The format string uses {0} through {6} placeholders corresponding to inputs a through g.
let label = Fmt("{0}: {1}", "Score", score)
let coords = Fmt("({0}, {1}, {2})", x, y, z)
// Also works for palette selection:
let col = Fmt('{' .. bucket .. '}', 'eee4da', 'f2b179', 'f65e3b')
Array Methods (Exec)
Methods on an array variable. All run in exec context (they lower to ArrayVar
exec gates), so call them inside on handlers / mods. Declare arrays with
var name: T[] (see statements).
The element type can be a record (var pts: Point[]): the array is stored as
one parallel array per field and each method fans out. sort/shuffle, the
aggregates, and the dual-array ops have no per-field meaning there and are
rejected with WS050. See Records as storage.
| Method | Signature | Description |
|---|---|---|
arr.push(value) | (value: T) | Append an element |
arr.pop() | () -> T | Remove and return the last element |
arr.get(index) | (index: int) -> {Value: T, OutOfBounds: bool} | Read the element at index (auto-unwraps to Value); .OutOfBounds flags a bad index — the explicit form of arr[i] |
arr.length() | () -> int | Number of elements |
arr.remove(index) | (index: int) | Remove the element at index |
arr.insert(index, value) | (index: int, value: T) | Insert before index |
arr.clear() | () | Remove all elements |
arr.find(value) | (value: T) -> {Index: int, Found: bool, Value: T} | Find the first match (auto-unwraps to Index); Index is -1 when Found is false |
arr.sort(descending?) | (descending?: bool) | Sort in place |
arr.sortMultiple(other, ..., descending?) | (other: T[], ..., descending?: bool) | Sort in place, reordering up to 7 parallel arrays through the same permutation |
arr.reverse() | () | Reverse in place |
arr.shuffle() | () | Randomly reorder |
arr.swap(a, b) | (a: int, b: int) | Swap two elements |
arr.fill(value) | (value: T) | Set every element to value |
arr.resize(size, value) | (size: int, value: T) | Grow/shrink, filling new slots with value |
arr.sum() | () -> T | Sum of elements |
arr.min() / arr.max() | () -> T | Smallest / largest element |
arr.average() | () -> float | Mean of elements |
arr.append(source) | (source: T[]) | Append all elements of another array |
arr.copyFrom(source) | (source: T[]) | Replace contents with a copy of another array |
arr.slice(source, start, count) | (source: T[], start: int, count: int) | Copy source[start..start+count] into this array |
arr.fillFromPlayers() | () | Fill with all current players |
arr.fillFromTeam(team) | (team: entity) | Fill with the members of a team |
Element access uses bracket syntax: arr[i] reads (with .value / .bOutOfBounds),
arr[i] = x writes.
exec =. Any exec-gate call — an array method, a builtin, or a mod/chip call —
accepts an exec = <trigger> argument that drives its exec input, firing the gate each
time the trigger’s value changes. A per-index, always-nonzero trigger like index + 1
turns an array into a single-gate lookup table read straight from a pure binding:
var lut: color[] = [ /* ...constant entries... */ ]
out c: color = lut.get(i, exec = i + 1).Value
var scores: int[]
on RoundEnd() {
scores.push(currentScore)
scores.sort(true) // descending
let best = scores.max()
let count = scores.length()
}
sortMultiple sorts the receiver and drags parallel arrays along, which is how
you sort records by one field and keep the others attached:
var scores: Map<string, int>
var names: string[]
var points: int[]
in show: exec
on show {
scores.keys(names) // string[]
scores.values(points) // int[]
points.sortMultiple(names)
// points is sorted ascending and names[k] still owns points[k]
}
Sorting a copy and searching back with find is the alternative, and it ties
duplicate values to whichever entry matched first.
Player Input (Exec)
InputReader
character.InputReader() -> { Forward, Right, Up, Pitch, Yaw, Roll, MouseWheel, PressedC, PressedE, PressedQ, PressedLeftMouse, PressedRightMouse }
InputReader(character: character) -> { ...same fields... }
Read player input axes and pressed keys. Receiver on character.
Returns a record with fields:
Forward: float– forward/backward movement axis (-1 to 1)Right: float– left/right movement axis (-1 to 1)Up: float– up/down movement axis (-1 to 1)Pitch: float/Yaw: float/Roll: float– look axesMouseWheel: float– mouse wheel deltaPressedC/PressedE/PressedQ/PressedLeftMouse/PressedRightMouse: bool– key/button states
let input = char.InputReader()
let moving = input.Forward != 0.0 || input.Right != 0.0
let interacting = input.PressedE
GetInputs
character.GetInputs() -> { ...same fields as InputReader... }
GetInputs(player: character) -> { ...same fields... }
Sample the same twelve controls once, at the point the exec chain reaches this
call, rather than reading them continuously. The field names are identical to
InputReader’s, so only the context differs. Its operand also accepts a
persistent player, which wires straight into the character parameter.
Being exec form, it must sit on an exec chain; in pure position it reports
WS007.
on Clock(interval = 0.1) {
let input = char.GetInputs()
if input.PressedQ { char.ShowStatusMessage("q") }
}
Controller / Character Conversions (Exec)
These functions convert between entity types. They require exec context and support receiver syntax.
ControllerOf
entity.ControllerOf() -> controller
ControllerOf(entity: entity) -> controller
Get controller from entity. Receiver on entity.
CharacterOf
controller.CharacterOf() -> character
CharacterOf(controller: controller) -> character
Get character from controller. Receiver on controller.
on CharacterSpawned() -> (character) {
let ctrl = character.ControllerOf()
ctrl.DisplayText("Welcome!", fontSize = 24)
}
Camera / Aim (Exec)
GetAim
character.GetAim() -> { Origin: vector, Direction: vector }
GetAim(character: character) -> { Origin: vector, Direction: vector }
Reads the character’s camera/aim in a single gate. Returns a record:
Origin: vector— aim origin positionDirection: vector— aim direction vector
Receiver on character. Access the fields with .Origin / .Direction; both
share one gate, so reading both costs a single GetAim.
on trigger {
let aim = char.GetAim()
let origin = aim.Origin
let dir = aim.Direction
}
Display (Exec)
DisplayText
target.DisplayText(text, ...) -> int
DisplayText(target: controller, text: any, ...) -> int
Display HUD text to a player. Receiver on controller. Returns the resolved
textId (an int) so a later call can update or clear the same on-screen text.
The 2D layout ports are Vector2D composites, and the call feeds them one axis
at a time: positionX / positionY, anchorX / anchorY, and so on, each a
float. There is no vector-typed position or anchor – passing one is a
WS041 error. A constant axis bakes into the parent Vector2D data field; a
runtime value wires the matching sub-port. The call also exposes the scalar
styling below, plus fontSize / justify / easing / typeface / font, which
are constant-only data fields (not wire inputs).
DisplayText Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
target | controller | Yes | Player to display to |
text | any | Yes | Text content (auto-converted to string) |
positionX / positionY | float | No | 2D screen position, per axis |
anchorX / anchorY | float | No | 2D anchor point, per axis |
scaleX / scaleY | float | No | 2D scale, per axis |
pivotX / pivotY | float | No | 2D pivot point, per axis |
shadowOffsetX / shadowOffsetY | float | No | 2D drop-shadow offset, per axis |
angle | float | No | Rotation angle |
outlineSize | int | No | Text outline size |
outlineColor | color | No | Outline color |
fontColor | color | No | Font color |
shadowColor | color | No | Drop-shadow color |
miteredOutline | bool | No | Sharp (mitered) outline corners |
letterSpacing | float | No | Extra spacing between letters |
lineHeight | float | No | Line-height multiplier |
wrapWidth | float | No | Wrap width (0 = no wrap) |
skew | float | No | Italic-style skew |
zOrder | int | No | Draw order |
lifetime | float | No | Display duration (seconds) |
transition | float | No | Seconds to interpolate to the new state when re-emitted with the same textId |
textId | int | No | Unique ID for updating text in-place |
fontSize | int | No | Font size (constant only) |
justify | int | No | Justification: DisplayTextJustification.Left / .Center / .Right, or bare Left / Center / Right (constant only) |
easing | int | No | Transition curve: DisplayTextEasing.Linear / .EaseIn / .EaseOut / .EaseInOut, or bare Linear / EaseIn / EaseOut / EaseInOut (constant only) |
typeface | int | No | Typeface: TextTypeface.Regular / .Bold / .Italic / .BoldItalic, or bare Regular / Bold / Italic / BoldItalic (constant only) |
font | asset ref | No | Font asset reference (constant only) |
on RoundStart() {
ctrl.DisplayText("Round Start!", fontSize = 48, lifetime = 3.0)
}
// Update text in-place: capture the id, then re-display with the same textId.
on trigger {
let id = ctrl.DisplayText("Score: ${score}", fontSize = 24, lifetime = 10.0)
ctrl.DisplayText("Score: ${score}", textId = id, transition = 0.25)
}
Entity Getters (Exec)
All entity getter functions require exec context and support receiver syntax on entity.
GetLocation
entity.GetLocation() -> vector
GetLocation(entity: entity) -> vector
Get entity’s world position.
GetRotation
entity.GetRotation() -> rotator
GetRotation(entity: entity) -> rotator
Get entity’s world rotation.
GetLocationRotation
entity.GetLocationRotation() -> {Vector: vector, Rotation: rotator}
GetLocationRotation(entity: entity) -> {Vector: vector, Rotation: rotator}
Get both position and rotation at once.
GetLinearVelocity
entity.GetLinearVelocity() -> vector
GetLinearVelocity(entity: entity) -> vector
Get entity’s linear velocity.
GetAngularVelocity
entity.GetAngularVelocity() -> vector
GetAngularVelocity(entity: entity) -> vector
Get entity’s angular velocity.
GetVelocity
entity.GetVelocity() -> {Vector: vector, Rotation: rotator}
GetVelocity(entity: entity) -> {Vector: vector, Rotation: rotator}
Get both linear and angular velocity at once.
on trigger {
let pos = entity.GetLocation()
let rot = entity.GetRotation()
let vel = entity.GetLinearVelocity()
}
Entity Manipulation (Exec)
All entity manipulation functions require exec context and support receiver syntax on entity.
SetLocation
entity.SetLocation(pos: vector)
SetLocation(entity: entity, pos: vector)
Set entity position. Use this (not Teleport) to move an entity to world
coordinates. pos is a real vector port.
SetRotation
entity.SetRotation(rot: rotator)
SetRotation(entity: entity, rot: rotator)
Set entity rotation.
SetLocationRotation
entity.SetLocationRotation(pos: vector, rot: rotator)
SetLocationRotation(entity: entity, pos: vector, rot: rotator)
Set both position and rotation.
AddLocationRotation
entity.AddLocationRotation(pos: vector, rot: rotator)
AddLocationRotation(entity: entity, pos: vector, rot: rotator)
Add to position and rotation.
Teleport
entity.Teleport(dest: any)
Teleport(entity: entity, dest: any)
Teleport entity to destination.
RelativeTeleport
entity.RelativeTeleport(source: any, dest: any)
RelativeTeleport(entity: entity, source: any, dest: any)
Relative teleport between two points.
SetVelocity
entity.SetVelocity(linear?: vector, angular?: vector)
SetVelocity(entity: entity, linear?: vector, angular?: vector)
Set velocity. Both linear and angular are optional – pass whichever components you want to set.
AddVelocity
entity.AddVelocity(linear?: vector, angular?: vector)
AddVelocity(entity: entity, linear?: vector, angular?: vector)
Add to velocity. Both linear and angular are optional.
SetLinearVelocity
entity.SetLinearVelocity(vel: vector)
SetLinearVelocity(entity: entity, vel: vector)
Set linear velocity only.
SetAngularVelocity
entity.SetAngularVelocity(vel: vector)
SetAngularVelocity(entity: entity, vel: vector)
Set angular velocity only.
SetGravityDirection
entity.SetGravityDirection(rot: rotator)
SetGravityDirection(entity: entity, rot: rotator)
Set gravity direction for entity.
SetFrozen
entity.SetFrozen(frozen: bool)
SetFrozen(entity: entity, frozen: bool)
Freeze or unfreeze an entity’s physics.
on trigger {
entity.SetLocation(Vec(0.0, 0.0, 100.0))
entity.SetVelocity(linear = Vec(0.0, 0.0, 500.0))
entity.AddVelocity(linear = direction, angular = Vec(0.0, 90.0, 0.0))
}
Gamemode (Exec)
SetLeaderboard
controller.SetLeaderboard(key: string, value: any)
SetLeaderboard(controller: controller, key: string, value: any)
Set a leaderboard value. Receiver on controller.
IncLeaderboard
controller.IncLeaderboard(key: string, value: any)
IncLeaderboard(controller: controller, key: string, value: any)
Increment a leaderboard value. Receiver on controller.
GetLeaderboard
controller.GetLeaderboard(key: string) -> any
GetLeaderboard(controller: controller, key: string) -> any
Get a leaderboard value. Receiver on controller.
GetTeam
character.GetTeam() -> any
GetTeam(character: character) -> any
Get a character’s team. Receiver on character.
IsBuilderTeam / IsUnaffiliatedTeam
team.IsBuilderTeam() -> bool
IsBuilderTeam(team: entity) -> bool
team.IsUnaffiliatedTeam() -> bool
IsUnaffiliatedTeam(team: entity) -> bool
Pure predicates over a team entity: IsBuilderTeam is true for the builder
team, IsUnaffiliatedTeam for the unaffiliated (no-team) group. Receiver on the
team entity.
PlayerWins / TeamWins
player.PlayerWins(teamWinsInstead?: bool)
PlayerWins(player: controller, teamWinsInstead?: bool)
team.TeamWins()
TeamWins(team: entity)
End the current round by declaring a winner. (The old imperative EndRound
gate was removed; a round now ends via a win.) PlayerWins declares a player
the winner, or their team if teamWinsInstead is true; TeamWins declares a
team the winner.
GetCurrentRound
GetCurrentRound() -> int
The current round number.
GetTeamByName / GetTeamName
GetTeamByName(name: string) -> entity
team.GetTeamName() -> string
GetTeamName(team: entity) -> string
Look up a team by name, or get a team’s display name.
SetTeam
controller.SetTeam(team: entity, pin?: bool)
SetTeam(controller: controller, team: entity, pin?: bool)
Assign a player to a team, optionally pinning them to it.
Team leaderboards
team.GetTeamLeaderboardValue(key: string) -> int
team.SetTeamLeaderboardValue(key: string, value: int)
team.IncrementTeamLeaderboardValue(key: string, value: int)
Read, set, or add to a team-scoped leaderboard value. Receiver on the team
entity (also callable as free functions with team as the first argument).
on CharacterDied() -> (character) {
let ctrl = character.ControllerOf()
ctrl.IncLeaderboard("deaths", 1)
let score = ctrl.GetLeaderboard("score")
}
Character (Exec)
ShowHint
character.ShowHint(title: string, text: string)
ShowHint(character: character, title: string, text: string)
Display a hint popup to a character. Receiver on character.
on CharacterSpawned() -> (character) {
character.ShowHint("Welcome", "Press E to interact")
}
Damage
character.GetDamage() -> { Damage: float, DamageLimit: float }
character.SetDamage(damage: float)
character.IncDamage(amount: float)
Read, set, or add to a character’s accumulated damage. Receiver on character.
GetDamage() auto-unwraps to Damage where a float is expected (e.g.
if char.GetDamage() > 50.0), and .DamageLimit gives the death threshold.
SetTempPermission
character.SetTempPermission(permission: string, enable: bool)
Grant or revoke a temporary permission tag on a character. Receiver on character.
Inventory
character.GiveWeapon(weapon, slot?) // set a slot to an item asset
character.AddInventoryItem(item) // append an item
character.SetInventoryItem(item, slot?) // set a slot to an item
character.AddInventoryBrick(brick, size?) // append a placeable brick
character.SetInventoryBrick(brick, slot?, size?)
character.AddInventoryEntity(entityType) // append a spawnable entity
character.SetInventoryEntity(entityType, slot?)
character.AddInventoryItemAdv(item, damage?, speed?, scale?, itemName?, projectile?)
character.SetInventoryItemAdv(item, slot?, damage?, speed?, scale?, itemName?, projectile?)
Give items, procedural bricks, or spawnable entities to a character’s
inventory. Asset args are $Type/Name references — $BRItemBase/... for
items, a brick asset for bricks, an entity type for entities — inlined into
the gate’s data. The Adv variants add per-item overrides: damage/weapon
speed/scale multipliers, a display-name override, and a projectile override.
All receive on character.
on CharacterSpawned() -> (character) {
character.GiveWeapon($BRItemBase/Weapon_Pistol, 0)
character.AddInventoryItemAdv($BRItemBase/Weapon_Bow,
damage = 2.0, itemName = "Longbow of Doom")
}
Controller (Exec)
ShowStatusMessage
controller.ShowStatusMessage(message: string)
ShowStatusMessage(controller: controller, message: string)
Display a status bar message to a player. Receiver on controller.
on RoundStart() {
ctrl.ShowStatusMessage("Round started!")
}
ShowChatMessage
controller.ShowChatMessage(message: string)
ShowChatMessage(controller: controller, message: string)
Send a chat message that only this player sees (a whisper). Receiver on
controller.
ShowMessageBox
controller.ShowMessageBox(message: string, title?: string)
Pop up a modal message box for this player. Receiver on controller.
Player info
controller.GetUserName() -> string
controller.GetUserId() -> string
controller.GetDisplayName() -> string
controller.IsTrusted() -> bool
controller.HasPermission(permission: string) -> bool
controller.HasRole(role: string) -> bool
controller.SetCanRespawn(canRespawn: bool)
controller.ForceRespawn()
controller.SetTeamPinned(pinned: bool)
Read a player’s account name, persistent user id, or current display name;
check whether they are trusted by the brick owner, hold a named permission, or
have a named role (HasRole); toggle their ability to respawn or immediately
respawn them (ForceRespawn, also callable as ForceRespawn(player)); or pin
them to their team. All receive on controller.
Broadcast Messaging (Exec)
BroadcastChatMessage(message: string)
BroadcastStatusMessage(message: string, flash?: bool)
Send a chat message or status-bar message to every player. flash
re-flashes the status message even when its text is unchanged.
on roundEnd {
BroadcastChatMessage("Red team wins!")
BroadcastStatusMessage("Round over", flash = true)
}
Audio (Exec)
entity.PlayAudioAt(audio, volume?, pitch?, innerRadius?, maxDistance?, spatialized?)
PlayGlobalAudio(audio, volume?, pitch?)
player.PlayClientAudio(audio, volume?, pitch?)
Play a one-shot sound at an entity’s location (spatialized by default),
globally for all players, or non-spatially for a single player
(PlayClientAudio, receiver on the player — a character or persistent player
reference; also callable as PlayClientAudio(player, audio, ...)). The audio
arg is a $BrickOneShotAudioDescriptor/... asset reference. PlayAudioAt
receives on entity (characters work too).
on ZoneEntered() -> (character) {
character.PlayAudioAt($BrickOneShotAudioDescriptor/BOSA_Buttons_Button_1_Press)
}
Entity Tags (Exec)
entity.SetTag(tag: string)
entity.GetTag() -> string
Attach an arbitrary string tag to any entity and read it back later — handy
for marking players/entities with game state (team, slot index, role). Zone
components can also filter on tags. Receiver on entity.
Misc (Pure / Exec)
| Function | Signature | Description |
|---|---|---|
FindPlayer(query) | (query: string) -> character (exec) | Look up a player by name; emits their character |
PrintToConsole(text) | (text: any) -> () (exec) | Print a value to the game console (debugging) |
Opaque(value) | (value: any) -> any (pure) | Identity rerouter; the permanent constant-fold barrier — the wrapped value always stays a real runtime wire, never folded or seen through (probe/test circuits; see Constant Folding) |
DeltaTime() | () -> float | Seconds elapsed since the previous tick |
ServerUptime() | () -> float | Seconds the server has been running |
ReadBrickGrid() | () -> entity | The brick grid this gate’s microchip is on, as an entity |
NearlyEqual(a, b, tolerance) | (a: float, b: float, tolerance: float) -> bool | Approximate float equality |
Dampen(target, smoothTime) | (target: float, smoothTime: float) -> float | Critically-damped smoothing toward a target |
Easing(a, b, blend, fn?, dir?) | (a: T, b: T, blend: float, fn?: any, dir?: any) -> T | Ease from a to b by blend |
Tween(target, duration, fn?, dir?) | (target: T, duration: float, fn?: any, dir?: any) -> T | Stateful eased value toward target |
Timer(limit, restart?, pause?, resume?) | (limit: float, restart?/pause?/resume?: exec) -> {Time: float, Expired: exec} | Stateful countdown timer |
Blend/lerp/Easing/Tween interpolate any one math variant T: float|int|vector|rotator|quat|color.
The result is whatever T the inputs carry.
Easing/Tween take an easing fn and dir: an int, a bare enum-name
literal, or an EasingFunction / EasingDirection value (see
Built-in game enums). function = EasingFunction.Bounce and function = Bounce set the same field. Functions:
Linear, Sine, Quad, Cubic, Quart, Quint, Expo, Circ, Back,
Elastic, Bounce. Directions: In, Out, InOut. Omitted, they default to
Linear/In.
Timer is a function-call instance. The restart/pause/resume exec
controls are optional; its outputs are a value (Time) and an exec (Expired):
in trigger: exec
let t = Timer(10.0, restart = trigger)
out elapsed = t.Time
on t.Expired { /* fired when Time reaches the limit */ }
ReadBrickGrid() is pure and takes no arguments — it returns the brick grid
that this gate’s microchip is placed on as an entity, ready to pass to entity
getters/setters or wire into gates that expect a brick grid:
let grid = ReadBrickGrid()
let origin = grid.GetLocation()
Gate config properties
Some gate settings are not wire inputs — they are the checkboxes, dropdowns, and values in a gate’s in-game settings menu. Wirescript sets them as optional, constant-only call arguments; a constant is baked into the gate’s data, and anything you omit keeps the game default. (A non-constant value is a compile error — these can’t be wired.)
Enum values are bare member names, validated against the game’s own enum
member list at compile time. An unknown name is a WS028 error; a raw int is
also accepted (and range-checked).
let e = Easing(0.0, 1.0, t, function = Bounce, direction = InOut)
let c = ColorBlend(a, b, t, blendSpace = Oklab, clampAlpha = true)
p.DisplayText("hi", typeface = Bold, justify = Center, easing = EaseInOut)
Each enum used this way is also a built-in game enum type of its own, so its qualified value form works too, side by side with the bare name, and the two set the same field:
let e = Easing(0.0, 1.0, t, function = EasingFunction.Bounce, direction = EasingDirection.InOut)
let c = ColorBlend(a, b, t, blendSpace = ColorSpace.Oklab, clampAlpha = true)
p.DisplayText("hi", typeface = TextTypeface.Bold, justify = DisplayTextJustification.Center, easing = DisplayTextEasing.EaseInOut)
Every config field is settable — not just the aliases below. In addition to
the friendly names, each gate exposes each bool/int/float/string/enum
settings-menu field under its raw game name, so any of the ~60 config gates
works even without a curated alias:
SweepSimple(500.0, Direction = X_Negative, bOnlyHitPlayerBodyParts = true)
p.DisplayText("hi", FontSize = 40, Typeface = Bold, Justification = Center)
Completion offers these raw field names, and hovering one shows its type (and enum members). The friendly alias and the raw name set the same field, so pick either; the table below lists the ergonomic aliases for the common gates.
Config attributes by gate:
| Gate | Config attributes |
|---|---|
Sweep | bodyPartsOnly |
SweepSimple | direction (EBrickDirection), spreadTowardCenter, detectBricks, detectPlayers1–4, bodyPartsOnly, detectPhysics, detectMap |
Blend | clampAlpha |
ColorBlend | blendSpace (EBRColorSpace), clampAlpha |
Slerp | shortestPath, clampAlpha |
Easing | function (EasingFunction, schema EBREasingFunction), direction (EasingDirection, schema EBREasingDirection) |
ConvertColor | fromSpace, toSpace (EBRColorSpace) |
DisplayText | fontSize, justify (DisplayTextJustification, schema EBRDisplayTextJustification), easing (DisplayTextEasing, schema EBRDisplayTextEasing), typeface (TextTypeface, schema EBRTextTypeface), font (a $Font/... asset ref) |
GetAim | localAim |
AddInventoryItemAdv / SetInventoryItemAdv | overrideColors, meshColors (a color array), ammoOverride |
Enum member names: EBRColorSpace Linear Srgb Oklab Hsv; EBREasingFunction
Linear Sine Quad Cubic Quart Quint Expo Circ Back Elastic Bounce;
EBREasingDirection In Out InOut; EBRTextTypeface Regular Bold Italic BoldItalic;
EBRDisplayTextJustification Left Center Right; EBRDisplayTextEasing
Linear EaseIn EaseOut EaseInOut; EBrickDirection X_Positive X_Negative Y_Positive Y_Negative Z_Positive Z_Negative.
Each of these schema enum types is also usable directly, under its clean
Wirescript name, as a built-in game enum –
EBREasingFunction as EasingFunction, EBREasingDirection as
EasingDirection, EBRColorSpace as ColorSpace, EBRTextTypeface as
TextTypeface, EBRDisplayTextJustification as DisplayTextJustification,
EBRDisplayTextEasing as DisplayTextEasing, and EBrickDirection as
Direction.
Clock (Event)
The Clock gate emits a periodic execution pulse forever; it reads as an event.
It takes interval and enabled, both wire inputs: a constant bakes into
the gate and a variable wires in, so the rate and the on/off state can change at
runtime. The handler body runs on each pulse.
in running: bool
var ticks: int = 0
on Clock(interval = 2.0, enabled = running) {
ticks = ticks + 1
}
ChatCommand (Event)
Registers a chat command. The call parens hold only config args (the
command name and an optional description); the event’s data outputs are bound
by a trailing -> (…) tuple capture (or -> { field: local } record):
- String literals fill the config fields in order:
CommandName, thenHelpText. The description can also be given by name asDescription = "...". - The
->capture binds the event’s data outputs, in order:controller(the player who typed it), thenarguments(the command text as a string).
on ChatCommand("greet", "Greets the player") -> (controller, arguments) {
// CommandName = "greet", HelpText = "Greets the player"
// controller: the player who typed the command
// arguments: the command text as a string
controller.ShowStatusMessage("You said: ${arguments}")
}
The description is optional and can use the named form. Binding params are also optional — omit the ones you don’t need:
on ChatCommand("wave", Description = "Wave at everyone") {
// no bindings needed
}
Custom Events
A named, cross-gate event channel that carries up to 8 data values. Each comes
in two flavours: personal (same-owner) and global (ownership-agnostic), on
separate channel namespaces — a personal "x" and a global "x" never mix.
SendCustomEvent (Exec)
SendCustomEvent(name, data1, … data8, target = …) — name is the channel, a
constant string baked into the gate (a variable or computed value is a WS028
error), followed by up to 8 optional data values of any type. target is an
optional entity whose grid receives the matching object events. Delivery is
same-owner; use SendGlobalCustomEvent for the ownership-agnostic version. Fires
all matching receivers.
A targeted send needs
isObject = trueon the receiver. Giving atarget(including the receiver spelling,ent.SendCustomEvent(...)) makes it an object event, and only an object-scoped receiver matches one. A plainon CustomEvent("x")is scoped grid-wide and silently never fires for it - no error, no warning, at check or compile. The two spellings pair up:ent.SendCustomEvent("ui.init", who) // targeted -> object event on CustomEvent("ui.init", isObject = true) -> (who: character) { } SendCustomEvent("ui.init", who) // untargeted -> grid-wide on CustomEvent("ui.init") -> (who: character) { }This is the usual way to talk to a spawned prefab: keep the entity
SpawnPrefabreturned and target it, withisObject = trueon the prefab’s receivers.
on hit {
SendCustomEvent("damage", 7, attacker) // send an int + a character
}
on CustomEvent (Event)
The receiver’s call parens hold only the channel name (positional) and any
config (isObject = true); the typed data outputs are bound by a trailing
-> (…) tuple capture. Each data slot’s type can be given explicitly
(amount: int) or inferred from a matching in-unit
SendCustomEvent on the same channel; when neither is available the slot
defaults to float and a WS042 warning is emitted (the game stores each
data slot as a typed value, not any). Unused slots default to float.
isObject = true is constant config that scopes the receiver to a specific
grid/object (an object event) instead of firing grid-wide.
var lastDamage: int = 0 // a top-level var is already persistent — no `static`
on CustomEvent("damage") -> (amount: int, attacker: character) {
lastDamage = amount
attacker.ShowStatusMessage("You took ${amount} damage")
}
Global variants — SendGlobalCustomEvent / on GlobalCustomEvent
SendGlobalCustomEvent and on GlobalCustomEvent are the ownership-agnostic
counterparts: delivery ignores the owner, reaching every matching global receiver.
They have the same shape (constant channel name, up to 8 typed data values,
optional target entity, isObject config), on a channel namespace that is
separate from the personal one — SendGlobalCustomEvent("x") reaches
on GlobalCustomEvent("x") but never on CustomEvent("x"), and vice versa.
var total: int = 0
on GlobalCustomEvent("score") -> (points: int) { total = total + points }
on hit { SendGlobalCustomEvent("score", 10) }
One-tick delay. A
CustomEventreceiver fires on the tick after theSendCustomEventruns — the pulse is delivered on the next frame, not synchronously within the sender’s exec chain. Anything that must observe the event’s effect immediately has to account for that one-tick latency (and a send → receive → send round trip costs a tick each hop).
Signature checking (
WS030). When a send targets a constant channel name, the compiler compares each data value’s wire type against the matching receiver’s declared param types and warns (WS030) on a mismatch — e.g. sending afloatwhere the receiver declaredint. This runs for both the personal (SendCustomEvent/on CustomEvent) and global (SendGlobalCustomEvent/on GlobalCustomEvent) pairs, each within its own namespace. Types that share a wire variant are interchangeable and never flagged (any two entity kinds —character/entity/controller/… — are all the sameObjectvariant). The channel name must be a constant literal, so every send’s receiver set is known at compile time. In the editor, go-to-definition on a send’s channel-name string jumps to the receiver.A non-constant data value is still typed as
floaton the wire at emit rather than from the value’s real type — full end-to-end typing waits on generics. For now the receiver’s annotations are the source of truth, and constant sends carry their type.
Prefab Spawning (Exec)
SpawnPrefab
| Parameter | Type | Required | Description |
|---|---|---|---|
prefab | prefab ref | No | The prefab to spawn — a $./file.brz archive, a $./file.ws source compiled on reference, or an inline $ triple-backtick block (prefab reference). Embedded into the bundle at compile. |
offset | vector | No | Spawn position offset |
rotation | rotator | No | Spawn rotation offset |
velocity | vector | No | Initial velocity of the spawned entity |
lifetime | float | No | Lifetime in seconds (0 = permanent) |
limit | int | No | Max concurrent instances |
destroyAll | exec | No | Wire an exec here; pulsing it destroys every entity this gate has already spawned. Independent of the spawn Exec, so one gate both spawns and clears. |
Returns: entity – the spawned entity. Give the prefab with a $…brz
reference; omit prefab to configure it on the placed gate in-game instead
(copy a prefab onto the Spawn Prefab brick).
on trigger {
let spawned = SpawnPrefab(
prefab = $./turret.brz,
offset = Vec(0.0, 0.0, 50.0),
lifetime = 10.0,
limit = 5
)
spawned.SetVelocity(linear = launchDir)
}
destroyAll is a secondary exec trigger (like Timer’s restart): wire a reset /
round-start signal into it to remove every entity this spawner has produced, without
re-spawning. The gate spawns when its own exec chain fires and clears when
destroyAll fires:
in reset: exec
on trigger {
let cube = SpawnPrefab(prefab = $./msg.brz, limit = 64, destroyAll = reset)
cube.SetTag(payload)
}
// pulsing `reset` destroys every cube this gate has spawned
A $./file.brz reference reads the .brz at compile and embeds it into the
output bundle (content-addressed at Prefabs/Uploads/<hash>.brz), so the
compiled program carries its prefab. A $./file.ws reference compiles that
source first and embeds the result, and a prefab can also be written inline as a
$ followed by a triple-backtick block. See
Prefab References and the per-entity
fan-out section in best practices.
SpawnExplosion
Spawns an explosion of a given projectile/explosion class.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectileType | class ref | Yes | The explosion/projectile class — a $… asset reference (or a wired value) |
instigator | entity | No | The character/entity that caused it (kill credit, etc.) |
offset | vector | No | Spawn position offset |
scale | float | No | Explosion scale multiplier |
damage | float | No | Damage multiplier |
on hit {
SpawnExplosion($BRWeaponProjectile/Grenade, instigator = attacker, scale = 2.0, damage = 1.5)
}
SpawnExplosionAt
Like SpawnExplosion, but at an absolute world position instead of an offset
from the gate’s brick.
| Parameter | Type | Required | Description |
|---|---|---|---|
worldPosition | vector | Yes | Absolute world position to spawn the explosion at |
projectileType | class ref | Yes | The explosion/projectile class — a $… asset reference (or a wired value) |
instigator | entity | No | The character/entity that caused it (kill credit, etc.) |
scale | float | No | Explosion scale multiplier |
damage | float | No | Damage multiplier |
on hit {
SpawnExplosionAt(Vec(0.0, 0.0, 200.0), $BRWeaponProjectile/Grenade, instigator = attacker)
}
Raycasting (Exec)
Sweep
| Parameter | Type | Required | Description |
|---|---|---|---|
origin | vector | Yes | Ray start position |
direction | vector | Yes | Ray direction |
Distance | float | Yes | Maximum ray distance |
radius | float | No | Sphere radius (0 = line trace) |
relative | bool | No | Interpret origin/direction in the owning grid’s local frame |
ignore | entity | No | A single entity to exclude from hits |
ignoreList | entity[] | No | An array var of additional entities to exclude (on top of ignore) |
ignoreOwningGrid | bool | No | Exclude the grid this gate sits on (prevents self-hits) |
collisionChannel | int | No | Collision channel to sweep on (EBRSweepCollisionChannel: 0 Physics, 1 Weapon, 2 Interaction, 3 Tool, 4–7 Player1–4, 8 NoAdditionalRestriction) |
detectBricks | bool | No | Detect brick grids, including spawned prefabs — default false |
detectMap | bool | No | Detect the static world / environment — default false |
detectPhysics | bool | No | Detect physics-simulating objects — default false |
detectPlayers1–detectPlayers4 | bool | No | Detect players on collision channels 1–4 — default false |
Detection is opt-in. Every
detect*flag defaults tofalse, so a Sweep with none set detects nothing and always firesMiss. Enable the channel you want:detectBricksfor brick grids / spawned prefabs,detectPlayers1for players,detectPhysicsfor loose physics objects.
Returns a record with fields:
HitDistance: float– Distance to hit pointHitEntity: entity– Entity that was hitHitLocation: vector– World position of hitHitNormal: vector– Surface normal at hitHit: exec– Fires if something was hitMiss: exec– Fires if nothing was hit
on trigger {
let aim = char.GetAim()
// Run the Sweep INSIDE the exec handler; handle it with nested Hit/Miss branches.
// A top-level `Sweep(..., exec = t)` does NOT fire.
let r = Sweep(aim.Origin, aim.Direction, 10000.0,
radius = 5.0, ignore = char, detectPlayers1 = true)
on r.Hit { r.HitEntity.ShowStatusMessage("hit!") }
on r.Miss { /* nothing in range */ }
// If should also work here
if r.Hit { r.HitEntity.ShowStatusMessage("hit!") }
if r.Miss { /* nothing in range */ }
}
Random (Exec)
| Function | Signature | Description |
|---|---|---|
Random(min, max) | (min: int, max: int) -> int | Random integer in [min, max] |
Random(min, max) | (min: T, max: T) -> T, T ∈ vector/rotator/quat/color | Per-component random of the same type |
on RoundStart() {
let r = Random(0, 15)
if r == 0 { specialEvent = true }
}
Random rides the same PrimMath variant as the arithmetic operators, so its min/max may be a vector, rotator, quat, or color — it then rolls each component independently and returns that same type. Random(Vec(0.0, 0.0, 0.0), Vec(1.0, 1.0, 1.0)) is a random point in the unit cube; Random(a, b) on two colors is a random color between them (all four RGBA channels). Both bounds share the type of the result.
Note: Random is an exec function because it requires sequential execution to produce a new random value each time.
Sleep / Delay (Pure)
Buffer gates that delay a value passing through. Most useful with await and the _ armed flag placeholder.
| Function | Signature | Description |
|---|---|---|
Sleep(input, delay?, hold?) | (input: any, delay?: float, hold?: float) -> any | Delay by seconds (BufferSeconds gate) |
SleepTicks(input, delay?, hold?) | (input: any, delay?: int, hold?: int) -> any | Delay by ticks (BufferTicks gate) |
input– the value to delay. Use_insideawaitto wire the armed flag.delay– seconds/ticks to wait before the output follows the input.hold– seconds/ticks to hold the output after the input drops to zero. Set to -1 to use delay instead.
// Sleep 2 seconds using await
on start {
await Sleep(_, delay = 2.0)
doAfterDelay()
}
// Sleep 60 ticks (~1 second at 60Hz)
on start {
await SleepTicks(_, delay = 60)
doAfterDelay()
}
// Pure usage: delay a signal by 5 ticks
let delayed = SleepTicks(rawSignal, delay = 5)
Exec Override
Exec functions that are called outside of an exec context can be given an explicit exec named argument to provide the execution trigger:
// Outside a handler -- provide exec explicitly
let r = Random(0, 10, exec = someTrigger)
This wires someTrigger as the exec input of the gate, bypassing the requirement for an enclosing handler context.
Newer builtins
Player-reference gates (DisplayText, ShowChatMessage, HasRole, leaderboard and
team setters, the join/left/chat events, ControllerOf/CharacterOf) target the
persistent player-state on the current build. The controller type is unchanged and
still wires straight into them — existing scripts keep working.
Entity (Exec)
entity.GetSpeed() -> float // scalar speed
entity.GetVelocityAtPoint(point: vector) -> vector
entity.GetEntityTeam() -> entity // team of any entity (grid/prefab)
entity.SetEntityTeam(team: entity)
entity.IsFrozen() -> bool // whether the entity / brick grid is frozen
entity.DestroySpawned() // despawn a spawned entity
entity.DestroySpawnedPrefab() // despawn a spawned prefab
Character ammo (Exec)
character.GetAmmo(resource: entity) -> int
character.GrantAmmo(resource: entity, amount: int)
character.SetAmmo(resource: entity, amount: int)
character.GetInventoryEntry(slot: int) -> { Item, BrickAsset, EntityType }
character.GetCurrentInventorySlot() -> int
character.GetWeaponChamberAmmo(resource: entity, slot: int) -> int
character.IncWeaponChamberAmmo(resource: entity, slot: int, amount: int)
character.SetWeaponChamberAmmo(resource: entity, slot: int, amount: int)
The CharacterFiredWeapon(character, direction, start) event fires when a player
fires a weapon (direction/start are vectors). Sweep/SweepSimple results also
carry a HitColor field (the color of the surface hit).
Date / time (Pure)
GetUnixTime() -> int
FormatDate(unixTime: int, format: string, useUTC?: bool) -> { Output: string, Success: bool }
Value conversions (Pure)
Remap(value, inMin, inMax, outMin, outMax) -> float // rescale a value between ranges
LogicalShiftRight(a: int, b: int) -> int // logical (unsigned) >>
EnumToInt(value: enum) -> int // enum tag; folds a known variant, else uses the gate
IntToEnum(value: int, wrap?: bool) -> enum // enum type from context; const folds, runtime uses the gate
ItemToPickup(item: entity) -> entity // pickup asset for an item
color.ConvertColor(fromSpace?: int, toSpace?: int) -> color
"A".ToCharCode() -> { Codepoint: int, Success: bool }
FromCharCode(codepoint: int) -> { Character: string, Success: bool }
EnumToInt / IntToEnum are the gate-backed twins of .ToInt() (=
.Discriminant) and Enum.FromInt(n). EnumToInt requires an enum
argument (a non-enum is a type error); a compile-time-known enum folds to its
discriminant literal, a runtime enum routes through the gate. IntToEnum’s
result is an enum whose concrete type comes from the annotated target (like
FromInt); a constant tag folds to the enum record, a runtime tag routes
through the gate, and wrap clamps an out-of-range tag. See
enums.md.
ParseInt / ParseNumber likewise now expose a Success flag: they auto-unwrap to
their parsed Value in arithmetic/comparisons (ParseInt(s) == 5), and .Success is
false when the string wasn’t a valid number.
Self transform + simple raycast (Exec)
GetOwnTransform() -> { Location: vector, Rotation: rotator }
SweepSimple(distance: float, radius?: float, spreadConeAngle?: float)
-> { HitDistance, HitEntity, HitLocation, HitNormal, Hit, Miss }
SweepSimple sweeps from its own brick (the containing microchip’s brick, or the
gate’s own brick if not in a microchip), in the configured direction. It has no origin
input and takes no receiver. To sweep from an arbitrary point, use
the full Sweep(origin, direction, distance, ...) gate, which has a vector origin input.
distance is positional (SweepSimple(500.0, ...)).
Zone array fills (Exec) — array methods
arr.fillFromZoneEntities(zone, tagFilter?) // entities inside a zone
arr.fillFromZonePlayers(zone, tagFilter?) // players inside a zone
The character/entity zone enter/leave events also accept a tagFilter = argument
(alongside zone =) to restrict them to tagged entities.
Generic type syntax
Types may be written in generic form:
var nums: Array<int> = [1, 2, 3] // same as int[]
mod inc(v: Ref<int>) { v = v + 1 } // same as *int
Array<V> and Ref<V> are exact aliases of V[] and *V.
Maps (var m: Map<K, V>)
A map is a keyed variable collection (the MapVar gate family), declared as a var
of the generic Map<K, V> type. Keys must be int, string, or an object reference
(entity/character/controller) — any other key type is a WS039 error; values may
be any wire-storable scalar (int/float/bool/string/vector/rotator/quat/color/object).
A map starts empty unless given a constant literal initializer (= {} is the explicit
empty form).
A map value can also be a record (var m: Map<int, Point>): the map is stored
as one parallel map per field and set/get/has/length/remove/clear/keys fan
out (values/copyFrom are WS050). A record can never be a map key. See
Records as storage.
var scores: Map<string, int>
var names: string[]
on tick {
scores.set("alice", 10) // insert / overwrite
let g = scores.get("alice") // { Value, Found } — auto-unwraps to Value
if g.Found { PrintToConsole(g.Value) }
if scores.has("bob") { ... }
scores.remove("bob") // -> bool (was present)
let n = scores.length()
scores.keys(names) // fill an array with the keys
scores.clear()
}
Methods (exec context, like array methods): set(key, value), get(key),
has(key), remove(key), clear(), copyFrom(otherMap), length(),
keys(destArray), values(destArray).
Map literals
A var of Map<K, V> type can be given literal contents with { ... }.
Entries use => for any key expression, or : for a string / atom / int
literal key (or a bracketed [expr] computed key):
var m: Map<int, int> = { :red => 10, 7 => 0 } // arrow -- any key
var s: Map<string, int> = { "red": 1, "blue": 2 } // colon -- string literal key
var a: Map<int, int> = { :red: 1, :blue: 2 } // colon -- atom literal key
var e: Map<int, int> = {} // explicit empty map
on tick { m = { [runtimeKey] => x } } // computed key -- desugars
A constant map literal (every key and value a compile-time constant) in a
var initializer bakes straight into the map at rest – no runtime
gates, the map loads pre-populated. An initializer with any non-constant
entry doesn’t bake – its entries are dropped (with a compiler warning) and
the map loads empty; build it at runtime instead. Inside an exec handler,
m = { ... } (or a literal with runtime keys/values) desugars to clear()
followed by one set(key, value) per entry, in source order – the same
clear-then-populate shape as array literal assignment.
{ foo: 1 } with a bare identifier key is a record literal, not a map –
: only introduces a map key for a string/atom/int literal or a [expr]
computed key. Use foo => 1 or [foo]: 1 to key a map by an identifier’s
value.
Assigning a whole map from another map variable (m = m2) is not supported
– there is no whole-map-copy gate. Use m.copyFrom(m2) instead.
Exec-flow gates (Union / Branch)
Two exec-signal combinators, callable like any other builtin:
Union(a: exec, b: exec) -> exec
Branch(cond: bool, exec: exec) -> (A: exec, B: exec)
-
Union(a, b)merges two exec signals into one — the result fires whenever either input fires. Handy for running one handler from several triggers:let go = Union(init, Change(team)) on go { rebuild() } -
Branch(cond, exec)routes an incoming exec to.Aor.Bdepending oncondat the instant it fires (a runtimeiffor exec flow). Bind or trigger on the named outputs:on Branch(isRed, tick).A { redTick() } on Branch(isRed, tick).B { blueTick() }
Callable gate builtins
Every variable / array / map wire gate is also exposed as a plain function named after the in-game gate, alongside its method / operator / assignment form. The two forms are identical — the call desugars to the method or assignment at parse time — so pick whichever reads better. Named after the game gates for discoverability (and completion).
Variables
| Builtin | Same as |
|---|---|
GetVariable(v) | v |
SetVariable(v, x) | v = x |
IncrementVariable(v, n) | v = v + n |
Arrays — the function name maps to the array method of the same operation; the receiver is the first argument:
| Builtin | Same as | Builtin | Same as | |
|---|---|---|---|---|
GetArrayElement(a, i) | a.get(i) | SortArray(a, desc?) | a.sort(desc?) | |
SetArrayElement(a, i, x) | a[i] = x | ReverseArray(a) | a.reverse() | |
PushToArray(a, x) | a.push(x) | ShuffleArray(a) | a.shuffle() | |
PopFromArray(a) | a.pop() | SwapArrayElements(a, i, j) | a.swap(i, j) | |
InsertArrayElement(a, i, x) | a.insert(i, x) | SliceArray(a, src, s, n) | a.slice(src, s, n) | |
RemoveArrayElement(a, i) | a.remove(i) | AppendArray(a, src) | a.append(src) | |
GetArrayLength(a) | a.length() | CopyArray(a, src) | a.copyFrom(src) | |
FindArrayElement(a, x) | a.find(x) | SumArray(a) | a.sum() | |
ClearArray(a) | a.clear() | AverageArray(a) | a.average() | |
FillArray(a, x) | a.fill(x) | ArrayMaximum(a) | a.max() | |
ResizeArray(a, n, x) | a.resize(n, x) | ArrayMinimum(a) | a.min() |
Array fills (Gamemode / Zone gates — the function-call twins of the
fillFrom* array methods):
| Builtin | Same as |
|---|---|
FillArrayFromPlayers(a) | a.fillFromPlayers() |
FillArrayFromTeamMembers(a, team) | a.fillFromTeam(team) |
GetPlayersInZone(a, zone, tagFilter?) | a.fillFromZonePlayers(zone, tagFilter?) |
GetEntitiesInZone(a, zone, tagFilter?) | a.fillFromZoneEntities(zone, tagFilter?) |
Maps — map to the map methods of the same name:
| Builtin | Same as | Builtin | Same as | |
|---|---|---|---|---|
GetMapElement(m, k) | m.get(k) | ClearMap(m) | m.clear() | |
SetMapElement(m, k, v) | m.set(k, v) | CopyMap(m, src) | m.copyFrom(src) | |
HasMapElement(m, k) | m.has(k) | GetMapLength(m) | m.length() | |
RemoveMapElement(m, k) | m.remove(k) | GetMapKeys(m, out) | m.keys(out) | |
GetMapValues(m, out) | m.values(out) |
All array/map operations remain exec-context only (they run on the exec chain), exactly like their method forms.