forked from Simnation/Main
ed
This commit is contained in:
parent
34e3fad675
commit
d3942f9c62
4 changed files with 222 additions and 1219 deletions
|
@ -1,8 +1,8 @@
|
|||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
local isRobbing = false
|
||||
local currentContainer = nil
|
||||
local currentPoint = nil
|
||||
local containerBlip = nil
|
||||
local addedEntities = {}
|
||||
local nearbyPoint = nil
|
||||
|
||||
-- Debug function
|
||||
local function Debug(msg)
|
||||
|
@ -28,92 +28,22 @@ function DrawText3D(x, y, z, text)
|
|||
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()
|
||||
-- Function to find the nearest container point
|
||||
local function GetNearestContainerPoint()
|
||||
local playerPed = PlayerPedId()
|
||||
local playerCoords = GetEntityCoords(playerPed)
|
||||
local foundEntity = nil
|
||||
local foundType = nil
|
||||
local closestDistance = 5.0 -- Maximum detection distance
|
||||
local closestPoint = nil
|
||||
local minDistance = 3.0 -- Maximum interaction 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
|
||||
for _, point in pairs(Config.ContainerPoints) do
|
||||
local distance = #(playerCoords - point.coords)
|
||||
if distance < minDistance then
|
||||
minDistance = distance
|
||||
closestPoint = point
|
||||
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
|
||||
return closestPoint, minDistance
|
||||
end
|
||||
|
||||
-- Function to create a blip at the robbery location
|
||||
|
@ -187,511 +117,163 @@ local function PlayRobberyAnimation(containerType)
|
|||
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)
|
||||
local function StartContainerRobbery(point)
|
||||
if isRobbing then return end
|
||||
|
||||
isRobbing = true
|
||||
currentContainer = container
|
||||
currentPoint = point
|
||||
|
||||
-- Get container type from point
|
||||
local containerType = Config.ContainerTypes[point.type]
|
||||
if not containerType then
|
||||
QBCore.Functions.Notify("Invalid container type!", "error")
|
||||
isRobbing = false
|
||||
currentPoint = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- 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'
|
||||
})
|
||||
QBCore.Functions.Notify(Config.Notifications.noTools, "error")
|
||||
isRobbing = false
|
||||
currentContainer = nil
|
||||
currentPoint = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Check cooldowns
|
||||
local cooldownCheck = lib.callback.await('container_heist:server:checkCooldown', false, NetworkGetNetworkIdFromEntity(container))
|
||||
local cooldownCheck = lib.callback.await('container_heist:server:checkCooldown', false, point.id)
|
||||
if not cooldownCheck.success then
|
||||
lib.notify({
|
||||
title = Config.Notifications.title,
|
||||
description = cooldownCheck.message,
|
||||
type = 'error'
|
||||
})
|
||||
QBCore.Functions.Notify(cooldownCheck.message, "error")
|
||||
isRobbing = false
|
||||
currentContainer = nil
|
||||
currentPoint = 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'
|
||||
})
|
||||
QBCore.Functions.Notify(Config.Notifications.notEnoughPolice, "error")
|
||||
isRobbing = false
|
||||
currentContainer = nil
|
||||
currentPoint = 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)
|
||||
SetEntityCoords(PlayerPedId(), point.coords.x, point.coords.y, point.coords.z)
|
||||
SetEntityHeading(PlayerPedId(), point.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)
|
||||
local streetName = GetStreetNameFromHashKey(GetStreetNameAtCoord(point.coords.x, point.coords.y, point.coords.z))
|
||||
TriggerServerEvent('container_heist:server:alertPolice', point.coords, 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
|
||||
QBCore.Functions.Progressbar("container_robbery", 'Breaking into ' .. containerType.label, containerType.animation.duration, false, true, {
|
||||
disableMovement = true,
|
||||
disableCarMovement = true,
|
||||
disableMouse = false,
|
||||
disableCombat = true,
|
||||
}, {}, {}, {}, function() -- Done
|
||||
-- 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
|
||||
|
||||
TriggerServerEvent('container_heist:server:finishRobbery', point.id, point.type)
|
||||
QBCore.Functions.Notify(Config.Notifications.success, "success")
|
||||
isRobbing = false
|
||||
currentContainer = nil
|
||||
currentPoint = nil
|
||||
end, function() -- Cancel
|
||||
-- Cancelled
|
||||
QBCore.Functions.Notify(Config.Notifications.failed, "error")
|
||||
isRobbing = false
|
||||
currentPoint = nil
|
||||
end)
|
||||
end
|
||||
|
||||
-- Command to start container robbery
|
||||
RegisterCommand('robcontainer', function()
|
||||
local container, containerType = IsNearValidContainer()
|
||||
if container and containerType then
|
||||
StartContainerRobbery(container, containerType)
|
||||
local point, distance = GetNearestContainerPoint()
|
||||
|
||||
if point then
|
||||
StartContainerRobbery(point)
|
||||
else
|
||||
lib.notify({
|
||||
title = Config.Notifications.title,
|
||||
description = "No valid container nearby!",
|
||||
type = 'error'
|
||||
})
|
||||
QBCore.Functions.Notify("No container nearby!", "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()
|
||||
QBCore.Functions.Notify("Container Debug mode enabled", "primary")
|
||||
else
|
||||
lib.notify({
|
||||
title = "Container Debug",
|
||||
description = "Debug mode disabled",
|
||||
type = 'inform',
|
||||
position = 'top',
|
||||
duration = 3000
|
||||
})
|
||||
QBCore.Functions.Notify("Container Debug mode disabled", "primary")
|
||||
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
|
||||
-- Main thread for checking nearby container points
|
||||
CreateThread(function()
|
||||
while true do
|
||||
ScanAndAddContainersToTarget()
|
||||
Wait(10000) -- Scan every 10 seconds
|
||||
local playerPed = PlayerPedId()
|
||||
local playerCoords = GetEntityCoords(playerPed)
|
||||
local wait = 1000
|
||||
local point, distance = GetNearestContainerPoint()
|
||||
|
||||
if point and distance < 3.0 then
|
||||
wait = 0
|
||||
nearbyPoint = point
|
||||
|
||||
-- Draw marker
|
||||
DrawMarker(1, point.coords.x, point.coords.y, point.coords.z - 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.8, 0.8, 0, 255, 0, 100, false, true, 2, false, nil, nil, false)
|
||||
|
||||
-- Draw text
|
||||
DrawText3D(point.coords.x, point.coords.y, point.coords.z, point.label .. " [E]")
|
||||
|
||||
-- Check for interaction
|
||||
if IsControlJustReleased(0, 38) and distance < 1.5 then -- E key
|
||||
StartContainerRobbery(point)
|
||||
end
|
||||
else
|
||||
nearbyPoint = nil
|
||||
end
|
||||
|
||||
Wait(wait)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Event handler for target interaction
|
||||
RegisterNetEvent('container_heist:client:startRobbery', function(data)
|
||||
local container, _ = IsNearValidContainer()
|
||||
if container then
|
||||
StartContainerRobbery(container, data.containerType)
|
||||
-- Debug thread for showing all container points
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(0)
|
||||
|
||||
if Config.Debug then
|
||||
for _, point in pairs(Config.ContainerPoints) do
|
||||
DrawMarker(1, point.coords.x, point.coords.y, point.coords.z - 1.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)
|
||||
DrawText3D(point.coords.x, point.coords.y, point.coords.z + 0.5, point.id .. " (" .. point.type .. ")")
|
||||
end
|
||||
else
|
||||
Wait(1000)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Event to show police alert
|
||||
RegisterNetEvent('container_heist:client:policeAlert', function(coords, streetName, containerType)
|
||||
RegisterNetEvent('container_heist:client:policeAlert', function(coords, streetName, containerLabel)
|
||||
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'
|
||||
})
|
||||
QBCore.Functions.Notify(alertTitle .. ": " .. string.format(Config.Notifications.policeMessage, streetName), "police", 10000)
|
||||
|
||||
-- Add blip to map
|
||||
CreateRobberyBlip(coords)
|
||||
|
@ -707,7 +289,7 @@ AddEventHandler('onResourceStop', function(resourceName)
|
|||
RemoveBlip(containerBlip)
|
||||
end
|
||||
|
||||
if isRobbing and currentContainer then
|
||||
if isRobbing then
|
||||
StopAnimTask(PlayerPedId(), "amb@world_human_welding@male@base", "base", 1.0)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
Config = {}
|
||||
weapon_combatpistoConfig = {}
|
||||
|
||||
-- General Settings
|
||||
Config.Debug = true -- Set to true for debug prints
|
||||
Config.CooldownTime = 1 -- Minutes between heists (per player)
|
||||
Config.GlobalCooldown = 1 -- Minutes between heists (server-wide)
|
||||
Config.PoliceRequired = 1 -- Minimum police required
|
||||
Config.Debug = false -- Set to true for debug prints
|
||||
Config.CooldownTime = 30 -- Minutes between heists (per player)
|
||||
Config.GlobalCooldown = 15 -- Minutes between heists (server-wide)
|
||||
Config.PoliceRequired = 2 -- Minimum police required
|
||||
Config.PoliceJobs = {
|
||||
"police", -- Regular police
|
||||
"marshal", -- Marshal service
|
||||
"sheriff" -- Sheriff department
|
||||
}
|
||||
|
||||
-- Required Items
|
||||
|
@ -42,42 +43,38 @@ Config.Blip = {
|
|||
sprite = 67,
|
||||
color = 1,
|
||||
scale = 0.8,
|
||||
label = "Container Einbruch",
|
||||
label = "Container Robbery",
|
||||
duration = 180, -- seconds
|
||||
flash = true,
|
||||
}
|
||||
|
||||
|
||||
-- Container Types (for rewards and animations)
|
||||
Config.ContainerTypes = {
|
||||
-- Regular shipping containers
|
||||
{
|
||||
model = "prop_container_01a",
|
||||
type = "shipping",
|
||||
shipping = {
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
duration = 30000, -- milliseconds
|
||||
},
|
||||
rewards = {
|
||||
-- Each reward has a chance (total should be <= 100)
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
-- Cash reward
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_01b",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
|
||||
-- Weapons container
|
||||
weapons = {
|
||||
label = "Weapons Container",
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
|
@ -93,340 +90,10 @@ Config.ContainerTypes = {
|
|||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_01c",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_01d",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_01e",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_01f",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_01g",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_01h",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_02a",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -4.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_03a",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -2.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_03b",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -2.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_03mb",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -2.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_04a",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -2.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_04mb",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -2.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_05mb",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -2.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_ld",
|
||||
type = "shipping",
|
||||
label = "Shipping Container",
|
||||
offset = vector3(0.0, -2.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 30000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "prop_container_door_mb",
|
||||
type = "shipping",
|
||||
label = "Container Door",
|
||||
offset = vector3(0.0, -1.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 20000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 1, chance = 20},
|
||||
{item = "cash", label = "Cash", min = 500, max = 2000, chance = 30},
|
||||
},
|
||||
policeAlert = false,
|
||||
},
|
||||
|
||||
-- Trailer models
|
||||
{
|
||||
model = "trailers",
|
||||
type = "trailer",
|
||||
-- Cargo trailer
|
||||
cargo = {
|
||||
label = "Cargo Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
|
@ -442,12 +109,10 @@ Config.ContainerTypes = {
|
|||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "trailers2",
|
||||
type = "trailer",
|
||||
label = "Box Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
|
||||
-- Food trailer
|
||||
food = {
|
||||
label = "Food Trailer",
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
|
@ -462,320 +127,73 @@ Config.ContainerTypes = {
|
|||
},
|
||||
policeAlert = false,
|
||||
},
|
||||
{
|
||||
model = "trailers3",
|
||||
type = "trailer",
|
||||
label = "Transport Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "phone", label = "Phone", min = 1, max = 3, chance = 30},
|
||||
{item = "rolex", label = "Rolex Watch", min = 1, max = 2, chance = 20},
|
||||
{item = "goldchain", label = "Gold Chain", min = 1, max = 3, chance = 25},
|
||||
{item = "diamond", label = "Diamond", min = 1, max = 1, chance = 5},
|
||||
{item = "laptop", label = "Laptop", min = 1, max = 1, chance = 15},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 5000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "trailers4",
|
||||
type = "trailer",
|
||||
label = "Car Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "carparts", label = "Car Parts", min = 3, max = 8, chance = 60},
|
||||
{item = "toolbox", label = "Toolbox", min = 1, max = 2, chance = 40},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 3000, chance = 30},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "trailerlogs",
|
||||
type = "trailer",
|
||||
label = "Logging Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "wood", label = "Wood", min = 10, max = 20, chance = 80},
|
||||
{item = "cash", label = "Cash", min = 500, max = 2000, chance = 20},
|
||||
},
|
||||
policeAlert = false,
|
||||
},
|
||||
{
|
||||
model = "tanker",
|
||||
type = "trailer",
|
||||
label = "Fuel Tanker",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 50000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "petrol", label = "Petrol", min = 10, max = 20, chance = 70},
|
||||
{item = "cash", label = "Cash", min = 2000, max = 5000, chance = 30},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "tanker2",
|
||||
type = "trailer",
|
||||
label = "Chemical Tanker",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 50000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "chemicals", label = "Chemicals", min = 5, max = 15, chance = 60},
|
||||
{item = "cash", label = "Cash", min = 3000, max = 8000, chance = 40},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "docktrailer",
|
||||
type = "trailer",
|
||||
label = "Dock Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "electronics", label = "Electronics", min = 3, max = 8, chance = 40},
|
||||
{item = "plastic", label = "Plastic", min = 10, max = 20, chance = 60},
|
||||
{item = "aluminum", label = "Aluminum", min = 10, max = 20, chance = 50},
|
||||
{item = "copper", label = "Copper", min = 5, max = 15, chance = 30},
|
||||
{item = "cash", label = "Cash", min = 3000, max = 10000, chance = 25},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "tr2",
|
||||
type = "trailer",
|
||||
label = "Car Carrier Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "carparts", label = "Car Parts", min = 3, max = 8, chance = 60},
|
||||
{item = "toolbox", label = "Toolbox", min = 1, max = 2, chance = 40},
|
||||
{item = "cash", label = "Cash", min = 1000, max = 3000, chance = 30},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "tr3",
|
||||
type = "trailer",
|
||||
label = "Large Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "electronics", label = "Electronics", min = 3, max = 8, chance = 40},
|
||||
{item = "plastic", label = "Plastic", min = 10, max = 20, chance = 60},
|
||||
{item = "aluminum", label = "Aluminum", min = 10, max = 20, chance = 50},
|
||||
{item = "copper", label = "Copper", min = 5, max = 15, chance = 30},
|
||||
{item = "cash", label = "Cash", min = 3000, max = 10000, chance = 25},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "tr4",
|
||||
type = "trailer",
|
||||
label = "Training Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "electronics", label = "Electronics", min = 3, max = 8, chance = 40},
|
||||
{item = "plastic", label = "Plastic", min = 10, max = 20, chance = 60},
|
||||
{item = "aluminum", label = "Aluminum", min = 10, max = 20, chance = 50},
|
||||
{item = "copper", label = "Copper", min = 5, max = 15, chance = 30},
|
||||
{item = "cash", label = "Cash", min = 3000, max = 10000, chance = 25},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "tvtrailer",
|
||||
type = "trailer",
|
||||
label = "TV Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "electronics", label = "Electronics", min = 5, max = 10, chance = 70},
|
||||
{item = "wires", label = "Wires", min = 5, max = 15, chance = 60},
|
||||
{item = "cash", label = "Cash", min = 2000, max = 6000, chance = 30},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "armytanker",
|
||||
type = "trailer",
|
||||
label = "Military Tanker",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 50000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "weapon_pistol", label = "Pistol", min = 1, max = 1, chance = 15},
|
||||
{item = "pistol_ammo", label = "Pistol Ammo", min = 10, max = 30, chance = 30},
|
||||
{item = "armor", label = "Body Armor", min = 1, max = 3, chance = 25},
|
||||
{item = "cash", label = "Cash", min = 5000, max = 15000, chance = 20},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "armytrailer",
|
||||
type = "trailer",
|
||||
label = "Military Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 50000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "weapon_pistol", label = "Pistol", min = 1, max = 1, chance = 15},
|
||||
{item = "pistol_ammo", label = "Pistol Ammo", min = 10, max = 30, chance = 30},
|
||||
{item = "armor", label = "Body Armor", min = 1, max = 3, chance = 25},
|
||||
{item = "cash", label = "Cash", min = 5000, max = 15000, chance = 20},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "freighttrailer",
|
||||
type = "trailer",
|
||||
label = "Freight Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 45000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "electronics", label = "Electronics", min = 3, max = 8, chance = 40},
|
||||
{item = "plastic", label = "Plastic", min = 10, max = 20, chance = 60},
|
||||
{item = "aluminum", label = "Aluminum", min = 10, max = 20, chance = 50},
|
||||
{item = "copper", label = "Copper", min = 5, max = 15, chance = 30},
|
||||
{item = "cash", label = "Cash", min = 3000, max = 10000, chance = 25},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
{
|
||||
model = "graintrailer",
|
||||
type = "trailer",
|
||||
label = "Grain Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "grain", label = "Grain", min = 10, max = 20, chance = 80},
|
||||
{item = "cash", label = "Cash", min = 500, max = 2000, chance = 20},
|
||||
},
|
||||
policeAlert = false,
|
||||
},
|
||||
{
|
||||
model = "proptrailer",
|
||||
type = "trailer",
|
||||
label = "Prop Trailer",
|
||||
offset = vector3(0.0, -5.0, 0.0),
|
||||
heading = 180.0,
|
||||
animation = {
|
||||
dict = "amb@world_human_welding@male@base",
|
||||
name = "base",
|
||||
flag = 1,
|
||||
duration = 40000,
|
||||
},
|
||||
rewards = {
|
||||
{item = "electronics", label = "Electronics", min = 3, max = 8, chance = 40},
|
||||
{item = "plastic", label = "Plastic", min = 10, max = 20, chance = 60},
|
||||
{item = "aluminum", label = "Aluminum", min = 10, max = 20, chance = 50},
|
||||
{item = "copper", label = "Copper", min = 5, max = 15, chance = 30},
|
||||
{item = "cash", label = "Cash", min = 3000, max = 10000, chance = 25},
|
||||
},
|
||||
policeAlert = true,
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- Locations where containers can be found (optional, for target setup)
|
||||
Config.ContainerLocations = {
|
||||
-- Container Points (specific locations for container robberies)
|
||||
Config.ContainerPoints = {
|
||||
-- Port area shipping containers
|
||||
{
|
||||
coords = vector3(1000.0, -3000.0, 5.0),
|
||||
heading = 0.0,
|
||||
model = "prop_container_01a",
|
||||
spawnContainer = true, -- Whether to spawn a container at this location
|
||||
id = "port_container_1",
|
||||
type = "shipping", -- References the container type above for rewards
|
||||
coords = vector3(895.7916, -2968.3140, 5.9008),
|
||||
heading = 269.5337,
|
||||
label = "Break into Container"
|
||||
},
|
||||
{
|
||||
coords = vector3(980.0, -3000.0, 5.0),
|
||||
heading = 0.0,
|
||||
model = "prop_container_01b",
|
||||
spawnContainer = true,
|
||||
id = "port_container_2",
|
||||
type = "shipping",
|
||||
coords = vector3(990.23, -3050.34, 5.90),
|
||||
heading = 90.0,
|
||||
label = "Break into Container"
|
||||
},
|
||||
-- Add more fixed locations as needed
|
||||
|
||||
-- Weapons containers
|
||||
{
|
||||
id = "weapons_container_1",
|
||||
type = "weapons",
|
||||
coords = vector3(905.45, -3200.67, 5.90),
|
||||
heading = 0.0,
|
||||
label = "Break into Weapons Container"
|
||||
},
|
||||
{
|
||||
id = "weapons_container_2",
|
||||
type = "weapons",
|
||||
coords = vector3(910.34, -3195.23, 5.90),
|
||||
heading = 270.0,
|
||||
label = "Break into Weapons Container"
|
||||
},
|
||||
|
||||
-- Cargo trailers
|
||||
{
|
||||
id = "cargo_trailer_1",
|
||||
type = "cargo",
|
||||
coords = vector3(825.34, -3125.45, 5.90),
|
||||
heading = 270.0,
|
||||
label = "Break into Cargo Trailer"
|
||||
},
|
||||
{
|
||||
id = "cargo_trailer_2",
|
||||
type = "cargo",
|
||||
coords = vector3(830.56, -3130.78, 5.90),
|
||||
heading = 180.0,
|
||||
label = "Break into Cargo Trailer"
|
||||
},
|
||||
|
||||
-- Food trailers
|
||||
{
|
||||
id = "food_trailer_1",
|
||||
type = "food",
|
||||
coords = vector3(765.23, -3165.34, 5.90),
|
||||
heading = 180.0,
|
||||
label = "Break into Food Trailer"
|
||||
},
|
||||
{
|
||||
id = "food_trailer_2",
|
||||
type = "food",
|
||||
coords = vector3(770.45, -3170.56, 5.90),
|
||||
heading = 90.0,
|
||||
label = "Break into Food Trailer"
|
||||
},
|
||||
|
||||
-- Add more points as needed
|
||||
}
|
||||
|
|
|
@ -11,16 +11,16 @@ shared_scripts {
|
|||
}
|
||||
|
||||
client_scripts {
|
||||
'client/main.lua'
|
||||
'client.lua'
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
'server/main.lua'
|
||||
'server.lua'
|
||||
}
|
||||
|
||||
lua54 'yes'
|
||||
|
||||
dependencies {
|
||||
'ox_lib',
|
||||
'tgiann-inventory'
|
||||
'qb-core'
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
local containerCooldowns = {}
|
||||
local pointCooldowns = {}
|
||||
local playerCooldowns = {}
|
||||
local globalCooldowns = {}
|
||||
|
||||
|
@ -22,7 +22,7 @@ lib.callback.register('container_heist:server:checkRequiredItems', function(sour
|
|||
end)
|
||||
|
||||
-- Check cooldowns
|
||||
lib.callback.register('container_heist:server:checkCooldown', function(source, containerId)
|
||||
lib.callback.register('container_heist:server:checkCooldown', function(source, pointId)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
if not Player then return {success = false, message = "Player not found"} end
|
||||
|
@ -36,19 +36,30 @@ lib.callback.register('container_heist:server:checkCooldown', function(source, c
|
|||
return {success = false, message = "You need to wait " .. timeLeft .. " more minutes before attempting another heist!"}
|
||||
end
|
||||
|
||||
-- Check container cooldown
|
||||
if containerCooldowns[containerId] and (currentTime - containerCooldowns[containerId]) < (Config.CooldownTime * 60) then
|
||||
-- Check point cooldown
|
||||
if pointCooldowns[pointId] and (currentTime - pointCooldowns[pointId]) < (Config.CooldownTime * 60) then
|
||||
return {success = false, message = Config.Notifications.alreadyRobbed}
|
||||
end
|
||||
|
||||
-- Check global cooldown for container type
|
||||
for containerType, cooldownTime in pairs(globalCooldowns) do
|
||||
if (currentTime - cooldownTime) < (Config.GlobalCooldown * 60) then
|
||||
local timeLeft = math.ceil(((cooldownTime + (Config.GlobalCooldown * 60)) - currentTime) / 60)
|
||||
return {success = false, message = Config.Notifications.globalCooldown .. " (" .. timeLeft .. " minutes left)"}
|
||||
-- Find point type
|
||||
local pointType = nil
|
||||
for _, point in pairs(Config.ContainerPoints) do
|
||||
if point.id == pointId then
|
||||
pointType = point.type
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not pointType then
|
||||
return {success = false, message = "Invalid point!"}
|
||||
end
|
||||
|
||||
-- Check global cooldown for container type
|
||||
if globalCooldowns[pointType] and (currentTime - globalCooldowns[pointType]) < (Config.GlobalCooldown * 60) then
|
||||
local timeLeft = math.ceil(((globalCooldowns[pointType] + (Config.GlobalCooldown * 60)) - currentTime) / 60)
|
||||
return {success = false, message = Config.Notifications.globalCooldown .. " (" .. timeLeft .. " minutes left)"}
|
||||
end
|
||||
|
||||
return {success = true}
|
||||
end)
|
||||
|
||||
|
@ -74,7 +85,7 @@ lib.callback.register('container_heist:server:getPoliceCount', function()
|
|||
end)
|
||||
|
||||
-- Alert police
|
||||
RegisterNetEvent('container_heist:server:alertPolice', function(coords, streetName, containerType)
|
||||
RegisterNetEvent('container_heist:server:alertPolice', function(coords, streetName, containerLabel)
|
||||
local src = source
|
||||
local players = QBCore.Functions.GetPlayers()
|
||||
|
||||
|
@ -84,7 +95,7 @@ RegisterNetEvent('container_heist:server:alertPolice', function(coords, streetNa
|
|||
-- Check if player's job is in the list of police jobs
|
||||
for _, jobName in ipairs(Config.PoliceJobs) do
|
||||
if Player.PlayerData.job.name == jobName and Player.PlayerData.job.onduty then
|
||||
TriggerClientEvent('container_heist:client:policeAlert', playerId, coords, streetName, containerType)
|
||||
TriggerClientEvent('container_heist:client:policeAlert', playerId, coords, streetName, containerLabel)
|
||||
break -- No need to send multiple alerts to the same player
|
||||
end
|
||||
end
|
||||
|
@ -93,7 +104,7 @@ RegisterNetEvent('container_heist:server:alertPolice', function(coords, streetNa
|
|||
end)
|
||||
|
||||
-- Finish robbery and give rewards
|
||||
RegisterNetEvent('container_heist:server:finishRobbery', function(containerId, containerType)
|
||||
RegisterNetEvent('container_heist:server:finishRobbery', function(pointId, containerTypeName)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
if not Player then return end
|
||||
|
@ -103,8 +114,15 @@ RegisterNetEvent('container_heist:server:finishRobbery', function(containerId, c
|
|||
|
||||
-- Set cooldowns
|
||||
playerCooldowns[citizenId] = currentTime
|
||||
containerCooldowns[containerId] = currentTime
|
||||
globalCooldowns[containerType] = currentTime
|
||||
pointCooldowns[pointId] = currentTime
|
||||
globalCooldowns[containerTypeName] = currentTime
|
||||
|
||||
-- Get container type
|
||||
local containerType = Config.ContainerTypes[containerTypeName]
|
||||
if not containerType then
|
||||
Debug("Container type not found: " .. containerTypeName)
|
||||
return
|
||||
end
|
||||
|
||||
-- Decrease durability of flex tool if configured
|
||||
if Config.RequiredItems.flex.durability then
|
||||
|
@ -129,23 +147,9 @@ RegisterNetEvent('container_heist:server:finishRobbery', function(containerId, c
|
|||
exports['tgiann-inventory']:RemoveItem(src, Config.RequiredItems.flex.name, Config.RequiredItems.flex.amount)
|
||||
end
|
||||
|
||||
-- Find the container type in config
|
||||
local containerConfig = nil
|
||||
for _, config in pairs(Config.ContainerTypes) do
|
||||
if config.type == containerType then
|
||||
containerConfig = config
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not containerConfig then
|
||||
Debug("Container type not found in config: " .. containerType)
|
||||
return
|
||||
end
|
||||
|
||||
-- Give rewards based on chances
|
||||
local rewardsGiven = 0
|
||||
for _, reward in pairs(containerConfig.rewards) do
|
||||
for _, reward in pairs(containerType.rewards) do
|
||||
if math.random(1, 100) <= reward.chance then
|
||||
local amount = math.random(reward.min, reward.max)
|
||||
|
||||
|
@ -190,10 +194,10 @@ CreateThread(function()
|
|||
end
|
||||
end
|
||||
|
||||
-- Clean up container cooldowns
|
||||
for containerId, cooldownTime in pairs(containerCooldowns) do
|
||||
-- Clean up point cooldowns
|
||||
for pointId, cooldownTime in pairs(pointCooldowns) do
|
||||
if (currentTime - cooldownTime) > (Config.CooldownTime * 60) then
|
||||
containerCooldowns[containerId] = nil
|
||||
pointCooldowns[pointId] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -205,4 +209,3 @@ CreateThread(function()
|
|||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue