Reference
ss.props
Props are the movable objects a level is dressed with. The single bodies are the bollards, barrels, bins, cardboard boxes, signs and fences a car can knock over; the compound bodies are the multi-part breakables, such as road-work barriers, benches, landing lights, security barriers and shelves, which have one main body and parts that break off it. A prop is a live object in the same style as a vehicle: reading a field reads the game at that moment, and assigning a field writes it back. Props only exist within a race.
A prop starts a race static: part of the level's single static collision hull, with no physics
body of its own. The first thing that touches it, a car or a script, cuts it out of the hull and spawns a
dynamic body in its place. From then on the prop is live and falls, slides and settles like any
other body.
Static members#
These elements are static to the class itself and can be accessed like so:
local nearby = ss.props.near(ss.vehicle.position, 30)Static functions#
| Function | Return Values | Description |
|---|---|---|
ss.props[integer index] |
prop prop | Returns the prop at the given flat index: 0-based, batch by batch, with the single bodies first and the compound bodies after them. Returns nil if there is no such prop. |
ss.props.count |
integer count | The number of props in the loaded level. 0 outside a race. |
ss.props.all() |
table of prop props | Returns a table of references to all props in the loaded level. |
ss.props.each() |
function iterator | Returns an iterator over all props in the loaded level, for use in a for loop. |
ss.props.find(string pattern) |
table of prop props | Returns a table of references to all props whose name matches the given Lua pattern, case-insensitively. The search walks the batches (one per prop type) rather than the props, so it is cheap. |
ss.props.near(
vector-like point,
number radius
) |
table of prop props | Returns a table of references to all props within the given radius, in metres, of the given point, nearest first. Distances are compared on the C side and nothing is converted until a prop is read. |
Static fields#
| Type | Field | Read-only? | Description |
|---|---|---|---|
| boolean | ss.props.release_hulls |
No | Whether a never-touched prop loses its static hull when a script moves it. Defaults to true. See Moving a prop. |
Instance members#
Instance members must be accessed through a reference to a specific prop object.
local bollard = ss.props.find("bollard")[1]
bollard.position.y = bollard.position.y + 2
bollard:impulse(ss.vehicle.forward * bollard.mass * 10)Instance Methods#
| Function | Return Values | Description |
|---|---|---|
impulse(vec3 impulse) |
nil | Applies an impulse, in newton-seconds, at the prop's centre of mass. The velocity changes by impulse / mass. Wakes a static prop first. |
impulse(
vec3 impulse,
vec3 point
) |
nil | Applies an impulse, in newton-seconds, at a point in world coordinates, which also spins the prop. Wakes a static prop first. |
angular_impulse(vec3 impulse) |
nil | Applies an angular impulse, in newton-metre-seconds, to the prop's spin alone. Wakes a static prop first. |
mem(
integer offset,
string type
) |
any value | Returns a value of the prop's instance record at a specific memory address-offset. See ss.mem for the types. |
mem(
integer offset,
string type,
any value
) |
nil | Sets a value of the prop's instance record at a specific memory address-offset. |
memdump(
integer lowerBound,
integer upperBound
) |
nil | Logs the current state of the prop's memory addresses in the given range, one dword per line with its float reading, for debugging. |
Instance Fields#
| Type | Field | Read-only? | Description |
|---|---|---|---|
| boolean | valid |
Yes | Validates that the prop object exists in the loaded level. A prop kept across the end of a race becomes invalid: its fields read nil and assigning them results in an error. |
| string | name |
Yes | The name of the prop's type, such as common_bollard_02 or cardboard_box_01. |
| string | kind |
Yes | single for a single body, compound for a multi-part breakable. |
| integer | parts |
Yes | The number of parts of a compound body. 1 for a single body. |
| integer | index |
Yes | The prop's flat index, as taken by ss.props[index]. |
| integer | batch |
Yes | The index of the prop's batch. There is one batch per prop type. |
| integer | slot |
Yes | The prop's slot within its batch. |
| string | state |
Yes | static (never touched, part of the level's static hull), live (a dynamic body in the world), released (a single body the game released) or broken (a compound body the game has taken apart). |
| boolean | live |
Yes | Whether the prop's state is live. |
| vector | position |
No | The body's position in metres. Setting this field moves the prop through the game's own machinery; see Moving a prop. |
| vector | rotation |
No | The body's rotation in degrees, as pitch, yaw and roll, as for a vehicle. See Coordinates and angles. |
| vector | velocity |
No | The body's linear velocity in metres per second, on the world axes. Reads as zero while the prop has no body. Setting this field wakes the prop; see Waking a prop. |
| vector | angular_velocity |
No | The body's angular velocity in degrees per second about the world axes. Reads as zero while the prop has no body. Setting this field wakes the prop. |
| number | mass |
Yes | The body's mass in kilograms. nil while the prop has no body; the prop receives the game's own mass when it wakes. |
| vector | forward |
Yes | The prop's forward axis in the world, as a unit vector. |
| vector | up |
Yes | The prop's up axis in the world, as a unit vector. |
| vector | right |
Yes | The prop's right axis in the world, as a unit vector. |
| number | key |
Yes | The game's packed key for the prop: 2 | batch << 6 | slot << 16. |
| number | hull_key |
Yes | The prop's shape key in the static hull, while it still has one; nil otherwise. The lookup table is built on first use, which takes a few milliseconds once per level. |
| integer | address |
Yes | The address of the prop's instance record, for ss.mem. |
Instance Memory Offsets#
These offsets can be manipulated by the instance's mem method. They are relative to address.
| Field | Fieldtype | Description |
|---|---|---|
0x0 |
pointer |
The live rigid body. 0 while the prop is static. |
0x10 |
hkTransform |
A 3×4 rotation and the position: the resting, or last applied, transform. Use fields position and rotation |
0x9c |
int |
The state: 0 static, 2 live, 3 released. Use field state |
0xa0 |
byte |
The handle's type, 0xf. |
0xa4 |
u32 |
The handle's packed key. Use field key |
| Field | Fieldtype | Description |
|---|---|---|
0x8 |
pointer |
The world matrix. |
0x6c |
byte |
The handle's type, 0x10. |
0x70 |
u32 |
The handle's key. Use field key |
0x74 |
pointer |
The main rigid body. 0 until spawned. |
0x7c |
int |
The state: 0 static, 1 or 2 live, 3 broken. Use field state |
Moving a prop#
Setting position or rotation moves the prop through the game's own machinery, and
what that does depends on the prop's state. A live prop receives the transform, zero velocity and
a wake-up, so it settles where it is put. A released prop receives its body back at the new
transform. A static prop is first cut out of the level's static hull and then spawned as a dynamic
body there, exactly as the game does when a car first hits it. Props spawned by a script are dynamic from then
on and stay live until the level ends.
The hull cut is controlled by ss.props.release_hulls. When it is false, a moved prop
keeps its invisible hull where it stood until something touches that spot; the game's contact listener then
removes the hull and finds the body already spawned. The difference is one phantom contact: a car parked on
such a spot sank straight to the road in testing.
A prop should be placed on, or just above, the ground. It falls and settles: a bollard dropped from 2 m tips over, and a box moved sideways at its own height remains where it is put.
A compound body reads as its main body, and a move places the main body where asked; the game's own placement offset is accounted for, so a write reads back as written. Parts that have already broken off are bodies of their own and do not move with it. The parts are not objects of the API yet, and neither are the model instances of the level's static scenery, which have no physics.
Waking a prop#
Setting velocity or angular_velocity, or applying an impulse, wakes the prop. A
static prop receives its body first, at its own transform, then the velocity and an activation.
A write keeps the components it does not set, so prop.velocity = ss.vec3(vx, prop.velocity.y, vz)
pushes a prop sideways while gravity continues to act on it.
-- Pulls the nearest props towards the car, twenty times a second.
ss.every(0.05, function()
local car = ss.vehicle
if not car then return end
for _, prop in ipairs(ss.props.near(car.position, 80)) do
local d = car.position - prop.position
if d:length() > 5 then prop.velocity = ss.vec3(d.x, prop.velocity.y, d.z):normalized() * 3 end
end
end)The full version of this, with the pull stopping short so the props gather in a ring, is the Magnet sample.
Cost#
The prop tables are read once per level, when the level appears, so reading a field is a few loads and an angle
conversion. Measured on the development VM in a race with 867 props: near over a 400 m radius takes
0.2 ms, a sweep over every prop reading state 0.5 ms, find("bollard") 0.1 ms, and a
compound-body move 0.1 ms.