-- client/vehicles.lua print("^2[turfwar]^7 client/vehicles.lua LOADED (HQ marker + spawn + doSpawn mutex)") Config = Config or {} Config.Vehicles = Config.Vehicles or {} Config.GANGS = Config.GANGS or {} Config.JOIN_POINTS = Config.JOIN_POINTS or {} local currentGang = 0 local myVehNetId = 0 local myVehBlip = 0 local nextSpawnAt = 0 local myVehGangId = 0 -- GLOBAL mutex shared across ALL client scripts _G.__turfwar_doSpawnBusy = _G.__turfwar_doSpawnBusy or false -- ========================= -- Helpers -- ========================= local function Notify(msg) BeginTextCommandThefeedPost("STRING") AddTextComponentSubstringPlayerName(msg) EndTextCommandThefeedPostTicker(false, false) end local function Draw3DText(x, y, z, text) local onScreen, _x, _y = World3dToScreen2d(x, y, z) if not onScreen then return end SetTextScale(0.35, 0.35) SetTextFont(4) SetTextProportional(1) SetTextEntry("STRING") SetTextCentre(1) AddTextComponentString(text) DrawText(_x, _y) end local function getHQPosForGang(gangId) for _, jp in ipairs(Config.JOIN_POINTS or {}) do if tonumber(jp.gangId) == tonumber(gangId) then return jp.pos end end return nil end local function loadModel(model) local hash = type(model) == "number" and model or GetHashKey(model) if not IsModelInCdimage(hash) then return nil end RequestModel(hash) local timeout = GetGameTimer() + 7000 while not HasModelLoaded(hash) do Wait(10) if GetGameTimer() > timeout then return nil end end return hash end local function getGangBlipColor(gangId) gangId = tonumber(gangId) or 0 local g = Config.GANGS and Config.GANGS[gangId] local c = g and g.blipColor return tonumber(c) or 2 end local function setMyBlipForVehicle(veh) if DoesBlipExist(myVehBlip) then RemoveBlip(myVehBlip) myVehBlip = 0 end if not veh or veh == 0 then return end myVehBlip = AddBlipForEntity(veh) SetBlipSprite(myVehBlip, 225) SetBlipScale(myVehBlip, 0.75) SetBlipColour(myVehBlip, getGangBlipColor(myVehGangId)) -- <- gang color SetBlipAsShortRange(myVehBlip, false) BeginTextCommandSetBlipName("STRING") AddTextComponentString("Gang Vehicle") EndTextCommandSetBlipName(myVehBlip) end -- ========================= -- Gang updates (MATCH YOUR SERVER) -- ========================= local function setGang(gangId) currentGang = tonumber(gangId) or 0 end -- sent directly to the player on change RegisterNetEvent("turfwar:gangUpdate", function(gangId) setGang(gangId) end) -- compatibility event your server also triggers RegisterNetEvent("turfwar:setFaction", function(gangId) setGang(gangId) end) -- broadcast to all clients: (src, gangId) RegisterNetEvent("turfwar:playerGang", function(src, gangId) if src == GetPlayerServerId(PlayerId()) then setGang(gangId) end end) RegisterCommand("tw_gang", function() print("^3[turfwar]^7 currentGang = " .. tostring(currentGang)) Notify("currentGang = " .. tostring(currentGang)) end) -- On resource start, ask server for our current gang via existing requestFaction CreateThread(function() Wait(1000) TriggerServerEvent("turfwar:requestFaction") end) -- ========================= -- Server -> Client spawn -- ========================= RegisterNetEvent("turfwar:veh:doSpawn", function(data) if _G.__turfwar_doSpawnBusy then print("^1[turfwar]^7 Ignoring duplicate doSpawn (global mutex busy)") return end _G.__turfwar_doSpawnBusy = true local function done() _G.__turfwar_doSpawnBusy = false end local ok, err = pcall(function() if type(data) ~= "table" or not data.model then print("^1[turfwar]^7 doSpawn: bad data") done() return end local hash = loadModel(data.model) if not hash then Notify("~r~Vehicle model failed to load.~s~") done() return end local x = tonumber(data.x) or 0.0 local y = tonumber(data.y) or 0.0 local z = tonumber(data.z) or 0.0 local h = tonumber(data.heading) or 0.0 local plate = tostring(data.plate or "GANG") local veh = CreateVehicle(hash, x, y, z, h, true, false) SetModelAsNoLongerNeeded(hash) if not veh or veh == 0 then Notify("~r~Vehicle spawn failed.~s~") done() return end SetVehicleOnGroundProperly(veh) SetVehicleEngineOn(veh, true, true, false) SetVehicleDirtLevel(veh, 0.0) SetVehicleNumberPlateText(veh, plate) -- Apply RGB if provided if data.rgb and type(data.rgb) == "table" then local r = tonumber(data.rgb[1]) or 255 local g = tonumber(data.rgb[2]) or 255 local b = tonumber(data.rgb[3]) or 255 SetVehicleCustomPrimaryColour(veh, r, g, b) SetVehicleCustomSecondaryColour(veh, r, g, b) end local netId = NetworkGetNetworkIdFromEntity(veh) SetNetworkIdCanMigrate(netId, true) TriggerServerEvent("turfwar:veh:spawned", netId) myVehNetId = netId myVehGangId = tonumber(data.gangId) or currentGang or 0 setMyBlipForVehicle(veh) done() end) if not ok then print("^1[turfwar]^7 doSpawn crashed: " .. tostring(err)) _G.__turfwar_doSpawnBusy = false end end) RegisterNetEvent("turfwar:veh:notify", function(msg) Notify(msg) end) RegisterNetEvent("turfwar:veh:setLocal", function(netId, gangId) myVehNetId = tonumber(netId) or 0 myVehGangId = tonumber(gangId) or 0 end) RegisterNetEvent("turfwar:veh:clearLocal", function() myVehNetId = 0 myVehGangId = 0 if DoesBlipExist(myVehBlip) then RemoveBlip(myVehBlip) myVehBlip = 0 end end) -- ========================= -- HQ marker + E to request spawn -- (matches server validation against HQ join point) -- ========================= CreateThread(function() local useKey = (Config.Vehicles and Config.Vehicles.interactKey) or 38 -- E local useDist = (Config.Vehicles and Config.Vehicles.interactDist) or 2.5 local markerDist = (Config.Vehicles and Config.Vehicles.markerDist) or 20.0 while true do Wait(0) if not currentGang or currentGang == 0 then goto continue end local hq = getHQPosForGang(currentGang) if not hq then goto continue end local ped = PlayerPedId() local p = GetEntityCoords(ped) local d = #(p - hq) if d < markerDist then DrawMarker(1, hq.x, hq.y, hq.z - 1.0, 0,0,0, 0,0,0, 1.2,1.2,0.8, 255,255,255, 120, false,true,2, false,nil,nil,false) if d < useDist then Draw3DText(hq.x, hq.y, hq.z + 0.25, "~w~Gang Vehicle~n~~y~[E]~w~ Spawn / Replace") if IsControlJustPressed(0, useKey) then local now = GetGameTimer() if now < nextSpawnAt then goto continue end nextSpawnAt = now + 1200 local pcoords = { x = p.x, y = p.y, z = p.z } TriggerServerEvent("turfwar:veh:requestSpawn", pcoords) end end end ::continue:: end end)