Developer

3 min readUpdated 3 weeks ago

Overview

Integration runs through three server exports, replicated GlobalState keys any resource can read, and handler hooks in the open/ folder (shipped unescrowed).

Server Exports

  • GetCampStatus(campId): Returns the runtime status string, or nil for an unknown camp: 'idle' | 'active' | 'combat' | 'cleared' | 'cooldown' | 'depleted'
  • IsCampCleared(campId): Returns true while the camp status is 'cleared' or 'cooldown'. Spawn-once camps report 'depleted' instead; check GetCampStatus for those
  • GetCampAliveCount(campId): Number of hostiles still alive
lua
1local status = exports.ml_camps:GetCampStatus('bandit_grapeseed')
2local cleared = exports.ml_camps:IsCampCleared('bandit_grapeseed')
3local alive = exports.ml_camps:GetCampAliveCount('bandit_grapeseed')

GlobalState

Every camp replicates a live snapshot under mlcamp_<campId>, readable on both sides:

lua
1local snap = GlobalState['mlcamp_bandit_grapeseed']
2-- {
3--     status = 'combat',   -- idle | active | combat | cleared | cooldown | depleted
4--     alive = 3,           -- hostiles alive
5--     total = 6,           -- hostiles spawned
6--     nextRespawn = 0,     -- unix timestamp when the cooldown ends, 0 otherwise
7-- }

The active raid HUD settings are replicated under mlcampcfg as { enabled, position, resetVisibilityDefault }. Camp NPCs carry mlCampPed = true and mlCampId on their entity state bag, so other scripts can recognize them.

Server Handlers

Located in open/handlers_server.lua.

open/handlers_server.lua
1Handlers.HasCreatorPermission = function(source)
2    -- return true to allow the admin panel
3end
4
5Handlers.HasLivePermission = function(source)
6    -- return true to allow force clear / force reset / teleport
7end
8
9Handlers.OnCampActivated = function(campId, campData)
10end
11
12Handlers.OnCampCleared = function(campId, campData, raiders)
13    -- raiders: array of credited server ids, entry order
14end
15
16Handlers.ModifyLoot = function(campId, items, money)
17    return items, money
18end
  • HasCreatorPermission / HasLivePermission: Permission gates for the two staff tiers. The default implementation checks Config.Permissions through ml_bridge; replace it to plug in any permission system
  • OnCampActivated: Fires when a camp spawns its NPCs
  • OnCampCleared: Fires when the last hostile dies. raiders holds the credited player ids
  • ModifyLoot: Called before every loot payout (direct and per crate). Return the modified items array ({ name, count, metadata? }) and money table ({ account, amount } or nil)

Client Handlers

Located in open/handlers_client.lua.

open/handlers_client.lua
1Handlers.CanShowCampBlip = function(campId, campData)
2    return true -- return false to hide this camp's blip for the local player
3end
4
5Handlers.OnCampEntered = function(campId, campData)
6end
  • CanShowCampBlip: Per-player blip filter, evaluated when camps load
  • OnCampEntered: Fires when the local player crosses into a camp radius

Examples

open/handlers_server.lua
1Handlers.OnCampCleared = function(campId, campData, raiders)
2    for _, src in ipairs(raiders) do
3        Bridge.GiveItem(src, 'water', 1)
4    end
5end
open/handlers_server.lua
1Handlers.ModifyLoot = function(campId, items, money)
2    local camp = Config.Camps[campId]
3    if camp and camp.type == 'military' and money then
4        money.amount = math.floor(money.amount * 1.5)
5    end
6    return items, money
7end
your_script/server.lua
1RegisterNetEvent('myscript:enterBunker', function()
2    local src = source
3    if not exports.ml_camps:IsCampCleared('military_zancudo') then
4        Bridge.NotifyPlayer(src, 'error', 'Clear the outpost first.')
5        return
6    end
7    -- open the bunker
8end)
your_script/client.lua
1AddStateBagChangeHandler(nil, 'global', function(_, key, value)
2    if type(key) ~= 'string' or key:sub(1, 7) ~= 'mlcamp_' then return end
3    local campId = key:sub(8)
4    if value and value.status == 'cleared' then
5        print(('camp %s cleared near %s'):format(campId, tostring(cache.ped)))
6    end
7end)
your_script/client.lua
1local function isCampNpc(ped)
2    return Entity(ped).state.mlCampPed == true
3end
4
5-- e.g. skip camp NPCs in a ped-scanning loop
6for _, ped in ipairs(GetGamePool('CPed')) do
7    if ped ~= cache.ped and not isCampNpc(ped) then
8        -- safe to process
9    end
10end