Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  • Pure functions are expressions – they produce a value and can be used anywhere.
  • Exec functions require an active exec context (inside an on handler). 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.

FunctionSignatureDescription
sin(x)(x: float) -> floatSine
cos(x)(x: float) -> floatCosine
tan(x)(x: float) -> floatTangent
asin(x)(x: float) -> floatArc sine
acos(x)(x: float) -> floatArc cosine
atan(x)(x: float) -> floatArc tangent
atan2(y, x)(y: float, x: float) -> floatTwo-argument arc tangent
sinh(x)(x: float) -> floatHyperbolic sine
cosh(x)(x: float) -> floatHyperbolic cosine
tanh(x)(x: float) -> floatHyperbolic tangent
asinh(x)(x: float) -> floatInverse hyperbolic sine
acosh(x)(x: float) -> floatInverse hyperbolic cosine
atanh(x)(x: float) -> floatInverse hyperbolic tangent
exp(x)(x: float) -> floate^x
ln(x)(x: float) -> floatNatural logarithm
sign(x)(x: float) -> floatSign (-1, 0, or 1)
abs(x)(x: float) -> floatAbsolute value
sqrt(x)(x: float) -> floatSquare root
pow(x, exponent)(x: float, exponent: float) -> floatPower
clamp(x, min, max)(x: float, min: float, max: float) -> floatClamp to range
round(x)(x: float) -> floatRound to nearest integer
floor(x)(x: float) -> floatRound down
ceil(x)(x: float) -> floatRound up
min(a, b)(a: float, b: float) -> floatMinimum of two values
max(a, b)(a: float, b: float) -> floatMaximum of two values
log(x, base)(x: float, base: float) -> floatLogarithm with arbitrary base
lerp(a, b, t)(a: T, b: T, t: float) -> TLinear interpolation; T is any math variant (see Easing/Tween)
fmod(a, b)(a: float, b: float) -> floatFloored modulo
Deg2Rad(x)(x: float) -> floatDegrees to radians
Rad2Deg(x)(x: float) -> floatRadians 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)

FunctionSignatureDescription
BitCount(x)(x: int) -> intCount set bits (popcount)
BitNand(a, b)(a: int, b: int) -> intBitwise NAND (same as ~(a & b))
BitNor(a, b)(a: int, b: int) -> intBitwise 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)

FunctionSignatureDescription
Vec(x, y, z)(x: float, y: float, z: float) -> vectorConstruct a vector
Dot(a, b)(a: vector, b: vector) -> floatDot product
Cross(a, b)(a: vector, b: vector) -> vectorCross product
Normalize(v)(v: vector) -> vectorNormalize to unit length
Magnitude(v)(v: vector) -> floatLength of vector
MagnitudeSq(v)(v: vector) -> floatSquared length (avoids sqrt)
Distance(a, b)(a: vector, b: vector) -> floatDistance between two points
DistanceSq(a, b)(a: vector, b: vector) -> floatSquared distance (avoids sqrt)
ScaleVec(v, s)(v: vector, scalar: float) -> vectorScale vector by scalar
RotToDir(rot)(rot: vector) -> vectorConvert 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.

FunctionSignatureDescription
Rotation(pitch, yaw, roll)(float, float, float) -> rotatorConstruct an euler rotator
r.ToEuler()(rotator) -> {Pitch, Yaw, Roll: float}Split a rotator into components
dir.ToRotation()(vector) -> quatQuaternion that points along dir
q.ToDirection()(quat) -> vectorForward direction of q
v.Rotate(q)(vector, quat) -> vectorRotate a vector by a quaternion
q.Invert()(quat) -> quatInverse rotation
from.RotationTo(to)(vector, vector) -> quatQuaternion rotating from onto to
a.AngleTo(b)(quat, quat) -> floatAngle between two quaternions
a.Slerp(b, alpha)(quat, quat, float) -> quatSpherical interpolation
axis.RotationByAngle(angle)(vector, float) -> quatQuaternion from axis + angle (radians)
q.ToAxisAngle()(quat) -> {Axis: vector, Angle: float}Decompose into axis + angle
Quat(x, y, z, w)(float, float, float, float) -> quatConstruct a quaternion from raw components
q.SplitQuat()(quat) -> {X, Y, Z, W: float}Decompose into raw components
a.QuatDot(b)(quat, quat) -> floatQuaternion 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)

FunctionSignatureDescription
Color(r, g, b, a?)(r: float, g: float, b: float, a?: float) -> colorConstruct a color (linear RGBA, 0-1 range)
ColorSRGB(r, g, b, a)(int, int, int, int) -> colorConstruct from sRGB bytes (0-255)
ColorHex(hex)(string) -> colorConstruct 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) -> stringHex string
a.ColorBlend(b, alpha)(color, color, float) -> colorBlend 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

FunctionSignatureDescription
Cycle(count)(count: int) -> int execReturns 0,1,…,count-1 advancing each exec pulse
Toggle()() -> bool execFlips between false/true each exec pulse

