Skip to Content

Value types

The vector, vector2d, vector4d, angle and color FFI value types: constructors, methods, operators, pointers, containers, 64-bit integers and angle maths.


Value type overview

ValueConstructorFields
3D vectorvectorx, y, z
2D vectorvector2dx, y
4D vectorvector4dx, y, z, w
Angleanglepitch, yaw, roll; aliases x, y, z
Colourcolorr, g, b, a

These five LuaJIT FFI types are the only value types; their Lua type is cdata. Every API function that returns a vector, angle or colour returns a new copy of one of them, including entity fields, traces, hitboxes, render.screen_size, input.mouse_position, cmd.view_angles and console.variable:get for colours. Modifying a returned copy does not change game state. Only view.origin and view.angles return pointers into game memory.

Vector and angle components use 32-bit floats. Assigning 0.1 may read back as 0.10000000149011612; compare with a tolerance.

local b = a shares the same object. Use a constructor to copy: vector(a).

FFI value types

Constructors

vector(x?, y?, z?)
vector2d(x?, y?)
vector4d(x?, y?, z?, w?)
angle(pitch?, yaw?, roll?)
color(r?, g?, b?, a?)

Missing vector and angle components default to 0. In numeric colour construction, RGB channels default to 0 and alpha to 255.

Vector constructors also accept tables and compatible values:

vector{ x = 1, y = 2, z = 3 }
vector{ 1, 2, 3 }
vector(existing_value)
color{ r = 255, g = 0, b = 0, a = 255 }
color(existing_colour)
  • Table components default to 0. For color, this includes alpha.
  • The angle table constructor accepts x, y, z, not pitch, yaw, roll.
  • Copies use matching fields. vector(v) accepts an FFI vector, a vector pointer or another value with x, y, z, including an angle.
  • Colour channels are not clamped by FFI construction; values outside 0..255 wrap.

Methods

In the following table, vector dimensions identify the applicable types. Method arguments accept the values listed under Argument conversion, including tables.

MethodAvailable onResult
unpack()All value typesComponents as separate returns.
length(), lengthsqr()Vectors and angleNumber.
length_sqr()2D and 3D vectorsAlias of lengthsqr.
length2d(), length_2d()3D vectorXY length.
length2dsqr(), length_2d_sqr()3D vectorSquared XY length.
dot(b)All vectorsDot product.
dist(b), distsqr(b)All vectorsDistance or squared distance.
dist_to(b)3D vectorAlias of dist.
dist2d(b), dist_to_2d(b), dist2dsqr(b)3D vectorXY distance or squared XY distance.
cross(b)All vectors3D vector for 3D input; number for 2D and 4D input.
lerp(b, t)All vectors and colourNew value of the same type; t is required. Colour channels are rounded and clamped.
normalized()2D/3D vectors and angleNew normalized value; zero vectors remain zero, angle components use [-180, 180).
normalize()3D vector and angleNormalizes in place and returns the same value.
to(b)3D vectorUnit vector from this point to b.
to_angle()3D vectorDirection angle; pitch [-90, 90], yaw (-180, 180].
forward()AngleForward vector.
left_vectors()3D vectorLeft and up vectors.
left_vectors()AngleForward, left and up vectors.
right_vectors()3D vectorRight and up vectors.
right_vectors()AngleForward, right and up vectors.
is_valid()Vectors and angleWhether all components are finite.
is_zero(tolerance?)All vectorsWhether every absolute component is below tolerance; default 0.01.
hex()Colour"RRGGBBAA".

vector4d:cross(b) returns a.x*b.y - a.y*b.x - a.z*b.z - a.w*b.w. This is compatibility behaviour, not a geometric cross product.

Operators

For FFI vectors and angles:

ExpressionBehaviour
a + b, a - bComponentwise with a compatible value, pointer or named table. A numeric operand raises.
a * bComponentwise, or scalar multiplication with the number on either side.
a / bComponentwise, or division by a number on the right.
-aNew negated value.
a == bExact component comparison, only for the same FFI kind. Other kinds return false.

color supports only equality and string conversion. FFI value types do not provide <, # or concatenation.

String representations include vector(1.000000, 2.000000, 3.000000), vector2d(...), vector4d(...), angle(...) and color(255, 0, 0, 255).

For angles, a.pitch = 10 writes a.x. Assigning an unknown field raises 'angle' has no member named '<key>'.

FFI pointers

view.origin is a vector* into game memory. Field access, methods, string conversion and equality operate on the pointed-to value, but p + v uses C pointer arithmetic. Copy before vector arithmetic:

local origin = vector(view.origin)

The C types vector, vector2d, vector4d, angle and color are declared in every script. They may be used in ffi.cast and declarations, but cannot be redefined with ffi.cdef.

Containers

Entity fields may expose live game arrays as utl_vector_bool, utl_vector_int, utl_vector_uint32_t, utl_vector_float, utl_vector_vec3_t or utl_vector_angle_t. Changes write directly into game memory.

OperationBehaviour
#vElement count.
v[i]One-based access; nil outside the valid range.
v[i] = valueReplaces an existing element; out-of-range writes are ignored.
v:push_back(value)Appends and returns the new index.
v:remove_at(i)Removes the element and shifts later elements.
v:clear()Removes all elements.
v:ipairs()Iterator for for i, value in v:ipairs() do ... end.

Vector and angle elements are vector and angle copies. v[1].x = 5 does not update the array; assign v[1] = vector(...) instead.

Do not confuse these one-based containers with schema array_t, which is zero-based.

64-bit integers

int64_t and uint64_t are LuaJIT cdata used for command button masks and cvar flags.

local bit = require 'bit'
local mask = bit.lshift(1ULL, 33)
local all_bits = -1LL

Arithmetic and comparison with Lua numbers are supported. LuaJIT has no << operator; use bit.lshift, bit.band and bit.bor with 64-bit cdata when needed. Operations on ordinary numbers use the bit library's 32-bit behaviour.

tostring(1ULL) returns "1ULL". tonumber converts to a double, with exact integer representation only up to 2^53.

Angle maths

The standard math table has no additions; angle maths is done with vector and angle methods. vector:to_angle() returns pitch in [-90, 90] and yaw in (-180, 180], with (-90, 0, 0) / (90, 0, 0) for a vector pointing straight up / down. angle:normalized() wraps every component, roll included, into [-180, 180).

local aim = (target - eye):to_angle()                   -- angle from eye to target
local forward, right, up = view:right_vectors()          -- basis of an angle
local d = (aim - view):normalized()                      -- field of view between two angles
local fov = math.sqrt(d.pitch * d.pitch + d.yaw * d.yaw)

On this page