Developer

4 min readUpdated 2 weeks ago

Overview

Other resources read and control zones through server exports and listen to the loot events. The loot flow itself is changed in open/handlers_server.lua and open/handlers_client.lua, which stay unencrypted.

Server Exports

lua
1local zone = exports['ml_lootzones']:GetZoneData(areaId)
2local zones = exports['ml_lootzones']:GetAllZones()
3local inside, zoneId = exports['ml_lootzones']:IsPlayerInZone(source, areaId)
4local refreshed = exports['ml_lootzones']:RefreshZoneLoot(areaId)
5local changed = exports['ml_lootzones']:SetZoneEnabled(areaId, enabled)
  • GetZoneData(areaId): data of an active zone, or nil
  • GetAllZones(): list of active zones as { id, name, category }
  • IsPlayerInZone(source, areaId?): true when the player stands inside that zone. Without areaId it checks every active zone and also returns the ID of the zone found
  • RefreshZoneLoot(areaId): rolls the zone loot again. Returns true on success
  • SetZoneEnabled(areaId, enabled): opens or closes a zone until the next restart or rotation. Returns true on success

RefreshZoneLoot and SetZoneEnabled only answer calls made from another resource.

Events

lua
1AddEventHandler('ml_lootzones:server:lootCollected', function(data)
2end)

Fires on the server after a prop is looted or a lootbox stash is emptied. The same payload reaches the looting player as the client event ml_lootzones:client:lootCollected.

lua
1data = {
2    source = 1,
3    zoneId = 'zone_id',
4    zoneName = 'Zone name',
5    category = 'category_id',
6    propModel = 'prop_tool_box_02',
7    propIndex = 3,
8    coords = vector3(0.0, 0.0, 0.0),
9    isLootBox = false,
10    timestamp = 1760000000,
11}

Server Handlers

Located in open/handlers_server.lua.

Loot Flow

open/handlers_server.lua
1Handlers.OnBeforeClaim = function(source, areaId, lootIndex, lootConfig, areaData)
2    return true
3end
4
5Handlers.OnAfterClaim = function(source, areaId, lootIndex, itemsGiven, areaData)
6end
7
8Handlers.GetLootMultiplier = function(source, areaId, areaData)
9    return 1.0
10end
11
12Handlers.ModifyLoot = function(source, areaId, fixedLoots, probabilityLoots, areaData)
13    return fixedLoots, probabilityLoots
14end
15
16Handlers.GetBonusLoot = function(source, areaId, areaData)
17    return nil
18end
  • OnBeforeClaim: runs before the items are given. Return false to stop the claim
  • OnAfterClaim: runs after the items are given. itemsGiven lists them
  • GetLootMultiplier: multiplies item amounts. 2.0 doubles them
  • ModifyLoot: edits the guaranteed and chance tables before the roll
  • GetBonusLoot: extra items on top, as { { name = 'item', amount = 1 } }

Access

open/handlers_server.lua
1Handlers.CanPlayerLootZone = function(source, areaId, areaData)
2    return true, nil
3end
4
5Handlers.CanPlayerClaimLoot = function(source, areaId, lootIndex, lootConfig)
6    return true, nil
7end
8
9Handlers.OnClaimBlocked = function(source, areaId, lootIndex, reason)
10end
  • CanPlayerLootZone: return false, 'reason' to close a whole zone to that player
  • CanPlayerClaimLoot: same check for a single prop
  • OnClaimBlocked: runs when a claim is refused by a cooldown, the distance check or one of the hooks above

Dispatch

open/handlers_server.lua
1Handlers.ShouldTriggerDispatch = function(source, areaId, areaData)
2    return nil
3end
4
5Handlers.ModifyDispatchData = function(source, areaId, dispatchData)
6    return dispatchData
7end
  • ShouldTriggerDispatch: true or false forces the alert, nil keeps the configured chance
  • ModifyDispatchData: edits the alert before it is sent

Tools