Select / Swap (Pure)

FunctionSignatureDescription
Select(cond, a, b)(cond: bool, a: any, b: any) -> anyReturns 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

FunctionSignatureDescription
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) -> boolBool pulse when the input changes
Change(input)(input: any) -> anyPulse 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)

FunctionSignatureDescription
All string functions support receiver syntax on string:
FunctionSignatureDescription
s.Length()(s: string) -> intString length
s.Contains(search, caseSensitive?)(s: string, search: string, caseSensitive?: bool) -> boolCheck if string contains substring
s.StartsWith(prefix, caseSensitive?)(s: string, prefix: string, caseSensitive?: bool) -> boolCheck prefix
s.EndsWith(suffix, caseSensitive?)(s: string, suffix: string, caseSensitive?: bool) -> boolCheck suffix
s.Find(search, caseSensitive?, start?)(s: string, search: string, caseSensitive?: bool, start?: int) -> intFind substring index (-1 if not found)
s.Substring(start, length)(s: string, start: int, length: int) -> stringExtract substring
s.Replace(search, replacement, caseSensitive?, maxReplacements?, start?)(s: string, search: string, replacement: string, caseSensitive?: bool, maxReplacements?: int, start?: int) -> stringReplace 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) -> stringConvert to lowercase
s.ToUpper()(s: string) -> stringConvert to uppercase
s.Trim()(s: string) -> stringRemove leading/trailing whitespace
s.ParseInt() / ParseInt(s)(s: string) -> intParse an integer from text
s.ParseNumber() / ParseNumber(s)(s: string) -> floatParse 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)

FunctionSignatureDescription
Fmt(format, a?, b?, c?, d?, e?, f?, g?)(format: any, a-g?: any) -> stringFormat 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.

MethodSignatureDescription
arr.push(value)(value: T)Append an element
arr.pop()() -> TRemove 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()() -> intNumber 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()() -> TSum of elements
arr.min() / arr.max()() -> TSmallest / largest element
arr.average()() -> floatMean 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 axes
  • MouseWheel: float – mouse wheel delta
  • PressedC / 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 position
  • Direction: 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

ParameterTypeRequiredDescription
targetcontrollerYesPlayer to display to
textanyYesText content (auto-converted to string)
positionX / positionYfloatNo2D screen position, per axis
anchorX / anchorYfloatNo2D anchor point, per axis
scaleX / scaleYfloatNo2D scale, per axis
pivotX / pivotYfloatNo2D pivot point, per axis
shadowOffsetX / shadowOffsetYfloatNo2D drop-shadow offset, per axis
anglefloatNoRotation angle
outlineSizeintNoText outline size
outlineColorcolorNoOutline color
fontColorcolorNoFont color
shadowColorcolorNoDrop-shadow color
miteredOutlineboolNoSharp (mitered) outline corners
letterSpacingfloatNoExtra spacing between letters
lineHeightfloatNoLine-height multiplier
wrapWidthfloatNoWrap width (0 = no wrap)
skewfloatNoItalic-style skew
zOrderintNoDraw order
lifetimefloatNoDisplay duration (seconds)
transitionfloatNoSeconds to interpolate to the new state when re-emitted with the same textId
textIdintNoUnique ID for updating text in-place
fontSizeintNoFont size (constant only)
justifyintNoJustification: DisplayTextJustification.Left / .Center / .Right, or bare Left / Center / Right (constant only)
easingintNoTransition curve: DisplayTextEasing.Linear / .EaseIn / .EaseOut / .EaseInOut, or bare Linear / EaseIn / EaseOut / EaseInOut (constant only)
typefaceintNoTypeface: TextTypeface.Regular / .Bold / .Italic / .BoldItalic, or bare Regular / Bold / Italic / BoldItalic (constant only)
fontasset refNoFont 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)

FunctionSignatureDescription
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()() -> floatSeconds elapsed since the previous tick
ServerUptime()() -> floatSeconds the server has been running
ReadBrickGrid()() -> entityThe brick grid this gate’s microchip is on, as an entity
NearlyEqual(a, b, tolerance)(a: float, b: float, tolerance: float) -> boolApproximate float equality
Dampen(target, smoothTime)(target: float, smoothTime: float) -> floatCritically-damped smoothing toward a target
Easing(a, b, blend, fn?, dir?)(a: T, b: T, blend: float, fn?: any, dir?: any) -> TEase from a to b by blend
Tween(target, duration, fn?, dir?)(target: T, duration: float, fn?: any, dir?: any) -> TStateful 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:

