1
0
Fork 0
forked from Simnation/Main
Main/resources/[jobs]/[crime]/nordi_containerheist/client/main.lua
Nordi98 31cbcb2aca e
2025-07-20 03:42:24 +02:00

714 lines
25 KiB
Lua

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)