Skip to content

Zone creator (tm.creator) ​

Client side only. An in-world editor for zones and spawn points. The player flies or walks around the map, shapes a polygon, box or sphere zone (or captures positions), and the result comes back as plain data. It is a developer and staff tool: the resource that uses it decides who may open it and what to do with the result.

lua
RegisterCommand('makezone', function()
    -- a command handler is a thread, so the call may wait for the player
    -- check who may use it first, for example with a server callback that calls tm.fw.isAdmin
    local zone = tm.creator.startZone('box', 'garage', 'Garage')
    if zone[1] == 'cancelled' then return end
    print(json.encode({ coords = zone.coords, size = zone.size, rotation = zone.rotation }))
end, false)

There is no chat command in tm_lib. A resource starts the editor with a plain function call from a thread (a RegisterCommand handler, a menu callback, a CreateThread). The call yields until the player saves or cancels.

Functions ​

CallReturnsDescription
tm.creator.startZone(type, name, label, data?, opts?)tableOpens the zone editor. type is 'poly', 'box' or 'sphere'. Returns the zone, or { 'cancelled' } when the player cancels or an editor is already open.
tm.creator.closeZone(cancel?)tableCloses the editor from code. Returns the zone when saving, { 'cancelled' } when cancelling or when no zone editor is open. The thread waiting in startZone gets { 'cancelled' } either way.
tm.creator.getCoords(helpText?, opts?)vector4?One position with its heading. [G] captures, [Esc] cancels (returns nil). helpText is what [G] does, shown in the prompt.
tm.creator.getMultiCoords(opts?)vector4[]?Several positions in order. [G] adds one, [F] finishes, [Esc] cancels (returns nil).
tm.creator.startBlip(oldInput?)table?A dialog for a blip's sprite, color and scale. Returns { sprite, color, scale, coords } (the player's position), or nil when cancelled. oldInput is { sprite, color, scale } to start from.
tm.creator.isActive()booleanWhether an editor of this resource is open.

Only one editor can be open per resource at a time.

Zone results ​

lua
-- box
{ type = 'box', name, coords = vector3, size = vector3(width, length, height), rotation = heading,
  heading, width, length, height }
-- sphere
{ type = 'sphere', name, coords = vector3, radius, height = radius }
-- polygon: points are vector3 at the base height, thickness is the height of the prism
{ type = 'poly', name, points = vector3[], thickness, height = thickness }
-- cancelled
{ 'cancelled' }

A polygon needs at least 3 points to save. Pressing Enter with fewer shows a notice and keeps the editor open.

Starting from an existing zone ​

data opens the editor on a shape that already exists: coords, rotation, size (or width, length, height), radius for a sphere, thickness and points for a polygon. The list you pass is copied, so it is not modified.

Options ​

lua
tm.creator.startZone('box', 'door', 'Door', nil, {
    mode = 'resize',          -- 'gizmo' (default), 'resize' or 'keyboard'
    snap = 0.1,               -- resize handle step in meters, default 0.05
    fineSnap = 0.02,          -- the step while Ctrl is held, default a fifth of snap
    freecam = true,           -- false removes V and the Fly mode
    camera = { maxRadius = 200.0, stream = true }, -- passed to tm.freecam.createEditor
    help = { '[B] Bonus action' },                 -- extra lines for the help panel
    onFrame = function(box) ... end,               -- per frame hook for a box
})

onFrame(box) runs every frame for a box. box holds x, y, z, width, length, height, heading, mode and is reused, so keep nothing from it. Draw a preview in it, and return { x, y, z, heading } to move or turn the box (for a key of your own, listed in help). A hook that errors is dropped for the rest of the session and the error is logged.

getCoords and getMultiCoords accept { freecam = false } and { camera = {...} }.

Editing a box or sphere ​

Press [1] to [4] to pick a mode, or [G] to cycle them.

ModeHow it works
GizmoA hidden stand-in prop is dragged with tm.gizmo (move, rotate for boxes, scale). Needs tm.gizmo. If the gizmo is missing or refuses to start, the editor tells the player and uses Resize.
ResizeSix handles sit on the faces of the zone. Drag one with the mouse to resize along that axis (snapped to snap meters, Ctrl for fineSnap, Shift to grow both sides). Hold RMB to look around.
Keyboard[W/S] and [A/D] move in y and x, [R/F] up and down, [Q/E] turn a box. [Scroll] picks the step size (0.05 m up to 100 m, and 0.25 to 180 degrees for turning). Shift, Ctrl and Alt with scroll change the height (radius), width and length.
FlyThe freecam: mouse look, [WASD] to move, [Shift] to boost, [Scroll] for speed.

Polygons use Place (the cursor follows where the camera looks, [Space] adds a point, [Z] removes the last, [H] moves the last to the cursor or to you) and Fly. [R/F] raises and lowers the base, Shift+Scroll sets the height. [G] (polygon) or [Z] (box, sphere) cycles the display style: basic, walls, axes, both. The style is remembered between sessions.

[V] toggles the freecam in any mode. [Enter] saves, [Esc] or [Backspace] cancels.

What it takes and gives back ​

  • While open the editor holds a tm.inputlock (so key mappings such as an inventory do not fire from the editor's keys) and sets LocalPlayer.state.tmCreatorActive (not replicated).
  • The help panel is a tm.showTextUI prompt with the id creator-zone-builder (creator-single-coords and creator-multi-coords for the collectors). All its text comes from the creator.* locale keys.
  • Everything is released when the player saves or cancels, when the importing resource stops, and when the character unloads (tm_lib:fw:playerUnloaded): the camera, the stand-in prop, the cursor, the text prompt, the input lock and the gizmo.
  • Nothing runs per frame while the editor is closed.

Requirements ​

tm.freecam and tm.inputlock (both in tm_lib), tm.gizmo for the Gizmo mode only.