GateConfig attributes
SweepbodyPartsOnly
SweepSimpledirection (EBrickDirection), spreadTowardCenter, detectBricks, detectPlayers14, bodyPartsOnly, detectPhysics, detectMap
BlendclampAlpha
ColorBlendblendSpace (EBRColorSpace), clampAlpha
SlerpshortestPath, clampAlpha
Easingfunction (EasingFunction, schema EBREasingFunction), direction (EasingDirection, schema EBREasingDirection)
ConvertColorfromSpace, toSpace (EBRColorSpace)
DisplayTextfontSize, justify (DisplayTextJustification, schema EBRDisplayTextJustification), easing (DisplayTextEasing, schema EBRDisplayTextEasing), typeface (TextTypeface, schema EBRTextTypeface), font (a $Font/... asset ref)
GetAimlocalAim
AddInventoryItemAdv / SetInventoryItemAdvoverrideColors, 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 enumEBREasingFunction 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, then HelpText. The description can also be given by name as Description = "...".
  • The -> capture binds the event’s data outputs, in order: controller (the player who typed it), then arguments (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 = true on the receiver. Giving a target (including the receiver spelling, ent.SendCustomEvent(...)) makes it an object event, and only an object-scoped receiver matches one. A plain on 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 SpawnPrefab returned and target it, with isObject = true on 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 CustomEvent receiver fires on the tick after the SendCustomEvent runs — 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 a float where the receiver declared int. 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 same Object variant). 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 float on 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

ParameterTypeRequiredDescription
prefabprefab refNoThe 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.
offsetvectorNoSpawn position offset
rotationrotatorNoSpawn rotation offset
velocityvectorNoInitial velocity of the spawned entity
lifetimefloatNoLifetime in seconds (0 = permanent)
limitintNoMax concurrent instances
destroyAllexecNoWire 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.

ParameterTypeRequiredDescription
projectileTypeclass refYesThe explosion/projectile class — a $… asset reference (or a wired value)
instigatorentityNoThe character/entity that caused it (kill credit, etc.)
offsetvectorNoSpawn position offset
scalefloatNoExplosion scale multiplier
damagefloatNoDamage 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.

ParameterTypeRequiredDescription
worldPositionvectorYesAbsolute world position to spawn the explosion at
projectileTypeclass refYesThe explosion/projectile class — a $… asset reference (or a wired value)
instigatorentityNoThe character/entity that caused it (kill credit, etc.)
scalefloatNoExplosion scale multiplier
damagefloatNoDamage multiplier
on hit {
  SpawnExplosionAt(Vec(0.0, 0.0, 200.0), $BRWeaponProjectile/Grenade, instigator = attacker)
}

Raycasting (Exec)

Sweep

ParameterTypeRequiredDescription
originvectorYesRay start position
directionvectorYesRay direction
DistancefloatYesMaximum ray distance
radiusfloatNoSphere radius (0 = line trace)
relativeboolNoInterpret origin/direction in the owning grid’s local frame
ignoreentityNoA single entity to exclude from hits
ignoreListentity[]NoAn array var of additional entities to exclude (on top of ignore)
ignoreOwningGridboolNoExclude the grid this gate sits on (prevents self-hits)
collisionChannelintNoCollision channel to sweep on (EBRSweepCollisionChannel: 0 Physics, 1 Weapon, 2 Interaction, 3 Tool, 4–7 Player1–4, 8 NoAdditionalRestriction)
detectBricksboolNoDetect brick grids, including spawned prefabs — default false
detectMapboolNoDetect the static world / environment — default false
detectPhysicsboolNoDetect physics-simulating objects — default false
detectPlayers1detectPlayers4boolNoDetect players on collision channels 1–4 — default false

Detection is opt-in. Every detect* flag defaults to false, so a Sweep with none set detects nothing and always fires Miss. Enable the channel you want: detectBricks for brick grids / spawned prefabs, detectPlayers1 for players, detectPhysics for loose physics objects.

Returns a record with fields:

  • HitDistance: float – Distance to hit point
  • HitEntity: entity – Entity that was hit
  • HitLocation: vector – World position of hit
  • HitNormal: vector – Surface normal at hit
  • Hit: exec – Fires if something was hit
  • Miss: 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)

FunctionSignatureDescription
Random(min, max)(min: int, max: int) -> intRandom integer in [min, max]
Random(min, max)(min: T, max: T) -> T, Tvector/rotator/quat/colorPer-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.

FunctionSignatureDescription
Sleep(input, delay?, hold?)(input: any, delay?: float, hold?: float) -> anyDelay by seconds (BufferSeconds gate)
SleepTicks(input, delay?, hold?)(input: any, delay?: int, hold?: int) -> anyDelay by ticks (BufferTicks gate)
  • input – the value to delay. Use _ inside await to 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 .A or .B depending on cond at the instant it fires (a runtime if for 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

BuiltinSame 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:

BuiltinSame asBuiltinSame as
GetArrayElement(a, i)a.get(i)SortArray(a, desc?)a.sort(desc?)
SetArrayElement(a, i, x)a[i] = xReverseArray(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):

BuiltinSame 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:

BuiltinSame asBuiltinSame 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.