From 260aa8dcff24145eb57c00994d85739dd211a873 Mon Sep 17 00:00:00 2001 From: Nordi98 Date: Sun, 20 Jul 2025 18:25:02 +0200 Subject: [PATCH] Update main.lua --- .../nordi_containerheist/client/main.lua | 671 ++++++++++++++++-- 1 file changed, 621 insertions(+), 50 deletions(-) diff --git a/resources/[jobs]/[crime]/nordi_containerheist/client/main.lua b/resources/[jobs]/[crime]/nordi_containerheist/client/main.lua index 75178bd53..523e92389 100644 --- a/resources/[jobs]/[crime]/nordi_containerheist/client/main.lua +++ b/resources/[jobs]/[crime]/nordi_containerheist/client/main.lua @@ -1,54 +1,625 @@ -['hafenarbeiter'] = { - label = 'Red mit dem Hafenarbeiter', - icon = 'fa-solid fa-hard-hat', - model = "s_m_m_dockwork_01", - coords = vector3(1234.56, -3210.45, 5.9), -- Koordinaten anpassen - heading = 90, - data = { - firstname = 'Kalle', - lastname = 'Kutter', - text = "Moin... du wirkst nich wie jemand, der hier offiziell was abholen will.", - buttons = { - { - text = "Kommt drauf an, was es hier so gibt...", - data = { - text = "Hehehe... naja, sagen wir mal so: Manche Container stehen nachts ein bisschen... unbeaufsichtigt rum.", - buttons = { - { - text = "Ach ja? Und dann?", - data = { - text = "Na, wenn einer wüsste, wie man da *rein* kommt... bräuchte er sicher was mit Zähne oder Strom, verstehste?", - buttons = { +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 + +-- 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 at the correct position (rear for trailers, door for containers) +local function IsPlayerAtCorrectPosition(entity, containerType) + local playerPos = GetEntityCoords(PlayerPedId()) + local entityPos = GetEntityCoords(entity) + local entityHeading = GetEntityHeading(entity) + local entityForwardVector = GetEntityForwardVector(entity) + + -- Get vector from entity to player + local toPlayerVector = vector3( + playerPos.x - entityPos.x, + playerPos.y - entityPos.y, + 0.0 + ) + + -- Normalize the vector + local length = math.sqrt(toPlayerVector.x^2 + toPlayerVector.y^2) + if length > 0 then + toPlayerVector = vector3(toPlayerVector.x / length, toPlayerVector.y / length, 0.0) + else + return false + end + + -- Calculate dot product with forward vector + local forwardDot = toPlayerVector.x * entityForwardVector.x + toPlayerVector.y * entityForwardVector.y + + if string.match(containerType.type, "trailer") then + -- For trailers, player should be at the rear (negative dot product) + return forwardDot < -0.7 -- Threshold to ensure player is behind the trailer + else + -- For containers, player should be at the door (negative dot product, as doors are typically at the rear) + return forwardDot < -0.7 -- Threshold to ensure player is at the door end of the container + end +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 = 999.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 model = GetEntityModel(object) + local objectCoords = GetEntityCoords(object) + local distance = #(playerCoords - objectCoords) + + if distance <= 5.0 and distance < closestDistance then + for _, containerType in pairs(Config.ContainerTypes) do + if model == GetHashKey(containerType.model) then + -- Check if player is at the door for containers + if IsPlayerAtCorrectPosition(object, containerType) then + foundEntity = object + foundType = containerType + closestDistance = distance + break + else + lib.notify({ + title = Config.Notifications.title, + description = "You need to stand at the door end of the container!", + type = 'error' + }) + end + 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 model = GetEntityModel(vehicle) + local vehicleCoords = GetEntityCoords(vehicle) + local distance = #(playerCoords - vehicleCoords) + + if distance <= 5.0 and distance < closestDistance then + for _, containerType in pairs(Config.ContainerTypes) do + if model == GetHashKey(containerType.model) then + -- Check if player is at the rear for trailers + if IsPlayerAtCorrectPosition(vehicle, containerType) then + foundEntity = vehicle + foundType = containerType + closestDistance = distance + break + else + lib.notify({ + title = Config.Notifications.title, + description = "You need to stand at the rear of the trailer!", + type = 'error' + }) + 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 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 + + -- Get container dimensions + local containerModel = GetEntityModel(container) + local min, max = GetModelDimensions(containerModel) + local containerLength = max.y - min.y + + -- Position player for animation + local containerCoords = GetEntityCoords(container) + local containerHeading = GetEntityHeading(container) + local offsetCoords + local playerHeading + + -- Both trailers and containers should position player at the rear + offsetCoords = GetOffsetFromEntityInWorldCoords(container, 0.0, -(containerLength/2 + 1.0), 0.0) + playerHeading = containerHeading + 180.0 -- Face the rear of the trailer/container + + -- Set player position and heading + SetEntityCoords(PlayerPedId(), offsetCoords.x, offsetCoords.y, offsetCoords.z) + SetEntityHeading(PlayerPedId(), playerHeading) + + -- 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 + +-- 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) + + if not addedEntities[object] then + for _, containerType in pairs(Config.ContainerTypes) do + if model == GetHashKey(containerType.model) then + exports['qb-target']:AddTargetEntity(object, { + options = { { - text = "Klar. Ich versteh schon.", - data = { - text = "Gut. Dann hab ich dir ja nix gesagt, oder?", - buttons = { - { - text = "Du hast mich nie gesehen.", - close = true - } - } - } - }, - { - text = "Klingt mir zu heiß...", - close = true + type = "client", + event = "container_heist:client:startRobbery", + icon = "fas fa-angle-double-right", + label = "Break into " .. containerType.label, + containerType = containerType, + canInteract = function(entity) + return IsPlayerAtCorrectPosition(entity, containerType) + end } - } - } - }, - { - text = "Ich glaub, ich hab mich verlaufen...", - close = true - } - } + }, + distance = 3.0 + }) + addedEntities[object] = true + count = count + 1 + break + end + 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) + + if not addedEntities[vehicle] then + for _, containerType in pairs(Config.ContainerTypes) do + if model == GetHashKey(containerType.model) then + exports['qb-target']:AddTargetEntity(vehicle, { + options = { + { + type = "client", + event = "container_heist:client:startRobbery", + icon = "fas fa-angle-double-right", + label = "Break into " .. containerType.label, + containerType = containerType, + canInteract = function(entity) + return IsPlayerAtCorrectPosition(entity, containerType) + end + } + }, + distance = 3.0 + }) + addedEntities[vehicle] = true + count = count + 1 + break + end + end + end + end + end + end + + return count +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) + +-- 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, + canInteract = function(entity) + return IsPlayerAtCorrectPosition(entity, containerType) + end } }, - { - text = "Nur mal umschauen, Chef.", - close = true - } - } - } -} + 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) + +-- Automatically scan for containers periodically +CreateThread(function() + while true do + ScanAndAddContainersToTarget() + Wait(30000) -- Scan every 30 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)