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.
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
| Call | Returns | Description |
|---|---|---|
tm.creator.startZone(type, name, label, data?, opts?) | table | Opens 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?) | table | Closes 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() | boolean | Whether an editor of this resource is open. |
Only one editor can be open per resource at a time.
Zone results
-- 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
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.
| Mode | How it works |
|---|---|
| Gizmo | A 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. |
| Resize | Six 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. |
| Fly | The 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 setsLocalPlayer.state.tmCreatorActive(not replicated). - The help panel is a
tm.showTextUIprompt with the idcreator-zone-builder(creator-single-coordsandcreator-multi-coordsfor the collectors). All its text comes from thecreator.*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.