Stashes, shops, player searching, dropzones, and secondary inventories
Advanced inventory features including storage stashes, shops, player search/rob mechanics, dropzones, polygon inventories, and secondary inventory management.
-- Server side - Open property stashRegisterServerEvent('property:server:OpenStash', function() local src = source local player = Fetch:Source(src) if not player then return end local char = player:GetData('Character') if not char then return end -- Check if player is in a property local propertyId = GlobalState[string.format("%s:Property", src)] if propertyId then -- Open property stash COMPONENTS.Inventory.Stash:Open(src, 13, propertyId) else TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Not in a property', type = 'error' }) endend)-- Server side - Gang stashRegisterServerEvent('gang:server:OpenStash', function(gangId) local src = source local player = Fetch:Source(src) if not player then return end local char = player:GetData('Character') if not char then return end -- Check gang membership if char:GetData('Gang') == gangId then -- Open gang stash COMPONENTS.Inventory.Stash:Open(src, 13, string.format("gang_%s", gangId)) else TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Not a member of this gang', type = 'error' }) endend)-- Server side - Hidden cache stashRegisterServerEvent('cache:server:Open', function(cacheId) local src = source -- Verify player is near cache -- ... distance check logic ... -- Open cache COMPONENTS.Inventory.Stash:Open(src, 13, string.format("cache_%s", cacheId))end)
Stash Identifiers:
Copy
-- Property stashes"property_123" -- Property ID 123-- Gang stashes"gang_ballas" -- Ballas gang stash"gang_vagos" -- Vagos gang stash-- Job stashes"police_evidence" -- Police evidence locker"mechanic_storage" -- Mechanic storage-- Hidden caches"cache_001" -- Cache location 001
-- Server side - Open 24/7 shopRegisterServerEvent('shop:server:Open247', function(shopId) local src = source local player = Fetch:Source(src) if not player then return end -- Open shop COMPONENTS.Inventory.Shop:Open(src, shopId)end)-- Server side - Job-restricted shopRegisterServerEvent('shop:server:OpenPoliceArmory', function() local src = source local player = Fetch:Source(src) if not player then return end local char = player:GetData('Character') if not char then return end -- Check if police if char:GetData('Job') == 'police' and player.state.onDuty then COMPONENTS.Inventory.Shop:Open(src, 'police_armory') else TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Police only', type = 'error' }) endend)
-- Server side - Police search playerRegisterServerEvent('police:server:SearchPlayer', function(targetSource) local src = source local player = Fetch:Source(src) if not player then return end local char = player:GetData('Character') if not char then return end -- Check if police and on duty if char:GetData('Job') ~= 'police' or not player.state.onDuty then return end -- Get target character local target = Fetch:Source(targetSource) if not target then return end local targetChar = target:GetData('Character') if not targetChar then return end -- Check distance local srcCoords = GetEntityCoords(GetPlayerPed(src)) local targetCoords = GetEntityCoords(GetPlayerPed(targetSource)) if #(srcCoords - targetCoords) > 3.0 then TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Too far away', type = 'error' }) return end -- Search player (read-only) COMPONENTS.Inventory.Search:Character(src, targetSource, targetChar:GetData('SID')) -- Log search Logger:Info('Police', string.format('%s %s searched %s %s', char:GetData('First'), char:GetData('Last'), targetChar:GetData('First'), targetChar:GetData('Last') ))end)
-- Server side - Rob playerRegisterServerEvent('crime:server:RobPlayer', function(targetSource) local src = source local player = Fetch:Source(src) if not player then return end local char = player:GetData('Character') if not char then return end -- Get target local target = Fetch:Source(targetSource) if not target then return end local targetChar = target:GetData('Character') if not targetChar then return end -- Check if target is dead or cuffed if not target.state.isDead and not target.state.isCuffed then TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Target must be incapacitated', type = 'error' }) return end -- Check distance local srcCoords = GetEntityCoords(GetPlayerPed(src)) local targetCoords = GetEntityCoords(GetPlayerPed(targetSource)) if #(srcCoords - targetCoords) > 2.0 then return end -- Rob player COMPONENTS.Inventory:Rob(src, targetSource, targetChar:GetData('SID')) -- Notify victim TriggerClientEvent('mythic-notifications:client:Send', targetSource, { message = 'You are being robbed', type = 'error' }) -- Log robbery Logger:Info('Crime', string.format('%s %s robbed %s %s', char:GetData('First'), char:GetData('Last'), targetChar:GetData('First'), targetChar:GetData('Last') ))end)
-- Server side - Create drug run dropzoneslocal function CreateDrugRoute(routeId) local dropzones = {} -- Create multiple drop points local dropPoints = { vector3(100.0, 200.0, 30.0), vector3(150.0, 250.0, 35.0), vector3(200.0, 300.0, 32.0) } for i, coords in ipairs(dropPoints) do local dzId = COMPONENTS.Inventory:CreateDropzone(routeId, coords) table.insert(dropzones, { id = dzId, coords = coords, completed = false }) end return dropzonesend-- Start drug runRegisterServerEvent('drugs:server:StartRun', function() local src = source local routeId = src -- Use player source as unique route local dropzones = CreateDrugRoute(routeId) -- Send to client TriggerClientEvent('drugs:client:StartRun', src, dropzones)end)
-- Server side - Check if at dropzoneRegisterServerEvent('drugs:server:CheckDrop', function() local src = source local ped = GetPlayerPed(src) local coords = GetEntityCoords(ped) local dropzone = COMPONENTS.Inventory:CheckDropZones(src, coords) if dropzone then -- Player is at dropzone TriggerClientEvent('drugs:client:AtDropzone', src, dropzone.id) else TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Not at a dropzone', type = 'error' }) endend)
-- Server side - Complete deliveryRegisterServerEvent('drugs:server:CompleteDelivery', function(dropzoneId) local src = source -- Check player is at dropzone local ped = GetPlayerPed(src) local coords = GetEntityCoords(ped) local dropzone = COMPONENTS.Inventory:CheckDropZones(src, coords) if dropzone and dropzone.id == dropzoneId then -- Remove dropzone COMPONENTS.Inventory:RemoveDropzone(src, dropzoneId) -- Reward player COMPONENTS.Characters:AddMoney(char:GetData('SID'), 'cash', 500) TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Delivery complete +$500', type = 'success' }) endend)
-- Server side - Property stash systemRegisterServerEvent('property:server:OpenStorage', function() local src = source local player = Fetch:Source(src) if not player then return end -- Check if in property local propertyId = GlobalState[string.format("%s:Property", src)] if not propertyId then TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Not in a property', type = 'error' }) return end -- Check property ownership local property = Properties:Get(propertyId) if not property then return end local char = player:GetData('Character') if not char then return end -- Check if owner or has keys if property.owner == char:GetData('SID') or Properties:HasKeys(src, propertyId) then -- Open property stash COMPONENTS.Inventory.Stash:Open(src, 13, string.format("property_%s", propertyId)) else TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'No access to this storage', type = 'error' }) endend)
-- Server side - Complete drug delivery systemlocal ActiveRoutes = {}RegisterServerEvent('drugs:server:StartDelivery', function() local src = source local player = Fetch:Source(src) if not player then return end local char = player:GetData('Character') if not char then return end -- Check has drugs if not COMPONENTS.Inventory:HasItem(char:GetData('SID'), 'coke_brick', 1) then TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'Need cocaine brick to start', type = 'error' }) return end -- Create delivery route local routeId = src local dropPoints = { vector3(100.0, 200.0, 30.0), vector3(200.0, 300.0, 35.0), vector3(300.0, 400.0, 32.0) } local dropzones = {} for i, coords in ipairs(dropPoints) do local dzId = COMPONENTS.Inventory:CreateDropzone(routeId, coords) table.insert(dropzones, { id = dzId, coords = coords, reward = 500 }) end ActiveRoutes[src] = { dropzones = dropzones, completed = 0, total = #dropzones } -- Send to client TriggerClientEvent('drugs:client:StartRoute', src, dropzones)end)RegisterServerEvent('drugs:server:CompleteDrop', function(dropzoneId) local src = source if not ActiveRoutes[src] then return end -- Check at dropzone local ped = GetPlayerPed(src) local coords = GetEntityCoords(ped) local dropzone = COMPONENTS.Inventory:CheckDropZones(src, coords) if not dropzone or dropzone.id ~= dropzoneId then return end -- Find dropzone in route for i, dz in ipairs(ActiveRoutes[src].dropzones) do if dz.id == dropzoneId and not dz.completed then -- Mark complete dz.completed = true ActiveRoutes[src].completed = ActiveRoutes[src].completed + 1 -- Remove dropzone COMPONENTS.Inventory:RemoveDropzone(src, dropzoneId) -- Reward local char = Fetch:Source(src):GetData('Character') COMPONENTS.Characters:AddMoney(char:GetData('SID'), 'cash', dz.reward) TriggerClientEvent('mythic-notifications:client:Send', src, { message = string.format('Drop complete +$%d (%d/%d)', dz.reward, ActiveRoutes[src].completed, ActiveRoutes[src].total ), type = 'success' }) -- Check if all complete if ActiveRoutes[src].completed >= ActiveRoutes[src].total then -- Bonus for completing all COMPONENTS.Characters:AddMoney(char:GetData('SID'), 'cash', 1000) TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'All deliveries complete! +$1000 bonus', type = 'success' }) ActiveRoutes[src] = nil end break end endend)
Stash Identifiers: Use descriptive, namespaced identifiers like property_123, gang_ballas, or job_police_evidence to avoid conflicts.
Search vs Rob: Use Search for police searches (read-only, legal). Use Rob for criminal activities (can take items). Always validate permissions server-side.