local QBCore = exports['qb-core']:GetCoreObject() local isRobbing = false local currentContainer = nil local containerBlip = nil local addedEntities = {} -- Debug function local function Debug(msg) if Config.Debug then print("[Container Heist] " .. msg) end end -- Helper function to draw 3D text function DrawText3D(x, y, z, text) local onScreen, _x, _y = World3dToScreen2d(x, y, z) local px, py, pz = table.unpack(GetGameplayCamCoords()) SetTextScale(0.35, 0.35) SetTextFont(4) SetTextProportional(1) SetTextColour(255, 255, 255, 215) SetTextEntry("STRING") SetTextCentre(1) AddTextComponentString(text) DrawText(_x, _y) local factor = (string.len(text)) / 370 DrawRect(_x, _y + 0.0125, 0.015 + factor, 0.03, 41, 11, 41, 68) end -- Function to get model name from hash local function GetModelNameFromHash(hash) for _, containerType in pairs(Config.ContainerTypes) do if GetHashKey(containerType.model) == hash then return containerType.model end end return "Unknown" end -- Function to check if player is near a valid container local function IsNearValidContainer() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local foundEntity = nil local foundType = nil local closestDistance = 5.0 -- Maximum detection distance -- First check: Use raycast to detect what player is looking at local rayHandle = StartExpensiveSynchronousShapeTestLosProbe( playerCoords.x, playerCoords.y, playerCoords.z, playerCoords.x + (GetEntityForwardX(playerPed) * 5.0), playerCoords.y + (GetEntityForwardY(playerPed) * 5.0), playerCoords.z, 16, playerPed, 4 ) local _, hit, endCoords, _, entity = GetShapeTestResult(rayHandle) if hit and DoesEntityExist(entity) then local model = GetEntityModel(entity) local entityType = GetEntityType(entity) local distance = #(playerCoords - GetEntityCoords(entity)) -- Check if this entity is a valid container or trailer for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then return entity, containerType end end end -- Second check: Check all objects in area local objects = GetGamePool('CObject') for _, object in ipairs(objects) do if DoesEntityExist(object) and not IsEntityDead(object) then local model = GetEntityModel(object) local objectCoords = GetEntityCoords(object) local distance = #(playerCoords - objectCoords) if distance <= closestDistance then for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then if distance < closestDistance then foundEntity = object foundType = containerType closestDistance = distance end end end end end end -- Third check: Check all vehicles in area (for trailers) local vehicles = GetGamePool('CVehicle') for _, vehicle in ipairs(vehicles) do if DoesEntityExist(vehicle) and not IsEntityDead(vehicle) then local model = GetEntityModel(vehicle) local vehicleCoords = GetEntityCoords(vehicle) local distance = #(playerCoords - vehicleCoords) if distance <= closestDistance then for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then if distance < closestDistance then foundEntity = vehicle foundType = containerType closestDistance = distance end end end end end end return foundEntity, foundType end -- Function to create a blip at the robbery location local function CreateRobberyBlip(coords) if containerBlip then RemoveBlip(containerBlip) end containerBlip = AddBlipForCoord(coords) -- Set blip color based on job local playerJob = QBCore.Functions.GetPlayerData().job.name if playerJob == "marshal" then SetBlipColour(containerBlip, 38) -- Purple for Marshal elseif playerJob == "sheriff" then SetBlipColour(containerBlip, 16) -- Orange for Sheriff else SetBlipColour(containerBlip, Config.Blip.color) -- Default color for regular police end SetBlipSprite(containerBlip, Config.Blip.sprite) SetBlipScale(containerBlip, Config.Blip.scale) SetBlipAsShortRange(containerBlip, true) BeginTextCommandSetBlipName("STRING") AddTextComponentString(Config.Blip.label) EndTextCommandSetBlipName(containerBlip) if Config.Blip.flash then SetBlipFlashes(containerBlip, true) end -- Remove blip after duration SetTimeout(Config.Blip.duration * 1000, function() if containerBlip then RemoveBlip(containerBlip) containerBlip = nil end end) end -- Function to play container robbery animation local function PlayRobberyAnimation(containerType) local playerPed = PlayerPedId() local animDict = containerType.animation.dict local animName = containerType.animation.name RequestAnimDict(animDict) while not HasAnimDictLoaded(animDict) do Wait(10) end TaskPlayAnim(playerPed, animDict, animName, 8.0, -8.0, containerType.animation.duration, containerType.animation.flag, 0, false, false, false) -- Add particle effects for welding local boneIndex = GetPedBoneIndex(playerPed, 28422) local particleDict = "core" local particleName = "ent_amb_welding" RequestNamedPtfxAsset(particleDict) while not HasNamedPtfxAssetLoaded(particleDict) do Wait(10) end UseParticleFxAssetNextCall(particleDict) local particleHandle = StartParticleFxLoopedOnPedBone(particleName, playerPed, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, boneIndex, 0.5, false, false, false) -- Clean up after animation SetTimeout(containerType.animation.duration, function() StopParticleFxLooped(particleHandle, 0) StopAnimTask(playerPed, animDict, animName, 1.0) end) end -- Function to add target to specific entity local function AddTargetToEntity(entity, containerType) if not DoesEntityExist(entity) or not containerType then return false end -- Check if we've already added this entity if addedEntities[entity] then return false end -- Add target to this specific entity exports['qb-target']:AddTargetEntity(entity, { options = { { type = "client", event = "container_heist:client:startRobbery", icon = "fas fa-angle-double-right", label = "Break into " .. containerType.label, containerType = containerType, } }, distance = 3.0 }) addedEntities[entity] = true return true end -- Function to scan and add all nearby containers to target system local function ScanAndAddContainersToTarget() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local count = 0 -- Check for containers in the area (objects) local objects = GetGamePool('CObject') for _, object in ipairs(objects) do if DoesEntityExist(object) and not IsEntityDead(object) then local objectCoords = GetEntityCoords(object) local distance = #(playerCoords - objectCoords) if distance <= 50.0 then local model = GetEntityModel(object) for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then if AddTargetToEntity(object, containerType) then count = count + 1 Debug("Added target to container: " .. containerType.model .. " at distance: " .. distance) end break end end end end end -- Check for trailers in the area (vehicles) local vehicles = GetGamePool('CVehicle') for _, vehicle in ipairs(vehicles) do if DoesEntityExist(vehicle) and not IsEntityDead(vehicle) then local vehicleCoords = GetEntityCoords(vehicle) local distance = #(playerCoords - vehicleCoords) if distance <= 50.0 then local model = GetEntityModel(vehicle) for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then if AddTargetToEntity(vehicle, containerType) then count = count + 1 Debug("Added target to trailer: " .. containerType.model .. " at distance: " .. distance) end break end end end end end return count end -- Function to visualize containers for debugging local function VisualizeContainers() if not Config.Debug then return end CreateThread(function() while Config.Debug do local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) -- Check for containers in the area (objects) local objects = GetGamePool('CObject') for _, object in ipairs(objects) do if DoesEntityExist(object) and not IsEntityDead(object) then local objectCoords = GetEntityCoords(object) local distance = #(playerCoords - objectCoords) if distance <= 20.0 then local model = GetEntityModel(object) for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then -- Draw a marker at the container DrawMarker(1, objectCoords.x, objectCoords.y, objectCoords.z + 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 255, 0, 0, 100, false, true, 2, false, nil, nil, false) -- Draw text with container type DrawText3D(objectCoords.x, objectCoords.y, objectCoords.z + 2.5, containerType.label) break end end end end end -- Check for trailers in the area (vehicles) local vehicles = GetGamePool('CVehicle') for _, vehicle in ipairs(vehicles) do if DoesEntityExist(vehicle) and not IsEntityDead(vehicle) then local vehicleCoords = GetEntityCoords(vehicle) local distance = #(playerCoords - vehicleCoords) if distance <= 20.0 then local model = GetEntityModel(vehicle) for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then -- Draw a marker at the trailer DrawMarker(1, vehicleCoords.x, vehicleCoords.y, vehicleCoords.z + 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0, 0, 255, 100, false, true, 2, false, nil, nil, false) -- Draw text with trailer type DrawText3D(vehicleCoords.x, vehicleCoords.y, vehicleCoords.z + 2.5, containerType.label) break end end end end end Wait(0) end end) end -- Function to start container robbery local function StartContainerRobbery(container, containerType) if isRobbing then return end isRobbing = true currentContainer = container -- Check if player has required tools local hasTools = lib.callback.await('container_heist:server:checkRequiredItems', false) if not hasTools then lib.notify({ title = Config.Notifications.title, description = Config.Notifications.noTools, type = 'error' }) isRobbing = false currentContainer = nil return end -- Check cooldowns local cooldownCheck = lib.callback.await('container_heist:server:checkCooldown', false, NetworkGetNetworkIdFromEntity(container)) if not cooldownCheck.success then lib.notify({ title = Config.Notifications.title, description = cooldownCheck.message, type = 'error' }) isRobbing = false currentContainer = nil return end -- Check police count local policeCount = lib.callback.await('container_heist:server:getPoliceCount', false) if policeCount < Config.PoliceRequired then lib.notify({ title = Config.Notifications.title, description = Config.Notifications.notEnoughPolice, type = 'error' }) isRobbing = false currentContainer = nil return end -- Position player for animation local containerCoords = GetEntityCoords(container) local containerHeading = GetEntityHeading(container) local offsetCoords = GetOffsetFromEntityInWorldCoords(container, containerType.offset.x, containerType.offset.y, containerType.offset.z) -- Set player position and heading SetEntityCoords(PlayerPedId(), offsetCoords.x, offsetCoords.y, offsetCoords.z) SetEntityHeading(PlayerPedId(), containerHeading + containerType.heading) -- Alert police if configured if containerType.policeAlert then local streetName = GetStreetNameFromHashKey(GetStreetNameAtCoord(containerCoords.x, containerCoords.y, containerCoords.z)) TriggerServerEvent('container_heist:server:alertPolice', containerCoords, streetName, containerType.label) end -- Start robbery progress bar PlayRobberyAnimation(containerType) if lib.progressBar({ duration = containerType.animation.duration, label = 'Breaking into ' .. containerType.label, useWhileDead = false, canCancel = true, disable = { car = true, move = true, combat = true, }, anim = { dict = containerType.animation.dict, clip = containerType.animation.name, }, }) then -- Success TriggerServerEvent('container_heist:server:finishRobbery', NetworkGetNetworkIdFromEntity(container), containerType.type) lib.notify({ title = Config.Notifications.title, description = Config.Notifications.success, type = 'success' }) else -- Cancelled lib.notify({ title = Config.Notifications.title, description = Config.Notifications.failed, type = 'error' }) end isRobbing = false currentContainer = nil end -- Command to start container robbery RegisterCommand('robcontainer', function() local container, containerType = IsNearValidContainer() if container and containerType then StartContainerRobbery(container, containerType) else lib.notify({ title = Config.Notifications.title, description = "No valid container nearby!", type = 'error' }) end end, false) -- Command to scan and add all nearby containers to target system RegisterCommand('scancontainers', function() local count = ScanAndAddContainersToTarget() lib.notify({ title = "Container Scanner", description = "Added " .. count .. " containers/trailers to target system", type = 'success', position = 'top', duration = 3000 }) end, false) -- Command to force refresh all targets RegisterCommand('refreshcontainers', function() -- Clear existing targets addedEntities = {} -- Force a new scan local count = ScanAndAddContainersToTarget() lib.notify({ title = "Container Refresh", description = "Refreshed " .. count .. " containers/trailers", type = 'success', position = 'top', duration = 3000 }) end, false) -- Command to toggle debug mode RegisterCommand('containerdebug', function() Config.Debug = not Config.Debug if Config.Debug then lib.notify({ title = "Container Debug", description = "Debug mode enabled", type = 'inform', position = 'top', duration = 3000 }) VisualizeContainers() else lib.notify({ title = "Container Debug", description = "Debug mode disabled", type = 'inform', position = 'top', duration = 3000 }) end end, false) -- Debug command to show all nearby containers and trailers RegisterCommand('containersdebug', function() if not Config.Debug then return end local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local foundContainers = 0 -- Check for containers in the area (objects) local objects = GetGamePool('CObject') for _, object in ipairs(objects) do if DoesEntityExist(object) and not IsEntityDead(object) then local objectCoords = GetEntityCoords(object) local distance = #(playerCoords - objectCoords) if distance <= 20.0 then local model = GetEntityModel(object) local modelName = "Unknown" for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then modelName = containerType.model break end end print("Found container object: " .. modelName .. " (Hash: " .. model .. ") at distance: " .. distance) foundContainers = foundContainers + 1 end end end -- Check for trailers in the area (vehicles) local vehicles = GetGamePool('CVehicle') for _, vehicle in ipairs(vehicles) do if DoesEntityExist(vehicle) and not IsEntityDead(vehicle) then local vehicleCoords = GetEntityCoords(vehicle) local distance = #(playerCoords - vehicleCoords) if distance <= 20.0 then local model = GetEntityModel(vehicle) local modelName = "Unknown" for _, containerType in pairs(Config.ContainerTypes) do if model == GetHashKey(containerType.model) then modelName = containerType.model break end end print("Found trailer vehicle: " .. modelName .. " (Hash: " .. model .. ") at distance: " .. distance) foundContainers = foundContainers + 1 end end end print("Total containers/trailers found: " .. foundContainers) end, false) -- Command to identify the model of what you're looking at RegisterCommand('identifycontainer', function() local playerPed = PlayerPedId() local success, entity = GetEntityPlayerIsFreeAimingAt(PlayerId()) if success and DoesEntityExist(entity) then local model = GetEntityModel(entity) local modelName = GetModelNameFromHash(model) local entityType = GetEntityType(entity) local entityTypeStr = "Unknown" if entityType == 1 then entityTypeStr = "Ped" elseif entityType == 2 then entityTypeStr = "Vehicle" elseif entityType == 3 then entityTypeStr = "Object" end print("Entity Type: " .. entityTypeStr) print("Model Hash: " .. model) print("Model Name: " .. modelName) -- Add visual indicator local coords = GetEntityCoords(entity) DrawMarker(0, coords.x, coords.y, coords.z + 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 255, 0, 0, 100, false, true, 2, false, nil, nil, false) -- Show notification lib.notify({ title = "Container Identification", description = "Type: " .. entityTypeStr .. "\nModel: " .. modelName .. "\nHash: " .. model, type = 'inform', position = 'top', duration = 5000 }) else lib.notify({ title = "Container Identification", description = "No entity found. Aim at a container or trailer.", type = 'error', position = 'top', duration = 3000 }) end end, false) -- Setup target interactions for containers CreateThread(function() -- Wait for target system to be ready Wait(1000) -- Add target for all container types for _, containerType in pairs(Config.ContainerTypes) do exports['qb-target']:AddTargetModel(containerType.model, { options = { { type = "client", event = "container_heist:client:startRobbery", icon = "fas fa-angle-double-right", label = "Break into " .. containerType.label, containerType = containerType, } }, distance = 3.0 }) end -- Spawn containers at fixed locations if configured for _, location in pairs(Config.ContainerLocations) do if location.spawnContainer then local hash = GetHashKey(location.model) RequestModel(hash) while not HasModelLoaded(hash) do Wait(10) end local container = CreateObject(hash, location.coords.x, location.coords.y, location.coords.z, true, false, false) SetEntityHeading(container, location.heading) FreezeEntityPosition(container, true) SetModelAsNoLongerNeeded(hash) end end end) -- Initial scan when resource starts CreateThread(function() Wait(2000) -- Wait for everything to load ScanAndAddContainersToTarget() -- If debug mode is enabled, start visualizing containers if Config.Debug then VisualizeContainers() end end) -- Automatically scan for containers periodically CreateThread(function() while true do ScanAndAddContainersToTarget() Wait(10000) -- Scan every 10 seconds end end) -- Event handler for target interaction RegisterNetEvent('container_heist:client:startRobbery', function(data) local container, _ = IsNearValidContainer() if container then StartContainerRobbery(container, data.containerType) end end) -- Event to show police alert RegisterNetEvent('container_heist:client:policeAlert', function(coords, streetName, containerType) local playerJob = QBCore.Functions.GetPlayerData().job.name local alertTitle = Config.Notifications.policeTitle local alertIcon = 'fas fa-exclamation-triangle' -- Customize alert based on job if playerJob == "marshal" then alertTitle = "MARSHAL SERVICE ALERT" alertIcon = 'fas fa-star' -- Marshal badge icon elseif playerJob == "sheriff" then alertTitle = "SHERIFF DEPARTMENT ALERT" alertIcon = 'fas fa-shield-alt' -- Sheriff badge icon end -- Create alert for police officers lib.notify({ title = alertTitle, description = string.format(Config.Notifications.policeMessage, streetName), type = 'inform', position = 'top', icon = alertIcon, iconColor = '#ff0000' }) -- Add blip to map CreateRobberyBlip(coords) -- Play alert sound PlaySound(-1, "Lose_1st", "GTAO_FM_Events_Soundset", 0, 0, 1) end) -- Clean up on resource stop AddEventHandler('onResourceStop', function(resourceName) if resourceName == GetCurrentResourceName() then if containerBlip then RemoveBlip(containerBlip) end if isRobbing and currentContainer then StopAnimTask(PlayerPedId(), "amb@world_human_welding@male@base", "base", 1.0) end end end)