open/handlers_server.lua
1Handlers.ValidateTool = function(source, areaId, toolName, lootConfig)
2    return nil
3end
4
5Handlers.GetToolDurabilityDrain = function(source, areaId, toolName, baseDrain)
6    return nil
7end
  • ValidateTool: true accepts the tool, false refuses it, nil keeps the normal check
  • GetToolDurabilityDrain: return a number to replace the durability taken from the tool

Regrowth

open/handlers_server.lua
1Handlers.GetRegenOverride = function(areaId, lootIndex, baseRegen, areaData)
2    return nil
3end
4
5Handlers.OnLootRegenerated = function(areaId, lootIndex)
6end
  • GetRegenOverride: return seconds to replace the regrow time of a prop
  • OnLootRegenerated: runs when a looted prop comes back

Admin Changes

open/handlers_server.lua
1Handlers.OnZoneSaved = function(source, areaId, areaData, isNew)
2end
3
4Handlers.OnZoneDeleted = function(source, areaId)
5end
6
7Handlers.OnCategorySaved = function(source, categoryId, categoryData, isNew)
8end

Client Handlers

Located in open/handlers_client.lua.

Interaction

open/handlers_client.lua
1Handlers.CanInteractWithLoot = function(areaId, lootIndex, lootConfig, entity)
2    return true
3end
4
5Handlers.GetCustomTargetLabel = function(areaId, lootIndex, lootConfig)
6    return nil
7end
8
9Handlers.GetCustomTargetIcon = function(areaId, lootIndex, lootConfig)
10    return nil
11end
12
13Handlers.AddCustomTargetOptions = function(areaId, lootIndex, lootConfig, entity)
14    return {}
15end
  • CanInteractWithLoot: return false to hide the target option or prompt
  • GetCustomTargetLabel, GetCustomTargetIcon: return a string to replace the label or icon
  • AddCustomTargetOptions: extra target options on the same prop

Looting

open/handlers_client.lua
1Handlers.CanLootFromZone = function(areaId, lootIndex, lootConfig)
2    return true, nil
3end
4
5Handlers.OnBeforeLoot = function(areaId, lootIndex, lootConfig)
6end
7
8Handlers.OnAfterProgress = function(areaId, lootIndex, lootConfig)
9    return true
10end
11
12Handlers.OnLootClaimed = function(data)
13end
14
15Handlers.OnLootFailed = function(areaId, lootIndex, reason)
16end
  • OnAfterProgress: runs when the progress bar ends. Return false to cancel the claim
  • OnLootClaimed: receives the same payload as the lootCollected event

Stash and Animation

open/handlers_client.lua
1Handlers.OnBeforeOpenStash = function(stashId, areaId, lootIndex)
2end
3
4Handlers.OnAfterCloseStash = function(stashId, areaId)
5end
6
7Handlers.GetCustomAnimation = function(areaId, lootConfig)
8    return nil
9end
  • GetCustomAnimation: return an animation table to replace the one set on the prop

Zone Tracking

open/handlers_client.lua
1Handlers.OnZoneEnter = function(areaId, areaData)
2end
3
4Handlers.OnZoneExit = function(areaId)
5end
6
7Handlers.OnLootSpawned = function(areaId, lootTable)
8end
9
10Handlers.OnLootDespawned = function(areaId)
11end

Examples

open/handlers_server.lua
1local eventActive = false
2
3Handlers.CanPlayerLootZone = function(source, areaId, areaData)
4    if eventActive then
5        return false, 'Looting is closed during the event'
6    end
7    return true
8end
open/handlers_server.lua
1Handlers.GetLootMultiplier = function(source, areaId, areaData)
2    local job = Bridge.GetJob(source)
3    if job and job.name == 'vip' then
4        return 2.0
5    end
6    return 1.0
7end
open/handlers_client.lua
1Handlers.GetCustomTargetLabel = function(areaId, lootIndex, lootConfig)
2    if areaId == 'military_zone' then
3        return 'Search Military Crate'
4    end
5    return nil
6end
lua
1AddEventHandler('ml_lootzones:server:lootCollected', function(data)
2    if data.zoneId == 'military_zone' then
3        TriggerEvent('my_missions:server:progress', data.source, 'loot_military')
4    end
5end)