Triggerzone isn't fully released yet. Consider this a pre-release Beta. Do not use in production.
Triggerzone is not intended for end user use. It is a development tool and a development library, intended to supply a useful function to FiveM script developers.
It does not function without FiveM. This is intentional. It does nothing alone. This is also intentional.
The short version is that triggerzone triggers an event when you leave or exit a "zone", and provides an editor interface for defining such zones.
There is a tutorial that walks you through creating your first zone. It is done by placing verticies on the ground by clicking, and is, hopefully, very intuitive.
Zones are stored in .tzf files. Trigger Zone File. These are messagepack encoded Lua tables.
Usually, you will load these from the code of your own resource, more on that in the exports section, but you can manually load them using a command. Of course, this can be done in your server.cfg, or similar, so it doesn't have to be a problem.
It is recommended to load your zones from your code.
Zones are stored with a setting called events
. If this setting is true
, entering or exiting that zone will trigger an event, and your resource can act on that event as it sees fit.
You can also manually check if a point is inside a zone. For example, you can specify your own command, and have it check if you're inside the special zone before letting you use it. You can have an NPC act differently when talked to inside or outside a zone. That sort of thing.
Checking zones is a lot of math. A lot. If your zone has events
activated, the distance to that zone has to be checked all the time, causing it to slow down a little. Performance is being worked on, but there is no real way to avoid this distance check. For optimal performance, only enable events
if you really need it.
There is also a draw
setting, that will draw a visual representation of the zone even when it's not being edited. This might be useful for testing, but complex zones are drawn using a huge number of native calls. This is bad, and should be avoided if you don't absolutely need it. The graphics themselves are trivially light, but the CPU load of the native calls is substantial.
There is just one command, /triggerzone
, but it has several verbs.
Verb | Arguments | Function |
---|---|---|
cancel | Cancel the current edit, discarding all changes since last save. | |
check | name resource | Checks if the named zone file is valid, but does not load it. |
edit | name | Puts the zone of the given name into edit mode. |
help | Gives you this lovely message! | |
list | Display a list of loaded trigger zones. | |
load | name resource | Loads the named zone from disk, even if it's already loaded. |
new | name | Creates a new, blank zone for editing, with that initial name. |
save | Saves the zone being edited. | |
unload | name | Unloads the specified zone from all clients. |
/triggerzone cancel
Cancel editing, discarding and discard any changes made to the zone being edited. This will remove the zone from your local client, and prompt the server to re-send it as it is saved on the server.
/triggerzone check whatever.tzf
This will load the file whatever.tzf
from the zones directory of the triggerzone resource. Useful for verifying that the zone file in question is valid. It will either tell you it's broken, or it will tell you what is stored in it. If it gives you a list of things stored in the file, that means it checks out.
If a third argument is specified, that lets you specify what resource to load from when checking. If omitted, it will presume triggerzone
's own zones/ directory.
/triggerzone edit whatever
This presumes the zone named whatever
is already loaded, and will start the editor interface to fiddle with that zone.
This works exactly like in the tutorial, except it will have points already in it.
/triggerzone help
Shows you the table of verbs and what they do, much like the table above.
/triggerzone list
Shows a list of the currently loaded zones, and some metadata about it.
Column | Meaning |
---|---|
Name | The filename, minus the .tzf extention |
Label | The label shown at the centroid when editing |
Verticies | The number of points that make out the outline of the zone |
Events | "Yes" or "No" if this zone emits events |
Drawn | "Yes" or "No" if this zone is drawn outside the editor |
Origin | The name of the resource this resource was loaded from |
/triggerzone load my_awesome_zone.tzf
Load the specified .tzf file, and activate it as a zone. If it is Drawn or emits Events, it will start doing so immediately, for all users.
If a third argument is specified, that lets you specify what resource to load from. If omitted, it will presume triggerzone
's own zones/ directory.
/triggerzone new aw_yiss
This will create a brand new zone named aw_yiss
, and open it for editing.
See the tutorial for details.
When naming zones, keep in mind that they can't have special symbols, or spaces, in their names, as the name ends up being the filename. Also, keep in mind that you might conflict with other resoruces, so rather than using a generic name, use one specific to your resource. For example, rather than parking
, use falcon_casino_parking
, or whatever matches your resource.
/triggerzone save
Saves the zone currently being edited, using it's name as a filename. Will store it back in the resource it was loaded from, if possible.
This will discard the local copy of the zone, and the server will respond with the saved copy to reload it. This will be propagated to all clients.
/triggerzone unload loathsome_square
Unloads a zone by the name specified, if it is loaded. This takes immediate effect on all clients.
Function | Control action | Default bind |
---|---|---|
Speed up camera | 38 INPUT_PICKUP | E |
Slow down camera | 44 INPUT_COVER | Q |
Boost camera speed | 21 INPUT_SPRINT | Left Shift |
Interact with vertex | 24 INPUT_ATTACK | Left mouse button |
Delete vertex | 25 INPUT_AIM | Right mouse button |
Move camera forward | 32 INPUT_MOVE_UP_ONLY | W |
Move camera backward | 33 INPUT_MOVE_DOWN_ONLY | S |
Move camera left | 34 INPUT_MOVE_LEFT_ONLY | D |
Move camera right | 35 INPUT_MOVE_RIGHT_ONLY | A |
Modifier | 36 INPUT_DUCK | Left Ctrl |
Decrease value | 16 INPUT_SELECT_NEXT_WEAPON | Scrollwheel down |
Increase value | 17 INPUT_SELECT_PREV_WEAPON | Scrollwheel up |
Toggle UI focus | 22 INPUT_JUMP | Spacebar |
Left-clicking while the cursor is yellow inserts a vertex at the given position. Clicking and holding will insert the vertex, and then let you drag it into place.
Similarly, when the cursor is red, you can click and drag the closest vertex to a new position.
The "Decrese value" and "Increase value" controls do different things depending on if "Modifier" is pressed.
Without the modifier, it will increase and decrease the Height property of the zone. With the modifier, it will increase and decrease the Altitude property of the zone.
Large parts of the editor UI was designed and implemented by MonBjo on GitHub. Thank you very much for the assist!
To interact with the input fields and buttons, press the space bar.
If you are brand new to the triggerzone, you might want to look at the tutorial as well.
- Zone controls
- Points list
- Area visualizer
- Cursor-and-beam
- Centroid
- Glance data
- Color controls
- Minimap
This area holds a number of controls that define how your zone behaves.
This is the label drawn at the centroid, and is sent with the zone name in events. This is what you intend for the zone to be called, or referred to as, when interacting with a user.
The floor of your zone is this altitude above global zero. This is set when you place the first point, but you can edit it freely. This can also be adjusted with the hotkey Ctrl+Scrollwheel, assuming you have the default GTAV keybinds.
The distance, in meters, from the Altitude to the ceiling of the zone. For reference, the default player ped is 2.0 units tall, but it's location is 1.0 units above where it is standing. This means a zone that is flush with the floor will have to be just slightly taller than 1.0 for it to register players standing in it, but to not register them if they jump.
Does this zone trigger an event when entering or leaving it?
Note: This has performance implications.
Is this zone drawn, even when it is not being edited?
Note: This has serious performance implications.
Discard all changes since the last save, and stop editing this zone.
Save changes made to this zone, and send them to the server. It is then distributed to all clients immediately!
This list shows the current points that make up your zone. Note that the points are not in any way preserved, and their number/index will hop around as you edit the zones. This is due to the polygon cleanup that happens behind the scenes.
If you click one of the points, you can then use the Action buttons below. View will move the camera to put the point into view. Delete will remove the point permanently.
You can hide the points list by clicking the little arrow.
The area visualizer shows the walls, floor, and ceiling of your zone. When your player ped is inside the zone, as shown in the screenshot, it will use the Active zone color. When you are outside, it will use the Inactive zones color.
Note that while the lines and walls are drawn in 3D space, the vertex labels are drawn on top, and will show through the world. If you move your camera too far from the zone, it will no longer draw the labels.
The spherical cursor shows you your intraction range. When it turns red, you will interact with the closest vertex. When it is yellow, you are in insert mode.
When it is interact mode, the glance data will update to show which vertex you will interact with. When it is in insert mode, the glance data will update to show what index the new vertex will have.
The beam shows where the zone corner will be, and obeys Altitude and Height, even if the cursor is above or below the zone.
This shows the current label for the zone, and where the geometric centroid of the zone is. This does not have to be inside the zone, it's just a marker. The centroid beam shows the height of the zone, just like the cursor beam.
Note that this will move around as you edit the zone.
This little blob of text will show you some data about the state of the editor.
- Filename
- Number of verticies (misspelled in the screenshot, since fixed)
- The current camera speed (Use Q and E to adjust)
- Interaction hint
The interaction hint in the screenshot shows that a new vertex will be inserted at index 12. When the cursor is close enough to an existing vertex, it will turn red, and the interaction hint will tell you what vertex you will interact with.
These are the colors of active and inactive zones. A zone is active when your current player ped is inside it.
Note that the walls and lines have separate alpha values. The vertex labels follow the lines alpha value.
The minimap will still show everything it would normally show, but it will now focus on where your cursor is. The little ring shown is not to any sort of scale, but it will tell you exactly where the cursor is, not where the camera is.
Zoom is adjusted according to the distance between the camera and the cursor, meaning you get a very zoomed in view if you are editing up close.
These are the functions you can use from your own resource, and what they do.
Name | Where | Arguments | Intended use |
---|---|---|---|
GetLoadedZones | Server | Get a list of loaded zones, and their associated resource of origin. | |
GetZoneFilenames | Server | resource | Get a list of .tzf files in the zones/ directory of the given resource. |
GetZoneLabel | Shared | zoneName | Get the label for the zone of the given name. |
GetZonesAtPoint | Shared | point | Get a list of zones that overlap with the given vector3 point. |
IsPointInsideZone | Shared | point, zoneName | Check if the given vector3 point is inside the zone with the given name. |
Load | Server | filename, resource | Load zone data, and set the zone. |
LoadZoneFile | Server | filename, resource | Load zone data, but don't actually set the zone. |
Set | Server | name, zoneData, resource | Set a zone from raw data. |
Store | Server | name, zoneData, resource | Store the zone data to a file who's name is derived from the given name. |
"Shared" means it's both on the client and the server. Server obviously means it's only available from the server.
When an arguemnt is "resource", it can be omitted. It will assume you mean the resource you are calling the function from.
local zoneTable = exports.triggerzone:GetLoadedZones()
for zoneName, originResource in pairs(zoneTable) do
print(zoneName, originResource)
end
Takes no arguments. Returns a table of loaded zones, for example
{
demonstration = "someResource",
falcon_casino_floor_1 = "falcon-casino",
tutorial = "triggerzone_test",
}
The key is the zone name, the value is the resource of origin.
local filenames = exports.triggerzone:GetZoneFilenames()
for _, filename in ipairs(filenames) do
exports.triggerzone:Load(filename)
end
Optionally takes a resource name as an argument. By default, it will use the resource it is called from.
Returns a table of filenames for any .tzf files in that resource, in no particular order, for example
{ "pdm_behind_counter.tzf", "pdm_office_1.tzf", "pdm_office_2.tzf", "pdm_mechanic_bay.tzf" }
Useful for autoloading zone files without having to maintain a list.
function DoThingInZone(player, zone)
local coords = GetEntityCoords(GetPlayerPed(player))
if exports.triggerzone:IsPointInsideZone(coords, zone) then
DoTheThing(player, zone)
else
print( ("You must be in %s to do the thing!"):format(exports.triggerzone:GetZoneLabel(zone)) )
end
end
Returns a string. If the zone is not loaded, it returns an empty string, and if there is no label set, it returns the zone name as given.
function WhereIsNPC(npc)
local coords = GetEntityCoords(npc)
local zones = exports.triggerzone:GetZonesAtPoint(coords)
for zoneName, label in pairs(zones) do
print( ("NPC is in %s!"):format(label) )
Frobnicate(npc, zoneName)
end
end
Iterates through all loaded zones, and checks if the given point is in them. Returns a table of zones, with their label, that the point is in. For example,
{
del_perro_pier = "The Pier",
pier_hotdog_stand_3 = "Hotdog stand",
}
function OpenBossMenu(player)
local coords = GetEntityCoords(GetPlayerPed(player))
if exports.triggerzone:IsPointInsideZone(coords, "laywer_boss_office") then
TriggerClientEvent("laywerjob:showBossMenu", player)
else
TriggerClientEvent("laywerjob:showMessage", player, "You must be in your office to do that.")
end
end
Returns a boolean. If the zone is not loaded, it will always return false, as no point can ever be inside an unloaded zone.
local filenames = exports.triggerzone:GetZoneFilenames()
for _, filename in ipairs(filenames) do
exports.triggerzone:Load(filename)
end
Loads a zone file, and sends it to all clients for immediate use.
Returns true if the load was successful, false otherwise. Will output success/failure to console as well.
function LoadSporkZone()
local success, name, data = exports.triggerzone:LoadZoneFile('spork.tzf')
if not success then
print( ("Failed to load spork zone file, because %s"):format(name) )
return
end
print( ("Oh no, we loaded %s, and did nothing with it!"):format(data.label or name) )
end
Loads the given zone file into memory, but does not send to clients or in any way make it available anywhere else.
Returns three values.
- A boolean that tells you if the load was successful.
- A string that holds the name of the zone (as derived from the filename), or the reason it failed to load.
- A table containing the zone data, or just an empty table if the loading failed.
function NoFileNeededHAHAHAHA()
local zoneData = {
label = "Why would you do this?!"
altitude = 36.4,
height = 2.0,
points = {
vector2(0.0, 0.0)
vector2(1.0, 0.0)
vector2(1.0, 1.0)
vector2(0.0, 1.0)
}
}
exports.triggerzone:Set("some_zone", zoneData)
end
Sets a zone with the given zoneData, even if that data makes very little sense.
Always returns true, even if you are being silly, and the whole thing catches fire.
Please, do not do this. Use the editor instead. It is provided only for the warm, fuzzy feeling of completeness.
function SabotageZoneFilesDirectory()
local zoneData = {
label = "Why would you do this?!"
}
local saved, filename, size = exports.triggerzone:Store("some_zone", zoneData)
if saved then
if size > 9000 then
print("OH WOW, where did all that data come from?!")
end
else
print("OH NO! TIME TO PANIC!!!")
end
end
Writes out a zone file with the given zone data.
Returns a boolean denoting if the save was successful, the name of the file it was saved as, and the length of the encoded messagepack string.
Generally speaking, you should never need to do this yourself. Please don't use this. Use the editor instead.
There is a config.lua
, and there should probably be some documentation related to it at some point.
For now, though, just don't mess with it. You really don't need to.
I'd call it a FAQ, but since there has been no release, there can't possibly be any frequently asked questions, right?
Having a uniform structure across all resources using this one is a good idea. Just trust me on this.
Because encoding and decoding vector2 typed data to/from other formats is a pain, and messagepack is very well battle tested.
Because it's a Trigger Zone File. I wanted an easily recognizable file extention, and, as far as I could tell, this one wasn't "taken".
Not that I know of, but Agent Michael Ford is a pretty cool guy, and doesn't afraid of anything.