285 lines
9.1 KiB
Lua
285 lines
9.1 KiB
Lua
local redLightModel = `jd_weighstation_lightred`
|
|
local greenLightModel = `jd_weighstation_lightgreen`
|
|
|
|
-- Active lights per lane (Now local entities)
|
|
local activeLights = {
|
|
LaneA = {}
|
|
}
|
|
|
|
-- Lane states: true = green, false = red, nil = off
|
|
local laneStates = {
|
|
LaneA = nil
|
|
}
|
|
|
|
local isInRange = false
|
|
local isNearControl = false
|
|
local playerAllowed = false
|
|
|
|
-- AutoMode tracking (single lane)
|
|
local autoTrack = {
|
|
LaneA = {
|
|
detected = false,
|
|
active = false,
|
|
detectionThread = nil,
|
|
lastVeh = nil,
|
|
lastTrigger = 0
|
|
}
|
|
}
|
|
|
|
-- ============================================================
|
|
-- THE FIX: GHOST OBJECT CLEANUP
|
|
-- ============================================================
|
|
local function ForceCleanUp()
|
|
local models = { redLightModel, greenLightModel }
|
|
for _, model in ipairs(models) do
|
|
local handle, entity = FindFirstObject()
|
|
local success
|
|
repeat
|
|
if GetEntityModel(entity) == model then
|
|
SetEntityAsMissionEntity(entity, true, true)
|
|
DeleteEntity(entity)
|
|
end
|
|
success, entity = FindNextObject(handle)
|
|
until not success
|
|
EndFindObject(handle)
|
|
end
|
|
end
|
|
|
|
-- ============================================================
|
|
-- PERMISSION SYSTEM
|
|
-- ============================================================
|
|
CreateThread(function()
|
|
ForceCleanUp() -- Run cleanup once when script starts
|
|
if Config.ACL.UseACL then
|
|
TriggerServerEvent("weigh_lights:checkPermissionMain")
|
|
else
|
|
playerAllowed = true
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent("weigh_lights:permissionResultMain", function(allowed)
|
|
playerAllowed = allowed
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- MODEL LOADING & SPAWNING
|
|
-- ============================================================
|
|
local function loadModel(model)
|
|
if not HasModelLoaded(model) then
|
|
RequestModel(model)
|
|
local t0 = GetGameTimer()
|
|
while not HasModelLoaded(model) do
|
|
Wait(10)
|
|
if GetGameTimer() - t0 > 5000 then RequestModel(model) end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function clearLaneObjects(lane)
|
|
if activeLights[lane] then
|
|
for _, obj in ipairs(activeLights[lane]) do
|
|
if DoesEntityExist(obj) then
|
|
SetEntityAsMissionEntity(obj, true, true)
|
|
DeleteEntity(obj)
|
|
end
|
|
end
|
|
end
|
|
activeLights[lane] = {}
|
|
end
|
|
|
|
local function spawnLaneLight(lane, model)
|
|
loadModel(model)
|
|
if not Config.MainLights.Lanes[lane] then return end
|
|
|
|
clearLaneObjects(lane) -- Ensure clean slate before spawning
|
|
|
|
for _, light in ipairs(Config.MainLights.Lanes[lane].Lights) do
|
|
local pos = light.coords
|
|
-- CRITICAL: 5th param is FALSE (Not networked)
|
|
local obj = CreateObjectNoOffset(model, pos.x, pos.y, pos.z, false, false, false)
|
|
|
|
SetEntityCoordsNoOffset(obj, pos.x, pos.y, pos.z)
|
|
if light.rotation then
|
|
SetEntityRotation(obj, light.rotation.x, light.rotation.y, light.rotation.z, 2, true)
|
|
end
|
|
FreezeEntityPosition(obj, true)
|
|
SetEntityInvincible(obj, true)
|
|
table.insert(activeLights[lane], obj)
|
|
end
|
|
end
|
|
|
|
-- ============================================================
|
|
-- REFRESH LANE
|
|
-- ============================================================
|
|
local function refreshLane(lane)
|
|
local state = laneStates[lane]
|
|
if state == nil then
|
|
clearLaneObjects(lane)
|
|
return
|
|
end
|
|
local model = state and greenLightModel or redLightModel
|
|
spawnLaneLight(lane, model)
|
|
end
|
|
|
|
local function setLaneStateLocal(lane, state, notify)
|
|
laneStates[lane] = state
|
|
|
|
if isInRange then
|
|
refreshLane(lane)
|
|
end
|
|
|
|
if notify then
|
|
local desc = state == nil and "OFF" or (state and "GREEN" or "RED")
|
|
lib.notify({
|
|
title = 'Weigh Station',
|
|
description = ("Lane A set to %s"):format(desc),
|
|
type = state == nil and 'inform' or (state and 'success' or 'error')
|
|
})
|
|
end
|
|
end
|
|
|
|
-- ============================================================
|
|
-- NETWORK SYNC
|
|
-- ============================================================
|
|
RegisterNetEvent("jd_weighlight:setLaneState", function(lane, state)
|
|
if lane ~= "LaneA" then return end
|
|
setLaneStateLocal(lane, state, false)
|
|
end)
|
|
|
|
CreateThread(function()
|
|
Wait(3000)
|
|
TriggerServerEvent("jd_weighlight:requestState")
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- RANGE CHECK
|
|
-- ============================================================
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(1000)
|
|
local pCoords = GetEntityCoords(PlayerPedId())
|
|
local nearAny = false
|
|
|
|
if Config.MainLights.Lanes.LaneA then
|
|
for _, light in ipairs(Config.MainLights.Lanes.LaneA.Lights) do
|
|
if #(pCoords - light.coords) < (Config.MainLights.LightSpawnRange or 200.0) then
|
|
nearAny = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if nearAny and not isInRange then
|
|
isInRange = true
|
|
TriggerServerEvent("jd_weighlight:requestState")
|
|
elseif not nearAny and isInRange then
|
|
isInRange = false
|
|
clearLaneObjects("LaneA")
|
|
autoTrack.LaneA.detected = false
|
|
autoTrack.LaneA.active = false
|
|
autoTrack.LaneA.lastVeh = nil
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- CONTROL MENU
|
|
-- ============================================================
|
|
local function openControlMenu()
|
|
local menu = {
|
|
id = 'jd_weighlight_menu_single',
|
|
title = 'Weigh Station Light Control',
|
|
options = {
|
|
{ title = 'Lane A → GREEN', icon = 'check-circle', onSelect = function()
|
|
TriggerServerEvent("jd_weighlight:setLaneState", "LaneA", true)
|
|
end },
|
|
{ title = 'Lane A → RED', icon = 'ban', onSelect = function()
|
|
TriggerServerEvent("jd_weighlight:setLaneState", "LaneA", false)
|
|
end },
|
|
{ title = 'Lane A → OFF', icon = 'power-off', onSelect = function()
|
|
TriggerServerEvent("jd_weighlight:setLaneState", "LaneA", nil)
|
|
end },
|
|
}
|
|
}
|
|
lib.registerContext(menu)
|
|
lib.showContext('jd_weighlight_menu_single')
|
|
end
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(300)
|
|
local dist = #(GetEntityCoords(PlayerPedId()) - Config.MainLights.ControlCoords)
|
|
if not Config.MainLights.AutoMode then
|
|
if dist < (Config.MainLights.ControlDistance or 1.5) and not isNearControl then
|
|
isNearControl = true
|
|
if playerAllowed then lib.showTextUI("[E] Control Weigh Lights") end
|
|
elseif dist >= (Config.MainLights.ControlDistance or 1.5) and isNearControl then
|
|
isNearControl = false
|
|
lib.hideTextUI()
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(0)
|
|
if not Config.MainLights.AutoMode and isNearControl and playerAllowed and IsControlJustPressed(0, 38) then
|
|
openControlMenu()
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- AUTO MODE (SINGLE LANE)
|
|
-- ============================================================
|
|
local laneTriggerCenters = {
|
|
LaneA = vector3(2905.2993, 4171.1538, 50.2786)
|
|
}
|
|
local laneRadius = 1.0
|
|
local leaveThreshold = 8.0
|
|
|
|
local function isVehicleAllowed(vehicle)
|
|
if not DoesEntityExist(vehicle) then return false end
|
|
local modelName = tostring(GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) or ""):lower()
|
|
local vClass = GetVehicleClass(vehicle)
|
|
if Config.RampLight.AllowedModels then
|
|
for _, m in ipairs(Config.RampLight.AllowedModels) do
|
|
if modelName == tostring(m):lower() then return true end
|
|
end
|
|
end
|
|
return Config.RampLight.AllowedClasses[vClass] ~= nil
|
|
end
|
|
|
|
local function startLaneAutoThread()
|
|
local track = autoTrack.LaneA
|
|
if track.detectionThread then return end
|
|
|
|
track.detectionThread = CreateThread(function()
|
|
while true do
|
|
Wait(400)
|
|
if isInRange then
|
|
local veh = GetVehiclePedIsIn(PlayerPedId(), false)
|
|
if veh ~= 0 and isVehicleAllowed(veh) then
|
|
local dist = #(GetEntityCoords(veh) - laneTriggerCenters.LaneA)
|
|
if dist <= laneRadius and not track.detected then
|
|
track.detected = true
|
|
track.lastVeh = veh
|
|
TriggerServerEvent("jd_weighlight:autoTrigger", "LaneA")
|
|
elseif dist > leaveThreshold and track.detected then
|
|
track.detected = false
|
|
track.lastVeh = nil
|
|
TriggerServerEvent("jd_weighlight:setLaneState", "LaneA", true)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
if Config.MainLights.AutoMode then
|
|
CreateThread(function()
|
|
Wait(2000)
|
|
startLaneAutoThread()
|
|
end)
|
|
end |