adding new mlos

This commit is contained in:
KingMcDonalds
2026-02-27 01:01:45 -08:00
parent 66ed739664
commit 1e8170f614
402 changed files with 2237 additions and 196 deletions
@@ -0,0 +1,7 @@
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
author 'John Chin'
description 'Weigh Station Shared Props'
version '1.0.0'
@@ -0,0 +1,189 @@
-- =============================================
-- Optimized Weigh Station 03 Lights
-- =============================================
local redLightModel, greenLightModel = `jd_weighstation_lightred`, `jd_weighstation_lightgreen`
local activeLights03 = { LaneA = {}, LaneB = {} }
local laneStates03 = { LaneA = nil, LaneB = nil }
local isInRange03, playerAllowed03 = false, false
local autoTrack03 = {
LaneA = { detected = false, thread = nil },
LaneB = { detected = false, thread = nil }
}
local laneTriggerCenters03 = {
LaneA = vector3(-2481.8938, 3854.9802, 20.4138),
LaneB = vector3(-2489.8101, 3858.9575, 20.4138)
}
local laneRadius03, leaveThreshold03 = 4.0, 10.0
-- Localized natives
local PlayerPedId, GetEntityCoords, GetVehiclePedIsIn, GetPedInVehicleSeat, GetVehicleClass, GetEntityModel =
PlayerPedId, GetEntityCoords, GetVehiclePedIsIn, GetPedInVehicleSeat, GetVehicleClass, GetEntityModel
local DoesEntityExist, CreateObjectNoOffset, SetEntityCoordsNoOffset, SetEntityRotation, FreezeEntityPosition, SetEntityInvincible, DeleteEntity =
DoesEntityExist, CreateObjectNoOffset, SetEntityCoordsNoOffset, SetEntityRotation, FreezeEntityPosition, SetEntityInvincible, DeleteEntity
local HasModelLoaded, RequestModel, GetGameTimer, SetEntityAsMissionEntity, FindFirstObject, FindNextObject, EndFindObject =
HasModelLoaded, RequestModel, GetGameTimer, SetEntityAsMissionEntity, FindFirstObject, FindNextObject, EndFindObject
-- ============================================================
-- Cleanup existing lights
-- ============================================================
local function ForceCleanUp03()
for _, model in ipairs({redLightModel, greenLightModel}) do
local handle, obj = FindFirstObject()
local success
repeat
if GetEntityModel(obj) == model then
SetEntityAsMissionEntity(obj,true,true)
DeleteEntity(obj)
end
success, obj = FindNextObject(handle)
until not success
EndFindObject(handle)
end
end
-- ============================================================
-- Model loading helper
-- ============================================================
local function loadModel03(model)
if HasModelLoaded(model) then return end
RequestModel(model)
local t0 = GetGameTimer()
while not HasModelLoaded(model) do
Wait(10)
if GetGameTimer() - t0 > 5000 then RequestModel(model) end
end
end
-- ============================================================
-- Spawn / Clear lane lights
-- ============================================================
local function clearLaneObjects03(lane)
for _, obj in ipairs(activeLights03[lane]) do
if DoesEntityExist(obj) then
SetEntityAsMissionEntity(obj,true,true)
DeleteEntity(obj)
end
end
activeLights03[lane] = {}
end
local function spawnLaneLight03(lane, model)
if not Config.MainLights.Lanes[lane] then return end
clearLaneObjects03(lane)
loadModel03(model)
for _, light in ipairs(Config.MainLights.Lanes[lane].Lights) do
local pos = light.coords
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(activeLights03[lane], obj)
end
end
local function refreshLane03(lane)
local state = laneStates03[lane]
if state == nil then
clearLaneObjects03(lane)
else
spawnLaneLight03(lane, state and greenLightModel or redLightModel)
end
end
-- ============================================================
-- Events
-- ============================================================
RegisterNetEvent("jd_weighlight03:setLaneState", function(lane, state)
laneStates03[lane] = state
if isInRange03 then refreshLane03(lane) end
end)
-- Initial setup
CreateThread(function()
ForceCleanUp03()
Wait(1000)
TriggerServerEvent("jd_weighlight03:requestState")
if Config.ACL.UseACL then
TriggerServerEvent("jd_weighlight03:checkPermission")
else
playerAllowed03 = true
end
end)
-- ============================================================
-- AutoMode detection
-- ============================================================
local function isVehicleAllowed03(vehicle)
if not DoesEntityExist(vehicle) then return false end
local model, vClass = GetEntityModel(vehicle), GetVehicleClass(vehicle)
for _, m in ipairs(Config.RampLight.AllowedModels) do
if model == GetHashKey(m) then return true end
end
return Config.RampLight.AllowedClasses and Config.RampLight.AllowedClasses[vClass]
end
local function startLaneAutoThread03(lane)
local track = autoTrack03[lane]
if not track or track.thread then return end
track.thread = CreateThread(function()
while true do
Wait(400)
if not isInRange03 then goto continue end
local ped = PlayerPedId()
local veh = GetVehiclePedIsIn(ped,false)
if veh ~= 0 and GetPedInVehicleSeat(veh,-1) == ped and isVehicleAllowed03(veh) then
local dist = #(GetEntityCoords(veh) - laneTriggerCenters03[lane])
if dist <= laneRadius03 and not track.detected then
track.detected = true
TriggerServerEvent("jd_weighlight03:autoTrigger", lane)
elseif dist > leaveThreshold03 and track.detected then
track.detected = false
TriggerServerEvent("jd_weighlight03:setLaneState", lane,true)
end
end
::continue::
end
end)
end
if Config.MainLights and Config.MainLights.AutoMode then
CreateThread(function()
Wait(2000)
startLaneAutoThread03("LaneA")
startLaneAutoThread03("LaneB")
end)
end
-- ============================================================
-- Spawn/Despawn range check
-- ============================================================
CreateThread(function()
while true do
Wait(1000)
local pCoords = GetEntityCoords(PlayerPedId())
local nearAny = false
for _, lane in pairs(Config.MainLights.Lanes) do
if #(pCoords - lane.Lights[1].coords) < 200.0 then
nearAny = true
break
end
end
if nearAny and not isInRange03 then
isInRange03 = true
TriggerServerEvent("jd_weighlight03:requestState")
elseif not nearAny and isInRange03 then
isInRange03 = false
clearLaneObjects03("LaneA")
clearLaneObjects03("LaneB")
end
end
end)
@@ -0,0 +1,82 @@
-- =============================================
-- Optimized Station 03 Lane Logic
-- =============================================
local laneStates03 = {
LaneA = Config.MainLights and Config.MainLights.AutoMode and true or nil,
LaneB = Config.MainLights and Config.MainLights.AutoMode and true or nil
}
local autoTimers03 = { LaneA = false, LaneB = false }
local UseACL = Config.ACL and Config.ACL.UseACL
local aceMainLights = Config.ACL and Config.ACL.MainLights
-- Localize functions
local TriggerClientEvent, IsPlayerAceAllowed = TriggerClientEvent, IsPlayerAceAllowed
local CreateThread, Wait = CreateThread, Wait
-- ============================================================
-- Send current lane states to requesting player
-- ============================================================
RegisterNetEvent("jd_weighlight03:requestState", function()
local src = source
for lane, state in pairs(laneStates03) do
TriggerClientEvent("jd_weighlight03:setLaneState", src, lane, state)
end
end)
-- ============================================================
-- Set lane state (with ACL check)
-- ============================================================
RegisterNetEvent("jd_weighlight03:setLaneState", function(lane, state)
local src = source
-- Validate lane
if laneStates03[lane] == nil then
print(("[weigh_lights03] Invalid lane name from player %d: %s"):format(src, tostring(lane)))
return
end
-- ACL check
if UseACL and not IsPlayerAceAllowed(src, aceMainLights) then
print(("[weigh_lights03] Player %d tried to edit lane %s without permission!"):format(src, lane))
return
end
-- Update and sync
laneStates03[lane] = state
TriggerClientEvent("jd_weighlight03:setLaneState", -1, lane, state)
end)
-- ============================================================
-- AutoMode trigger
-- ============================================================
RegisterNetEvent("jd_weighlight03:autoTrigger", function(lane)
if not Config.MainLights.AutoMode or laneStates03[lane] == nil or autoTimers03[lane] then return end
-- Force RED immediately
laneStates03[lane] = false
TriggerClientEvent("jd_weighlight03:setLaneState", -1, lane, false)
-- Start reset timer
autoTimers03[lane] = true
CreateThread(function()
Wait(30000)
autoTimers03[lane] = false
-- Only reset to GREEN if still RED
if laneStates03[lane] == false then
laneStates03[lane] = true
TriggerClientEvent("jd_weighlight03:setLaneState", -1, lane, true)
end
end)
end)
-- ============================================================
-- Check ACL permission
-- ============================================================
RegisterNetEvent("weigh_lights03:checkPermissionMain", function()
local src = source
local allowed = not UseACL or IsPlayerAceAllowed(src, aceMainLights)
TriggerClientEvent("weigh_lights03:permissionResultMain", src, allowed)
end)
@@ -0,0 +1,111 @@
-- WeighStation03/RampLight/client.lua
local spawnedObjects03 = {}
local lastState03 = nil
-- ============================================================
-- FIX: HELPER FUNCTIONS (NOW INCLUDED)
-- ============================================================
local function listContains(list, hash)
for _, v in ipairs(list or {}) do
if GetHashKey(v) == hash then return true end
end
return false
end
local function vehicleIsAllowed(veh)
local model = GetEntityModel(veh)
local vClass = GetVehicleClass(veh)
if listContains(Config.RampLight.AllowedModels, model) then return true end
return (Config.RampLight and Config.RampLight.AllowedClasses and Config.RampLight.AllowedClasses[vClass])
end
-- ============================================================
-- CLEANUP & SPAWNING
-- ============================================================
local function ForceCleanUp03()
local models = { GetHashKey(Config.RampLight.ModelRed), GetHashKey(Config.RampLight.ModelGreen) }
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
RegisterNetEvent("jd_ramplight03:updateLights", function(state)
-- Clean up Station 03 lights only
for _, obj in ipairs(spawnedObjects03) do
if DoesEntityExist(obj) then DeleteEntity(obj) end
end
spawnedObjects03 = {}
if state == nil then return end
RequestModel(Config.RampLight.ModelGreen)
RequestModel(Config.RampLight.ModelRed)
while not HasModelLoaded(Config.RampLight.ModelGreen) or not HasModelLoaded(Config.RampLight.ModelRed) do Wait(0) end
local posRed = Config.RampLight.RedCoords
local posGreen = Config.RampLight.GreenCoords
local function finalize03(obj, rot)
if obj then
SetEntityRotation(obj, rot.x, rot.y, rot.z, 2, true)
FreezeEntityPosition(obj, true)
SetEntityInvincible(obj, true)
table.insert(spawnedObjects03, obj)
end
end
if state == "heavy" then
local rObj = CreateObjectNoOffset(Config.RampLight.ModelRed, posRed.x, posRed.y, posRed.z, false, false, false)
local gObj = CreateObjectNoOffset(Config.RampLight.ModelGreen, posGreen.x, posGreen.y, posGreen.z, false, false, false)
finalize03(rObj, Config.RampLight.RedRotation)
finalize03(gObj, Config.RampLight.GreenRotation)
elseif state == "light" then
local gObj = CreateObjectNoOffset(Config.RampLight.ModelGreen, posRed.x, posRed.y, posRed.z, false, false, false)
local rObj = CreateObjectNoOffset(Config.RampLight.ModelRed, posGreen.x, posGreen.y, posGreen.z, false, false, false)
finalize03(gObj, Config.RampLight.RedRotation)
finalize03(rObj, Config.RampLight.GreenRotation)
end
end)
-- ============================================================
-- DETECTION THREAD (STATION 03)
-- ============================================================
CreateThread(function()
ForceCleanUp03()
while true do
Wait(500)
local ped = PlayerPedId()
local pCoords = GetEntityCoords(ped)
local veh = GetVehiclePedIsIn(ped, false)
local dist = #(pCoords - Config.RampLight.CenterPoint)
local state = nil
if veh ~= 0 and GetPedInVehicleSeat(veh, -1) == ped and vehicleIsAllowed(veh) then
if dist <= Config.RampLight.DetectionDistance then
state = "light"
local hasTrailer, trailer = GetVehicleTrailerVehicle(veh)
if hasTrailer and trailer then
local tModel = GetEntityModel(trailer)
if listContains(Config.RampLight.HeavyModels, tModel) then
state = "heavy"
end
end
end
end
if state ~= lastState03 then
lastState03 = state
TriggerServerEvent("jd_ramplight03:requestSync", state, dist)
end
end
end)
@@ -0,0 +1,59 @@
-- WeighStation03/RampLight/server.lua
local currentOwner03 = nil
local ownerDist03 = 999.0
local currentState03 = nil
-- ============================================================
-- SYNC REQUEST (STATION 03)
-- ============================================================
RegisterNetEvent("jd_ramplight03:requestSync")
AddEventHandler("jd_ramplight03:requestSync", function(state, dist)
local src = source
-- Only update if no one owns it, or this player is closer to Station 03
if currentOwner03 == nil or src == currentOwner03 or dist < ownerDist03 then
currentOwner03 = src
ownerDist03 = dist
currentState03 = state
-- Broadcast specifically to Station 03 listeners
TriggerClientEvent("jd_ramplight03:updateLights", -1, currentState03)
end
end)
-- ============================================================
-- OWNER VALIDATION LOOP
-- ============================================================
CreateThread(function()
while true do
Wait(2000)
if currentOwner03 then
local ped = GetPlayerPed(currentOwner03)
-- Check if player still exists and is near Station 03 coordinates
if ped == 0 then
currentOwner03 = nil
TriggerClientEvent("jd_ramplight03:updateLights", -1, nil)
else
local coords = GetEntityCoords(ped)
local dist = #(coords - Config.RampLight.CenterPoint)
if dist > Config.RampLight.DetectionDistance then
currentOwner03 = nil
ownerDist03 = 999.0
currentState03 = nil
TriggerClientEvent("jd_ramplight03:updateLights", -1, nil)
else
ownerDist03 = dist
end
end
end
end
end)
AddEventHandler('playerDropped', function()
if source == currentOwner03 then
currentOwner03 = nil
ownerDist03 = 999.0
TriggerClientEvent("jd_ramplight03:updateLights", -1, nil)
end
end)
@@ -0,0 +1,145 @@
-- =============================================
-- Station 03 Amber Lights Client Logic
-- =============================================
local lightSets03, playerAllowed03 = {}, false
-- Localize natives
local CreateThread, Wait, table_insert = CreateThread, Wait, table.insert
local IsControlJustPressed, vector3 = IsControlJustPressed, vector3
local CreateObject, SetEntityRotation, SetEntityVisible, FreezeEntityPosition, DoesEntityExist, DeleteEntity =
CreateObject, SetEntityRotation, SetEntityVisible, FreezeEntityPosition, DoesEntityExist, DeleteEntity
-- ============================================================
-- REQUEST PERMISSION (Station 03)
-- ============================================================
CreateThread(function()
if Config.ACL.UseACL then
TriggerServerEvent("weigh_lights03:checkPermission")
else
playerAllowed03 = true
end
end)
RegisterNetEvent("weigh_lights03:permissionResult", function(allowed)
playerAllowed03 = allowed
end)
-- ============================================================
-- LOAD LIGHT SETS (Station 03)
-- ============================================================
CreateThread(function()
for _, cfg in ipairs(Config.RoadSigns.LightSets) do
table_insert(lightSets03, {
name = cfg.name,
model = cfg.model,
coordsA = cfg.coordsA,
coordsB = cfg.coordsB,
rotA = cfg.rotA or vector3(0,0,0),
rotB = cfg.rotB or vector3(0,0,0),
control = cfg.control,
distance = cfg.distance or 2.0,
propA = nil,
propB = nil,
flashing = false,
pointCreated = false
})
end
end)
-- ============================================================
-- SYNC LOGIC (Station 03)
-- ============================================================
CreateThread(function()
Wait(1000)
TriggerServerEvent("weigh_lights03:requestSync")
end)
RegisterNetEvent("weigh_lights03:syncAll", function(states)
for name, state in pairs(states) do
for _, set in ipairs(lightSets03) do
if set.name == name then
if state then StartFlashing03(set) else StopFlashing03(set) end
end
end
end
end)
RegisterNetEvent("weigh_lights03:syncSingle", function(setName, newState)
for _, set in ipairs(lightSets03) do
if set.name == setName then
if newState then StartFlashing03(set) else StopFlashing03(set) end
end
end
end)
-- ============================================================
-- FLASHING LOGIC (Station 03)
-- ============================================================
function StartFlashing03(set)
if set.flashing then return end
set.flashing = true
CreateThread(function()
local zOffset = -0.09
set.propA = CreateObject(set.model, set.coordsA.x, set.coordsA.y, set.coordsA.z + zOffset, false, false, false)
set.propB = CreateObject(set.model, set.coordsB.x, set.coordsB.y, set.coordsB.z + zOffset, false, false, false)
SetEntityRotation(set.propA, set.rotA.x, set.rotA.y, set.rotA.z, 2, true)
SetEntityRotation(set.propB, set.rotB.x, set.rotB.y, set.rotB.z, 2, true)
FreezeEntityPosition(set.propA, true)
FreezeEntityPosition(set.propB, true)
local visible = true
while set.flashing do
visible = not visible
SetEntityVisible(set.propA, visible, false)
SetEntityVisible(set.propB, not visible, false)
Wait(500)
end
if DoesEntityExist(set.propA) then DeleteEntity(set.propA) end
if DoesEntityExist(set.propB) then DeleteEntity(set.propB) end
set.propA, set.propB = nil, nil
end)
end
function StopFlashing03(set)
set.flashing = false
end
-- ============================================================
-- INTERACTION (E-KEY) (Station 03)
-- ============================================================
CreateThread(function()
while #lightSets03 == 0 do Wait(100) end
if Config.ACL.UseACL then
while not playerAllowed03 do Wait(100) end
end
for _, set in ipairs(lightSets03) do
if set.control and not set.pointCreated then
local point = lib.points.new(set.control, set.distance)
set.pointCreated = true
function point:nearby()
if Config.ACL.UseACL and not playerAllowed03 then
lib.hideTextUI()
return
end
if self.currentDistance < set.distance then
lib.showTextUI("[E] Toggle Amber Light: " .. set.name)
if IsControlJustPressed(0, 38) then
TriggerServerEvent("weigh_lights03:toggleLight", set.name, not set.flashing)
end
else
lib.hideTextUI()
end
end
function point:onExit()
lib.hideTextUI()
end
end
end
end)
@@ -0,0 +1,41 @@
-- =============================================
-- Station 03 Amber Lights Server Logic
-- =============================================
local lightStates03 = {}
-- Localize functions
local TriggerClientEvent, IsPlayerAceAllowed, print = TriggerClientEvent, IsPlayerAceAllowed, print
-- ============================================================
-- REQUEST SYNC (Station 03)
-- ============================================================
RegisterNetEvent("weigh_lights03:requestSync", function()
TriggerClientEvent("weigh_lights03:syncAll", source, lightStates03)
end)
-- ============================================================
-- TOGGLE LIGHT (Station 03)
-- ============================================================
RegisterNetEvent("weigh_lights03:toggleLight", function(setName, newState)
local src = source
if Config.ACL.UseACL and not IsPlayerAceAllowed(src, Config.ACL.AmberLights) then
print(("[weigh_lights03] Player %d tried to toggle light '%s' without permission!"):format(src, setName))
return
end
-- Update only if the state actually changed
if lightStates03[setName] ~= newState then
lightStates03[setName] = newState
TriggerClientEvent("weigh_lights03:syncSingle", -1, setName, newState)
end
end)
-- ============================================================
-- CHECK PERMISSION (Station 03)
-- ============================================================
RegisterNetEvent("weigh_lights03:checkPermission", function()
local src = source
local allowed = not Config.ACL.UseACL or IsPlayerAceAllowed(src, Config.ACL.AmberLights)
TriggerClientEvent("weigh_lights03:permissionResult", src, allowed)
end)
@@ -0,0 +1,194 @@
-- =============================================
-- Optimized Client logic for weigh station 03
-- =============================================
local uiOpen = false
local checkDistance = 12.0
local truckWeights = {}
local truckTrailers = {}
local lastLaneWeight = {}
local serverCallbacks03 = {}
local laneCenters = {}
-- FIXED: Localize frequently used natives correctly (Removed illegal # operator)
local GetEntityCoords = GetEntityCoords
local PlayerPedId = PlayerPedId
local DoesEntityExist = DoesEntityExist
local GetEntityModel = GetEntityModel
local GetVehicleTrailerVehicle = GetVehicleTrailerVehicle
local GetVehicleNumberPlateText = GetVehicleNumberPlateText
local GetGamePool = GetGamePool
-- =========================
-- Helper: Server callback
-- =========================
function TriggerServerCallback03(name, cb, ...)
local id = math.random(1, 999999)
serverCallbacks03[id] = cb
TriggerServerEvent(name, id, ...)
end
RegisterNetEvent("weighstation03:serverCallback", function(id, ...)
if serverCallbacks03[id] then
serverCallbacks03[id](...)
serverCallbacks03[id] = nil
end
end)
-- =========================
-- Helpers & Initializations
-- =========================
local function IsModelInList(modelHash, list)
if not list then return false end
for i = 1, #list do
if GetHashKey(list[i]) == modelHash then return true end
end
return false
end
-- Pre-calculate lane centers once to save CPU during loops
CreateThread(function()
while not Config or not Config.MainLights do Wait(100) end
for laneName, laneData in pairs(Config.MainLights.Lanes) do
lastLaneWeight[laneName] = nil
local sum = vector3(0.0, 0.0, 0.0)
local count = 0
for _, light in ipairs(laneData.Lights) do
sum = sum + light.coords
count = count + 1
end
laneCenters[laneName] = sum / count
end
end)
-- =========================
-- Control Point Detection (Dynamic Sleep)
-- =========================
CreateThread(function()
while true do
local sleep = 1000 -- Idle sleep
local ped = PlayerPedId()
local pos = GetEntityCoords(ped)
local controlCoords = Config.MainLights.ControlCoords
local dist = #(pos - controlCoords)
if dist < 10.0 then
sleep = 0 -- Wake up for input detection
if dist < (Config.MainLights.ControlDistance or 2.0) then
SendNUIMessage({ action = "showPopup", text = "[G] Open Weigh Station 03" })
if IsControlJustPressed(0, 47) and not uiOpen then
uiOpen = true
SetNuiFocus(true, true)
SendNUIMessage({ action = "showUI" })
TriggerServerCallback03("weighstation03:getRecentVehicles", function(recent)
SendNUIMessage({ action = "updateRecentVehicles", recent = recent })
end)
end
else
SendNUIMessage({ action = "hidePopup" })
end
end
Wait(sleep)
end
end)
-- =========================
-- MAIN TRUCK DETECTION (Low Resmon)
-- =========================
CreateThread(function()
while true do
-- Check distance to the station center to decide if we even need to scan vehicles
local ped = PlayerPedId()
local pPos = GetEntityCoords(ped)
local distToStation = #(pPos - vector3(-2487.0051, 3855.9683, 20.7139)) -- Station center
if distToStation > 150.0 then
-- Far away: Sleep for 3 seconds
Wait(3000)
else
-- Near station: Scan every 1 second
Wait(1000)
local vehicles = GetGamePool('CVehicle')
local tickResults = {}
local activeVehiclesThisTick = {}
for laneName, _ in pairs(Config.MainLights.Lanes) do
tickResults[laneName] = { weight = 0, truck = nil, minDist = checkDistance + 0.1 }
end
for i = 1, #vehicles do
local vehicle = vehicles[i]
if DoesEntityExist(vehicle) then
local model = GetEntityModel(vehicle)
if IsModelInList(model, Config.RampLight.AllowedModels) then
local vehiclePos = GetEntityCoords(vehicle)
local nearestLaneName = nil
local nearestLaneDist = checkDistance
for laneName, center in pairs(laneCenters) do
local distToLane = #(vehiclePos - center)
if distToLane < nearestLaneDist then
nearestLaneDist = distToLane
nearestLaneName = laneName
end
end
if nearestLaneName then
activeVehiclesThisTick[vehicle] = true
local _, trailer = GetVehicleTrailerVehicle(vehicle)
-- Generate weight logic
if not truckWeights[vehicle] or truckTrailers[vehicle] ~= trailer then
local plate = GetVehicleNumberPlateText(vehicle)
local truckModelName = GetDisplayNameFromVehicleModel(model)
-- Weight generation
truckWeights[vehicle] = math.random(35000, 100000)
truckTrailers[vehicle] = trailer
TriggerServerEvent("weighstation03:logTruck", plate, truckModelName, truckWeights[vehicle])
end
if nearestLaneDist < tickResults[nearestLaneName].minDist then
tickResults[nearestLaneName].weight = truckWeights[vehicle]
tickResults[nearestLaneName].truck = vehicle
tickResults[nearestLaneName].minDist = nearestLaneDist
end
end
end
end
end
-- Memory Cleanup
for veh, _ in pairs(truckWeights) do
if not DoesEntityExist(veh) or not activeVehiclesThisTick[veh] then
truckWeights[veh] = nil
truckTrailers[veh] = nil
end
end
-- Update UI
for laneName, data in pairs(tickResults) do
if lastLaneWeight[laneName] ~= data.weight then
lastLaneWeight[laneName] = data.weight
SendNUIMessage({
action = "updateWeight",
lane = laneName,
weight = data.weight,
vehicle = data.truck or nil
})
end
end
end
end
end)
RegisterNUICallback('closeUI', function(data, cb)
uiOpen = false
SetNuiFocus(false, false)
SendNUIMessage({ action = "hideUI" })
cb('ok')
end)
@@ -0,0 +1,101 @@
document.addEventListener("DOMContentLoaded", () => {
const win = document.getElementById("browser-window");
const closeBtn = document.getElementById("close-btn");
// Lane status
const statusLaneA = document.getElementById("status-laneA");
const statusLaneB = document.getElementById("status-laneB");
// Recent vehicles log
const recentVehicles = document.getElementById("recent-vehicles");
// Totals / Summary
const totalTrucksEl = document.getElementById("total-trucks");
const avgWeightEl = document.getElementById("avg-weight");
const overweightCountEl = document.getElementById("overweight-count");
// Popup
const popup = document.createElement("div");
popup.id = "popup";
popup.style.position = "fixed";
popup.style.top = "20px";
popup.style.left = "20px";
popup.style.padding = "10px 20px";
popup.style.backgroundColor = "#000080";
popup.style.color = "#fff";
popup.style.fontFamily = "MS Sans Serif, Arial, sans-serif";
popup.style.fontSize = "14px";
popup.style.fontWeight = "bold";
popup.style.border = "2px solid #000";
popup.style.display = "none";
popup.style.zIndex = "9999";
popup.style.borderRadius = "4px";
document.body.appendChild(popup);
// Tracking
let recentVehiclesData = [];
let totalTrucks = 0;
let totalWeight = 0;
let overweightCount = 0;
let loggedVehicles = {}; // track by unique vehicle ID
window.addEventListener("message", (event) => {
const data = event.data;
if (data.action === "showUI") {
win.style.display = "block";
document.body.style.cursor = "default";
} else if (data.action === "hideUI") {
win.style.display = "none";
document.body.style.cursor = "none";
} else if (data.action === "showPopup") {
popup.innerText = data.text;
popup.style.display = "block";
} else if (data.action === "hidePopup") {
popup.style.display = "none";
} else if (data.action === "updateWeight") {
const laneWeightEl = data.lane === "LaneA" ? document.getElementById("laneA-weight") : document.getElementById("laneB-weight");
laneWeightEl.innerText = data.weight ? data.weight + " LBS" : "-- LBS";
const statusEl = data.lane === "LaneA" ? statusLaneA : statusLaneB;
if (data.weight) {
statusEl.innerText = data.weight > 80000 ? `${data.lane}: ⚠️ Overweight` : `${data.lane}: Occupied`;
} else {
statusEl.innerText = `${data.lane}: Open`;
}
// Only log new vehicles once globally
if (data.vehicle && data.weight && !loggedVehicles[data.vehicle]) {
loggedVehicles[data.vehicle] = true;
totalTrucks++;
totalWeight += data.weight;
if (data.weight > 80000) overweightCount++;
totalTrucksEl.innerText = totalTrucks;
avgWeightEl.innerText = Math.floor(totalWeight / totalTrucks);
overweightCountEl.innerText = overweightCount;
const entry = `${new Date().toLocaleTimeString()} - ${data.lane} - ${data.weight} LBS`;
recentVehiclesData.unshift(entry);
if (recentVehiclesData.length > 10) recentVehiclesData.pop();
recentVehicles.innerHTML = recentVehiclesData.map(e => `<li>${e}</li>`).join("");
}
// Remove from logged if lane is empty
if (!data.weight && data.vehicle) {
loggedVehicles[data.vehicle] = false;
}
}
});
closeBtn.addEventListener("click", () => {
fetch(`https://${GetParentResourceName()}/closeUI`, {
method: 'POST',
body: JSON.stringify({})
}).finally(() => {
win.style.display = "none";
document.body.style.cursor = "none";
});
});
});
@@ -0,0 +1,116 @@
body {
font-family: "MS Sans Serif", Arial, sans-serif;
background-color: transparent;
margin: 0;
padding: 0;
}
/* Browser window */
#browser-window {
width: 800px;
height: 600px;
background-color: #c0c0c0;
border: 3px solid #000;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: none;
box-shadow: 7px 7px 0px #000;
padding: 0;
overflow: hidden;
}
/* Top bar */
#browser-topbar {
height: 30px;
background-color: #000080;
color: #fff;
padding: 6px;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
font-size: 14px;
}
/* URL tab */
#tab {
font-weight: normal;
background-color: #c0c0c0;
color: #000;
padding: 4px 8px;
border: 2px inset #fff;
margin-left: 4px;
font-size: 13px;
}
/* Close button */
#close-btn {
width: 25px;
height: 25px;
cursor: pointer;
font-weight: bold;
}
#close-btn:hover {
background: #ff0000;
color: #fff;
}
/* Menu buttons */
#browser-menu {
display: flex;
height: 30px;
background-color: #c0c0c0;
border-bottom: 2px inset #fff;
}
.menu-btn {
border: 2px outset #fff;
margin: 2px;
padding: 4px 10px;
background-color: #c0c0c0;
cursor: pointer;
font-size: 13px;
}
.menu-btn:active {
border-style: inset;
}
/* Content area */
#browser-content {
background-color: #e0e0e0;
padding: 30px;
font-size: 16px;
border-top: 2px solid #fff;
border-bottom: 2px solid #000;
height: calc(100% - 30px - 30px);
overflow-y: auto;
}
/* Lane boxes */
.lane {
margin-top: 15px;
padding: 10px;
border: 2px inset #fff;
background-color: #c0c0c0;
font-size: 16px;
}
.lane-name {
font-weight: bold;
}
/* Lane status, log, summary */
.lane-status, .log, .summary {
margin-top: 20px;
padding: 10px;
border: 2px inset #fff;
background-color: #c0c0c0;
}
.lane-status h3, .log h3, .summary h3 {
margin: 0 0 5px 0;
font-size: 16px;
}
.log ul {
padding-left: 20px;
margin: 0;
}
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Weigh Station UI</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="browser-window">
<!-- Top bar -->
<div id="browser-topbar">
<span id="tab">www.sanandreasdot.gov/panel</span>
<button id="close-btn">X</button>
</div>
<!-- Menu buttons -->
<div id="browser-menu">
<button class="menu-btn">File</button>
<button class="menu-btn">Edit</button>
<button class="menu-btn">View</button>
<button class="menu-btn">Help</button>
</div>
<!-- Main content -->
<div id="browser-content">
<h2>Weigh Station Panel</h2>
<!-- Lanes -->
<div class="lane">
<span class="lane-name">Lane A:</span> <span id="laneA-weight">-- LBS</span>
</div>
<div class="lane">
<span class="lane-name">Lane B:</span> <span id="laneB-weight">-- LBS</span>
</div>
<!-- Lane Status -->
<div class="lane-status">
<h3>Lane Status</h3>
<div id="status-laneA">Lane A: Open</div>
<div id="status-laneB">Lane B: Open</div>
</div>
<!-- Recent Vehicles Log -->
<div class="log">
<h3>Recent Vehicles</h3>
<ul id="recent-vehicles">
<!-- dynamically updated -->
</ul>
</div>
<!-- Totals / Summary -->
<div class="summary">
<h3>Summary</h3>
<div>Total Trucks Today: <span id="total-trucks">0</span></div>
<div>Average Weight: <span id="avg-weight">0</span> LBS</div>
<div>Overweight Trucks: <span id="overweight-count">0</span></div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
@@ -0,0 +1,30 @@
local lightStates03 = {}
-- ============================================================
-- OPTIMIZED SYNC
-- ============================================================
RegisterNetEvent("weigh_lights03:requestSync", function()
local src = source
TriggerClientEvent("weigh_lights03:syncAll", src, lightStates03)
end)
RegisterNetEvent("weigh_lights03:toggleLight", function(setName, newState)
local src = source
if Config.ACL.UseACL then
if not IsPlayerAceAllowed(src, Config.ACL.AmberLights) then
return
end
end
lightStates03[setName] = newState
-- Only broadcast to others to save bandwidth
TriggerClientEvent("weigh_lights03:syncSingle", -1, setName, newState)
end)
-- Optimized Permission Check
RegisterNetEvent("weigh_lights03:checkPermission", function()
local src = source
local allowed = not Config.ACL.UseACL or IsPlayerAceAllowed(src, Config.ACL.AmberLights)
TriggerClientEvent("weigh_lights03:permissionResult", src, allowed)
end)
@@ -0,0 +1,105 @@
Config = {}
-- =============================================
-- ACE PERMISSIONS
-- =============================================
Config.ACL = {
UseACL = false, -- set to false to disable ACE checks allowing anyone to use the light controls
MainLights = "weighstation.mainlights",
AmberLights = "weighstation.amberlights"
}
-- =============================================
-- MAIN WEIGH STATION TRAFFIC LIGHTS
-- =============================================
Config.MainLights = {
AutoMode = true,
Lanes = {
LaneA = {
Lights = {
{
coords = vector3(-2475.057373, 3873.079346, 26.406738),
rotation = vector3(0.0, 0.0, 135.0)
},
{
coords = vector3(-2480.732422, 3852.302246, 24.777773),
rotation = vector3(20.0, -90.0, 150.00)
},
},
},
LaneB = {
Lights = {
{
coords = vector3(-2481.596680, 3876.128662, 26.406738),
rotation = vector3(0.0, 0.0, 135.00)
},
{
coords = vector3(-2487.283691, 3855.302734, 24.777773),
rotation = vector3(20.0, -90.0, 150.00)
},
},
}
},
ControlCoords = vector3(-2516.8596, 3859.7903, 20.6101),
ControlDistance = 1.0,
LightSpawnRange = 200.0,
ResyncTime = 60000
}
-- =============================================
-- ROAD SIGN / AMBER LIGHT SETS
-- =============================================
Config.RoadSigns = {
LightSets = {
{
name = "Great Ocean Hwy S",
model = `jd_weighstation_lightamber`,
coordsA = vector3(-2237.833740, 4307.489746, 50.284489),
rotA = vector3(0.0, 0.0, 145.0),
coordsB = vector3(-2238.857666, 4308.206543, 50.284489),
rotB = vector3(0.0, 0.0, 145.0),
control = vector3(-2238.4390, 4307.7974, 47.8325),
distance = 1.0
}
}
}
-- =============================================
-- WEIGH STATION RAMP LIGHT SYSTEM
-- =============================================
Config.RampLight = {
CenterPoint = vector3(-2441.3560, 3919.4810, 19.9648),
DetectionDistance = 19.0,
ModelGreen = `jd_weightstation03_greencheck`,
ModelRed = `jd_weightstation03_redx`,
RedCoords = vector3(-2432.925049, 3914.801270, 28.507784),
GreenCoords = vector3(-2435.523193, 3916.300537, 28.507784),
RedRotation = vector3(0.0, 0.0, 10.0),
GreenRotation = vector3(0.0, 0.0, 10.0),
AllowedClasses = {
[18] = false,
[20] = false,
},
AllowedModels = { "phantom", "hauler", "packer", "phantom3", "hauler2", "packer2" }, -- ← container truck models
HeavyModels = {
"TRAILER", -- ← container heavy trailers
"TRAILERS", "TRAILER2", "TRAILER3", "TRAILER4",
"trailers", "trailers2", "trailers3", "trailers4",
"freighttrailer", "armytanker", "docktrailer"
},
LightModels = {
"tr2", "tr3", "tr4", "trflat", -- ← container light trailers
"tanker", "tanker2", "trailerlogs",
"tvtrailer", "baletrailer", "graintrailer"
},
}
@@ -0,0 +1,34 @@
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
author 'John Chin'
description 'Weigh Stations (3rd Location)'
version '1.0.0'
shared_scripts {
'@ox_lib/init.lua',
'config.lua'
}
client_scripts {
'MainLights/client.lua',
'RoadSignLights/client.lua',
'RampLight/client.lua',
'WeighUi/client.lua'
}
server_scripts {
'MainLights/server.lua',
'RoadSignLights/server.lua',
'RampLight/server.lua',
'WeighUi/server.lua'
}
ui_page 'WeighUi/html/ui.html'
files {
'WeighUi/html/ui.html',
'WeighUi/html/style.css',
'WeighUi/html/script.js'
}

Some files were not shown because too many files have changed in this diff Show More