Entities and models
Things in the world the mod places and moves: models, shapes, cameras, and the map’s own parts. A model is a .model file of the mod’s; a shape is a box, a ramp, a cylinder, a sphere or a capsule the mod sizes itself.
Each moves along a path it is given once, so a moving lift costs the network no more than a still one: move to a point, move by an offset, follow a curve, turn, or loop. It can ride another, as a turret rides a lift, and be solid to some things and not others. A solid one carries the players and NPCs standing on it, and pushes those it moves into. One that cannot push a body out of its way pauses, and goes on later by as long; an order to it ends the pause, with no unblocked. See players, NPCs and entities for ids and objects, and who sees what for read_by.
Each file a mod names is loaded by every player as the mod first names it. preload names files ahead, so players load them as they join.
entities
Section titled “entities”entities: {[number]: Entity}The entities by id, entities[id]: on the server those the mod made, alive; on a client those it draws.
Fields
Section titled “Fields”What a player, p, or an entity, e, reads beside the mod’s state. A field of the other side’s fails.
| Name | Type | Side | What it is |
|---|---|---|---|
p.camera | CameraView? | On the server | Where they see from and which way: their eyes, or the camera set_camera gave them. |
e.id | number | On both sides | Its id, as entities[id] finds it. |
e.kind | string? | On both sides | What it is: part for the map's, else the kind the mod made it of. |
e.exists | boolean | On both sides | Whether it is still there; after a wait, check it. |
e.owner | Player? | On both sides | The player it is of, as it was made with owner. |
e.position | vector? | On both sides | Where it is. |
e.angles | Angles? | On both sides | How it is turned. |
e.move_seconds_left | number? | On the server | The seconds its move has left; nil once it is still, math.huge for a loop. |
e.rotation_seconds_left | number? | On the server | The seconds its turn has left; nil once it is still. |
e.solid | Solid? | On the server | What it blocks: true, false, or a table of who by kind, of players (true or a group), npcs, the mod's body kinds, shots, projectiles and one_way. Read back as written. |
e.blocked_by | {Player} | On the server | The players in its way as it last moved. |
e.read_by | (Group | ((Player?, Player) -> boolean))? | On the server | Who is shown it; everyone for nil. Written, not read: keep what you gave it in state. |
animate
Section titled “animate”x:animate(animation, { length?, loop?, start? }?)Plays an animation, an .anim file, on a player's body, an NPC or a model.
It is told to each player’s game once, as it starts, so a model walking a path with its walk looping costs no more than one standing still, and a player who joins sees one under way. The one that started last shows: a later one replaces it, and one looping goes on until another starts. A skinned model plays it whole; a body over its upper half, its legs walking, crouching and jumping as it moves, its hands kept on a weapon it holds. The engine’s own, as a shot or a reload, neither end one nor are ended by one. Four are kept at a time, the one shown, one before it and two to come: past them, the one starting last is dropped. On a part, a sound or an effect it fails; on what is gone it does nothing.
| Name | Type | What it is |
|---|---|---|
animation | animation | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
length | number?0 to 3600 | How long one play lasts, in seconds, above 0, at the pace that fits; the clip's own without. |
loop | booleandefault false | Plays it again and again, until another starts. |
start | number-3600 to 5, default 0 | When it starts, in seconds from now: up to an hour ago, seen from part-way through, or up to 5 s ahead. |
- Fails
- a value of the wrong type
- an option it does not have
- a value outside its range
To make a player dance
Section titled “To make a player dance”p:animate("dance_loop", { loop = true })To walk a model of a character along a path
Section titled “To walk a model of a character along a path”local guard = model("mannequin")guard:animate("walk_loop", { loop = true })guard:move_along({ vector.create(0, 0, 10), vector.create(10, 0, 10) }, 8, { face = true })preload
Section titled “preload”preload(kind, ...names)Names files of a kind that every player's game loads as they join, on their loading screen, not as the mod first uses them.
Each file named so far, by this or by any call, is in what a joining player loads before they play. A file first
named later is loaded in the background as it comes: until then a model or an image is not drawn, a sound not heard,
an animation not played, and a weapon neither held nor heard; a sound or an effect then starts from where it would
be by then. The server says once of each, sound "hum" was not preloaded: clients load it as it is first used.
Preload what plays in a hurry, at load or before it is needed, as a nuke’s sound once it is bought. A name of no
file fails at the call, and a mod names 4096 files of each kind at most.
| Name | Type | What it is |
|---|---|---|
kind | "model" | "sound" | "effect" | "image" | "weapon" | "animation" | "material" | Needed. |
...names | string | Any number of them. |
- Fails
- a value of the wrong type
To load a boss’s sounds and model at load
Section titled “To load a boss’s sounds and model at load”preload("sound", "zombie_groan1", "explosion_boom")preload("model", "rocket")spawn_camera
Section titled “spawn_camera”spawn_camera({ at?, angles? }?) → EntityMakes a camera, an entity a player's view can be put on with set_camera, and gives it.
Its kind is camera. It moves, turns, rides and goes as a model does, but is never drawn nor solid, and only the
players looking through it are sent it, so its solid and read_by fail.
| Name | Type | What it is |
|---|---|---|
options | table? | A table of the options below. It, and each option, may be left out. |
at | vector? | Where it is; the map's origin without. |
angles | Angles? | Which way it looks; unturned without. |
- Returns
Entity- Fails
- a value of the wrong type
- an option it does not have
To show a player the map from above
Section titled “To show a player the map from above”local cam = spawn_camera({ at = vector.create(0, 30, -20), angles = { pitch = -0.9 } })p:set_camera(cam, { look = "target" })set_camera
Section titled “set_camera”p:set_camera(target?, { offset?, look? }?)Puts a player's view on an entity, a player or a point; nil puts it back at their eyes.
On their own body it is third person. An offset from a body stays level, so an offset to the left and behind stays
over the shoulder. A point takes no offset and looks with the player’s mouse. A player’s shots leave from the
view, a bash and a throw from the body. The weapon in first person stays unless hide_weapon hides it; on the
player’s own body none is drawn. A camera outlasts death and goes with a new match; one on what goes is back at
the eyes. It takes effect a ping after the call. On a player who left, or what is gone, it does nothing.
Whoever watches the player sees through their camera too; a dead player with a camera of their own watches no one.
Sounds are heard where the camera is, unless a client script hears them at the body (view:set). What the view is
on, and what that rides, is sent to the player whatever its read_by.
| Name | Type | What it is |
|---|---|---|
target | (Player | Entity | number | vector)? | May be left out. |
options | table? | A table of the options below. It, and each option, may be left out. |
offset | vector? | How far from what it is on, turned by it: from a body's eyes, by its view's yaw alone. |
look | ("aim" | "target")? | "aim" turns the view with the player's mouse; "target" faces it as what it is on faces. Aim without. |
- Fails
- a value of the wrong type
- an option it does not have
To watch a player from over their shoulder
Section titled “To watch a player from over their shoulder”p:set_camera(p, { offset = vector.create(0.5, 0.3, -2.5) })To show a killcam from where the killer stood, and back
Section titled “To show a killcam from where the killer stood, and back”victim:set_camera(killer.position + vector.create(0, 1.6, 0))wait(3)victim:set_camera(nil)remove
Section titled “remove”x:remove()Takes away an NPC, a model, a shape, a camera, a part, or a looping sound or effect.
Its handlers go with it, and any order on it after does nothing. On a player it fails: a player is kicked.
To remove a crate once it is broken
Section titled “To remove a crate once it is broken”local crate = model("rocket")crate:remove()To clear every zombie at the end of a round
Section titled “To clear every zombie at the end of a round”for _ = 1, 3 do spawn()endfor _, e in entities do if e.kind == "body" then e:remove() endende:on(event: "move_finished" | "rotate_finished" | "blocked" | "unblocked", fn: (e: Moved) -> ())e:on(event: "damaged", fn: (e: Damaged) -> ())Hears a model's, a shape's or a part's move or turn arrive, or its pause for a body in its way and going on.
e:on(event: "move_finished" | "rotate_finished" | "blocked" | "unblocked", fn: (e: Moved) -> ())
move_finished and rotate_finished are heard as its move or its turn arrives, never after a stop nor of a
loop; blocked and unblocked as it pauses for the players and NPCs in its way, which the event lists, and goes
on. The handler is given the object as part for a part, entity for what the mod made, and runs as a coroutine
that may wait. A part’s handlers are given at a module’s top, and again as the module loads again in place; an
entity’s whenever the mod holds it, kept until it is removed. A module has one handler an event for each object in
each of its files: a second on replaces the first. On a player, an NPC, a sound or an effect it fails; on what is
gone it does nothing.
| Name | Type | What it is |
|---|---|---|
event | "move_finished" | "rotate_finished" | "blocked" | "unblocked" | Needed. |
fn | (e: Moved) -> () | Needed. |
- Fails
- a value of the wrong type
e:on(event: "damaged", fn: (e: Damaged) -> ())
Hears a model, a shape or a part struck by a shot, a bash or an explosion, which makes it damageable.
The event says who struck it, if anyone, attacker; for how much, amount; by what means and with which weapon;
where, point, and going which way, dir; and the projectile whose burst it was, if one. A shot strikes each wall
it goes into once, with the damage it carries there; an explosion strikes the point of its box nearest the burst, if
in its sight. The server works out no damage for what no handler hears. Its handlers are given and kept as the other
events’ are.
| Name | Type | What it is |
|---|---|---|
event | "damaged" | Needed. |
fn | (e: Damaged) -> () | Needed. |
- Fails
- a value of the wrong type
To say when a lift arrives
Section titled “To say when a lift arrives”local lift = model("rocket")local arrived = falselift:on("move_finished", function(e) arrived = true say("the lift is up")end)lift:move_by(vector.create(0, 4, 0), 1)To break a crate after three hits
Section titled “To break a crate after three hits”local crate = model("rocket", { at = vector.create(0, 0, 3) })crate.solid = truelocal hits = 0crate:on("damaged", function(e) hits += 1 if hits == 3 then crate:remove() endend)model(name, options?) → EntityPlaces a model, a .model file of the mod's, and gives its entity.
It stands still until an order moves it, and is not solid until its solid is set. A model and a part of the map
take the same orders: move_to, rotate_by, attach and the rest.
| Name | Type | What it is |
|---|---|---|
name | model | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
at | vector? | Where it stands; the map's origin without. |
angles | Angles? | How it is turned; unturned without. |
owner | (Player | number)? | The player it is about, whom owner, team and spectators pick by. |
read_by | (Group | ((Player?, Player) -> boolean))? | Who is shown it; everyone without. |
kind | string? | Its kind, one entity_kind declared on its base; the base's own without. |
- Returns
Entity- Fails
- a value of the wrong type
- an option it does not have
To place a solid crate, turned
Section titled “To place a solid crate, turned”local crate = model("rocket", { at = vector.create(4, 0, 2), angles = { yaw = math.pi / 2 } })crate.solid = trueTo show a player alone the flag they carry
Section titled “To show a player alone the flag they carry”local flag = model("rocket", { owner = p, read_by = owner })flag:attach(p, { offset = vector.create(0, 1.2, -0.3) })move_to
Section titled “move_to”e:move_to(point, seconds, { timing?, face?, loop? }?)Moves a model or a part to a point in a straight line, over some seconds.
The seconds are above 0 and up to an hour, rounded to whole ticks, one at least; set_position moves it at once. A
move and a turn go on together, and a new move starts from where it is then. move_seconds_left reads how long it
has to go, as of the last tick, so the call that orders a move reads the old one. on("move_finished", fn) hears it
arrive. On an NPC, a player, a sound or an effect it fails; on one gone it does nothing.
| Name | Type | What it is |
|---|---|---|
point | vector | Needed. |
seconds | number | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
timing | Timing? | How it eases, each lap if it loops; linear without. |
face | booleandefault false | Points its nose the way it goes while it moves, its roll kept, until it is turned again. |
loop | booleandefault false | Repeats it for good: it comes back to where it started, each lap taking its seconds. |
- Fails
- a value of the wrong type
- an option it does not have
To open a sliding door
Section titled “To open a sliding door”local door = model("rocket", { at = vector.create(0, 0, 4) })door:move_to(vector.create(2, 0, 4), 1.5, { timing = "ease-in-out" })To run a lift up and down for good
Section titled “To run a lift up and down for good”local lift = model("rocket")lift:move_to(vector.create(0, 6, 0), 4, { timing = "ease-in-out", loop = true })move_by
Section titled “move_by”e:move_by(offset, seconds, { timing?, face?, loop? }?)Moves a model or a part by an offset from where it is, in a straight line, over some seconds.
It takes its seconds and options as move_to does. A move that would take it past the map’s reach is dropped.
| Name | Type | What it is |
|---|---|---|
offset | vector | Needed. |
seconds | number | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
timing | Timing? | How it eases, each lap if it loops; linear without. |
face | booleandefault false | Points its nose the way it goes while it moves, its roll kept, until it is turned again. |
loop | booleandefault false | Repeats it for good: it comes back to where it started, each lap taking its seconds. |
- Fails
- a value of the wrong type
- an option it does not have
To raise a platform by 3 m
Section titled “To raise a platform by 3 m”local platform = model("rocket")platform:move_by(vector.create(0, 3, 0), 2)move_along
Section titled “move_along”e:move_along(points, seconds, options?)Moves a model or a part from where it is through each point in turn, over some seconds.
It takes 1 to 31 points, as markers’ positions, and goes on a curve through them, or straight from one to the
next with straight. Each stretch takes its share of the seconds by its length. An easing that overshoots stops
at the path’s ends.
| Name | Type | What it is |
|---|---|---|
points | {vector} | Needed. |
seconds | number | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
timing | Timing? | How it eases, each lap if it loops; linear without. |
straight | booleandefault false | Goes straight from one point to the next, rather than on a curve through them. |
face | booleandefault false | Points its nose the way it goes while it moves, its roll kept, until it is turned again. |
loop | booleandefault false | Goes round for good, from its last point back to where it started, each lap taking its seconds. |
- Fails
- a value of the wrong type
- an option it does not have
To fly a camera along a path, facing its way
Section titled “To fly a camera along a path, facing its way”local cam = spawn_camera({ at = vector.create(0, 5, 0) })local path = { vector.create(10, 5, 0), vector.create(10, 5, 10), vector.create(0, 8, 10) }cam:move_along(path, 8, { face = true, timing = "ease-in-out" })To run a cart round a closed track
Section titled “To run a cart round a closed track”local cart = model("rocket")local track = { vector.create(8, 0, 0), vector.create(8, 0, 8), vector.create(0, 0, 8) }cart:move_along(track, 12, { straight = true, face = true, loop = true })rotate_to
Section titled “rotate_to”e:rotate_to(angles, seconds, { timing?, loop? }?)Turns a model or a part to angles, over some seconds.
Each angle turns the way it says, so a yaw from 0 to 2π turns once round, up to a thousand turns either way; an
angle left out is kept. Yaw turns left, pitch lifts the front, roll tips it to its right. The seconds are above 0
and up to an hour, rounded to whole ticks, one at least; set_angles turns it at once. on("rotate_finished", fn)
hears it arrive.
| Name | Type | What it is |
|---|---|---|
angles | Angles | Needed. |
seconds | number | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
timing | Timing? | How it eases, each lap if it loops; linear without. |
loop | booleandefault false | Turns on for good, by as much again each lap, as a fan. |
- Fails
- a value of the wrong type
- an option it does not have
To swing a door open
Section titled “To swing a door open”local door = model("rocket")door:rotate_to({ yaw = math.pi / 2 }, 1, { timing = "ease-out" })rotate_by
Section titled “rotate_by”e:rotate_by(angles, seconds, { timing?, loop? }?)Turns a model or a part by angles from how it is turned, over some seconds.
It takes its seconds and options as rotate_to does. A turn that would go past a thousand turns is dropped.
| Name | Type | What it is |
|---|---|---|
angles | Angles | Needed. |
seconds | number | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
timing | Timing? | How it eases, each lap if it loops; linear without. |
loop | booleandefault false | Turns on for good, by as much again each lap, as a fan. |
- Fails
- a value of the wrong type
- an option it does not have
To spin a fan for good
Section titled “To spin a fan for good”local fan = model("rocket")fan:rotate_by({ yaw = 2 * math.pi }, 3, { loop = true })x:stop(what?)Stops a model's or a part's move and turn where it is, or an NPC's walk.
"move" stops its move alone and "rotate" its turn; an NPC stops with neither. A stopped move or turn is never
heard as finished.
| Name | Type | What it is |
|---|---|---|
what | ("move" | "rotate")? | May be left out. |
- Fails
- a value of the wrong type
To stop a lift between floors
Section titled “To stop a lift between floors”local lift = model("rocket")lift:move_to(vector.create(0, 6, 0), 4)lift:rotate_by({ yaw = math.pi }, 4)wait(1)lift:stop("move")To stop a zombie in its tracks
Section titled “To stop a zombie in its tracks”local zombie = spawn({ at = vector.create(0, 0, 10) })zombie:chase(p)zombie:stop()turn_with
Section titled “turn_with”e:turn_with(player?, { rate? }?)Makes a model, a shape or a part face where a player looks, without moving it; nil stops it where it faces.
It turns level, in the world, whatever it rides, from how it faces as the call is made, as a mounted gun’s barrel
follows its gunner. It costs no network a tick: each player’s game turns it toward the player as it draws them.
angles reads it as of the last tick, and what collides with it, and limit_view about it, take it as it faced
when it began to turn. A turn, a stop of its turn, or its player leaving ends it, facing where it was.
| Name | Type | What it is |
|---|---|---|
player | (Player | number)? | May be left out. |
options | table? | A table of the options below. It, and each option, may be left out. |
rate | number? | The fastest it turns, in radians a second, above 0; at once without. |
- Fails
- a value of the wrong type
- an option it does not have
To mount a gun that follows its gunner
Section titled “To mount a gun that follows its gunner”local gun = model("ak47", { at = vector.create(0, 1, 2) })gun:turn_with(p, { rate = math.pi })To let the gun go
Section titled “To let the gun go”local gun = model("ak47", { at = vector.create(0, 1, 2) })gun:turn_with(p)gun:turn_with(nil)attach
Section titled “attach”e:attach(to, { bone?, offset? }?)Makes a model, a part, a looping sound or a looping effect ride a player, an NPC, a model or a part.
It rides turned as what it rides is turned, and its moves and turns after are in that space: a flag on a
carrier’s back, a turret on a lift, a charge stuck to a zombie. A sound or an effect rides by its offset alone. An
attach that would make a ring, or put anything more than 16 deep, is dropped; one to a player who left does
nothing. detach, or what it rides going, lets it go where it is.
| Name | Type | What it is |
|---|---|---|
to | Player | Entity | number | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
bone | string? | The joint of a body it rides, by name, as "DEF-spine.003"; the body's origin without. |
offset | vector? | How far from what it rides, or from the joint, in that one's space. |
- Fails
- a value of the wrong type
- an option it does not have
To put a turret on a lift
Section titled “To put a turret on a lift”local lift = model("rocket")local turret = model("ak47")turret:attach(lift, { offset = vector.create(0, 1, 0) })lift:move_by(vector.create(0, 5, 0), 3)To strap a charge to a player’s back
Section titled “To strap a charge to a player’s back”local charge = model("grenade")charge:attach(p, { bone = "DEF-spine.003", offset = vector.create(0, 0, -0.15) })detach
Section titled “detach”e:detach()Lets a model, a part, a looping sound or a looping effect go of what it rides, where it is.
To drop a carried flag
Section titled “To drop a carried flag”local flag = model("rocket")flag:attach(p, { offset = vector.create(0, 1.2, -0.3) })flag:detach()shape(form, size, options?) → EntityMakes a shape of a form filling a size, or the convex hull of points, and gives its entity.
A form is "box", "wedge" (a ramp rising from nothing at its front, -z, to its height at its back),
"cylinder", "sphere" or "capsule", filling size, standing on its origin, upright and round across x and z.
"hull" takes a list of up to 64 points about its origin in place of a size, spanning a volume. A size is above 0
each way. Its kind is shape: it moves, rides, is solid and goes as a model does.
| Name | Type | What it is |
|---|---|---|
form | "box" | "wedge" | "cylinder" | "sphere" | "capsule" | "hull" | Needed. |
size | vector | {vector} | Needed. |
options | table? | A table of the options below. It, and each option, may be left out. |
material | material? | The .material file it is drawn with; not drawn without, a clip. |
solid | Solid? | What it stops, as its solid says: true, false or a mask; everything without. |
at | vector? | Where it stands; the map's origin without. |
angles | Angles? | How it is turned; unturned without. |
- Returns
Entity- Fails
- a value of the wrong type
- an option it does not have
To build a wooden ramp
Section titled “To build a wooden ramp”local ramp = shape("wedge", vector.create(2, 1, 4), { material = "wood", at = vector.create(0, 0, 6) })To block a doorway to players alone, unseen
Section titled “To block a doorway to players alone, unseen”shape("box", vector.create(2, 3, 0.2), { solid = { players = true }, at = vector.create(0, 0, 3) })To make a rock of a few points
Section titled “To make a rock of a few points”local points = { vector.create(0, 0, 0), vector.create(1, 0, 0), vector.create(0, 0, 1), vector.create(0.3, 0.8, 0.3),}shape("hull", points, { material = "wood" })