Custom Locations · origen_ilegalv2
Config.Locations.Types defines every location type that a gang leader can place on the map through the management panel (stashes, garages, clothing rooms, bank deposit points, management desks…). You can add your own location types by extending this table — no core edits required.
Each type controls:
- Its blip and 3D marker appearance.
- The permission a member needs to interact with it.
- Whether it's a default type (seeded automatically for new gangs).
Where to add custom types
Add new entries inside Config.Locations.Types in config/locations.lua:
Config.Locations = {
DrawDistance = 50.0,
InteractDistance = 1.5,
MarkerDrawDistance = 15.0,
Types = {
stash = { --[[ ... ]] },
garage = { --[[ ... ]] },
-- your custom type
armory = {
label = 'Open gang armory',
default = false,
requiredPermission = 'access_armory',
blip = {
enabled = false,
sprite = 110,
color = 1,
scale = 0.7,
},
marker = {
type = 22,
offset = { x = 0.0, y = 0.0, z = 1.0 },
scale = { x = 0.25, y = 0.25, z = 0.25 },
color = { r = 255, g = 0, b = 0, a = 255 },
bobUpAndDown = true,
faceCamera = true,
rotationOrder = 2,
rotate = false,
},
onInteract = function(gangData, locationId, locationData)
-- client-side logic here
TriggerServerEvent('my_resource:openArmory', gangData.id)
end,
},
},
}The key (armory in the example) is the locationType used internally. It is the value stored in the DB for each placed point.
Type fields
| Field | Type | Required | Description |
|---|---|---|---|
label | string | yes | Text shown in the interaction prompt. Can be a plain string or a locale key (e.g. locations.types.armory.label). |
default | boolean | yes | If true, the type is handled by the core (stash, garage, clothing, bank, management). Set false for custom types that define their own onInteract. |
requiredPermission | string | nil | yes | Rank permission required to interact with a placed point. Use nil to allow any member. |
blip | table | yes | Minimap blip config (see below). |
marker | table | yes | 3D world marker config (see below). |
onInteract | function | no | Required for custom types (default = false). Called client-side when a member interacts with the point. Receives (gangData, locationId, locationData). |
placement_preview | table | no | Only used by special types that need a world preview while placing (e.g. garage shows a vehicle to pick heading). |
blip
| Field | Type | Description |
|---|---|---|
enabled | boolean | Show the blip on the minimap. |
sprite | number | GTA blip sprite id. |
color | number | GTA blip color id. |
scale | number | Blip size. |
marker
| Field | Type | Description |
|---|---|---|
type | number | Marker type id — see FiveM marker reference. |
offset | { x, y, z } | Offset from the base coordinate. Legacy alias offsetZ (float) is still accepted. |
scale | { x, y, z } | Marker size per axis. |
color | { r, g, b, a } | RGBA 0–255. |
bobUpAndDown | boolean | Bob animation. |
faceCamera | boolean | Always face the camera. |
rotationOrder | number | Axis order for rotation (default 2). |
rotate | boolean | Spin on axis. |
How custom type interaction works
When default = false, the core calls the onInteract function defined directly in the type config. This function runs client-side and receives three arguments:
| Argument | Type | Description |
|---|---|---|
gangData | table | The player's gang data (id, name, members, ranks, etc.). |
locationId | string | The unique ID of the placed point (e.g. gang_ballas_armory_1). |
locationData | table | Raw location data including coords (x, y, z, h) and type. |
Permission validation (requiredPermission) is handled by the core before onInteract is called. If the member does not have the required permission, the interaction is blocked silently and onInteract never runs.
onInteract runs on the client. To run server-side logic (open an inventory, give items, etc.), trigger a server event from inside the function.
onInteract = function(gangData, locationId, locationData)
-- trigger your own server event from here
TriggerServerEvent('my_resource:openArmory', gangData.id)
end,RegisterNetEvent('my_resource:openArmory', function(gangId)
local src = source
-- your server-side logic here
exports.ox_inventory:openInventory(src, {
type = 'shop',
id = 'gang_armory_' .. gangId,
})
end)Place custom server handlers inside custom/server/ so that core updates do not overwrite your logic.
Permission system
requiredPermission maps directly to the rank permission flags defined in Config.Gang.DefaultRanks (config/gangs.lua). When a member tries to interact with a placed point, the core checks:
gang:hasPermission(identifier, location.requiredPermission)If it returns false, the interaction is silently blocked.
Adding your own permission flag
-
Define the flag in each default rank that should receive it:
luaconfig/gangs.luaDefaultRanks = { { id = 1, name = "Leader", permissions = { -- ... existing flags ... access_armory = true, }, }, { id = 3, name = "Member", permissions = { access_armory = false, }, }, }, -
Reference it from your location type:
luaarmory = { requiredPermission = 'access_armory', -- ... }, -
Placement gate (optional) — to restrict who can place the point (not just interact), the management panel reuses the standard
manage_locationspermission for all types. No extra config needed.
Every rank that should be able to see or use the new permission must have the flag explicitly defined. Missing flags default to false.
Members can also edit rank permissions at runtime through the gang panel (if they have manage_ranks). Any permission key declared in Config.Gang.DefaultRanks is automatically listed there.
Complete example — armory type
armory = {
label = 'locations.types.armory.label',
default = false,
requiredPermission = 'access_armory',
blip = {
enabled = true,
sprite = 110,
color = 1,
scale = 0.7,
},
marker = {
type = 22,
offset = { x = 0.0, y = 0.0, z = 1.0 },
scale = { x = 0.3, y = 0.3, z = 0.3 },
color = { r = 255, g = 50, b = 50, a = 200 },
bobUpAndDown = true,
faceCamera = true,
rotationOrder = 2,
rotate = false,
},
onInteract = function(gangData, locationId, locationData)
TriggerServerEvent('my_resource:openArmory', gangData.id)
end,
},-- Leader + Co-Leader:
access_armory = true,
-- Member + Recruit:
access_armory = false,RegisterNetEvent('my_resource:openArmory', function(gangId)
local src = source
exports.ox_inventory:openInventory(src, {
type = 'shop',
id = 'gang_armory_' .. gangId,
})
end)Restart the resource. The new Armory type appears in the location placement picker for any rank with manage_locations, and only members with access_armory can interact with the placed points.