-- server/vehicles.lua print("^2[turfwar]^7 server/vehicles.lua LOADED (config spawn pads)") VehicleSpawner = VehicleSpawner or {} -- ActiveVeh[src] = { netId=number, gangId=number } local ActiveVeh = {} -- PendingSpawn[src] = { gangId=number, model=string, plate=string } local PendingSpawn = {} local function gangFromSrc(src) if PlayerGang and PlayerGang[src] then return tonumber(PlayerGang[src]) or 0 end return 0 end local function getGangRGB(gangId) local g = Config and Config.GANGS and Config.GANGS[gangId] local rgb = g and g.rgb if rgb and rgb[1] and rgb[2] and rgb[3] then return tonumber(rgb[1]) or 255, tonumber(rgb[2]) or 255, tonumber(rgb[3]) or 255 end return 255, 255, 255 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 getSpawnPadForGang(gangId) local sp = Config and Config.GANG_VEHICLE_SPAWNS and Config.GANG_VEHICLE_SPAWNS[gangId] if not sp then return nil end return { x = tonumber(sp.x) or 0.0, y = tonumber(sp.y) or 0.0, z = tonumber(sp.z) or 0.0, h = tonumber(sp.w) or 0.0 } end local function dist(a, b) local dx = (a.x - b.x) local dy = (a.y - b.y) local dz = (a.z - b.z) return math.sqrt(dx*dx + dy*dy + dz*dz) end local function deleteVehicleFor(src) local entry = ActiveVeh[src] if not entry then return end if entry.netId and entry.netId ~= 0 then local e = NetworkGetEntityFromNetworkId(entry.netId) if e and DoesEntityExist(e) then DeleteEntity(e) end end TriggerClientEvent("turfwar:veh:clearLocal", src) ActiveVeh[src] = nil end local function setPendingTimeout(src) SetTimeout(8000, function() if PendingSpawn[src] then PendingSpawn[src] = nil TriggerClientEvent("turfwar:veh:notify", src, "~y~Spawn timed out. Try again.~s~") end end) end -- Called from server/main.lua on gang change: function VehicleSpawner.OnGangChanged(src, oldGang, newGang) oldGang = tonumber(oldGang) or 0 newGang = tonumber(newGang) or 0 if oldGang ~= newGang then deleteVehicleFor(src) end end -- ========================================================= -- EXTERNAL SPAWN (for vehicle shop purchases) -- ========================================================= function VehicleSpawner.SpawnModelFor(src, modelName, opts) opts = opts or {} -- HARD BLOCK: prevent double spawns from double-trigger / spam if PendingSpawn[src] then TriggerClientEvent("turfwar:veh:notify", src, "~y~Spawn already in progress...~s~") return false, "pending" end local gangId = gangFromSrc(src) if gangId == 0 then TriggerClientEvent("turfwar:veh:notify", src, "~r~You are Neutral.~s~") return false, "neutral" end if gangId == 3 then TriggerClientEvent("turfwar:veh:notify", src, "~r~Police can't use this system.~s~") return false, "police" end if type(modelName) ~= "string" or modelName == "" then TriggerClientEvent("turfwar:veh:notify", src, "~r~Bad vehicle model.~s~") return false, "bad_model" end local pad = getSpawnPadForGang(gangId) if not pad then TriggerClientEvent("turfwar:veh:notify", src, "~r~No vehicle spawn pad set for your gang.~s~") print(("^1[turfwar]^7 Missing Config.GANG_VEHICLE_SPAWNS[%s]"):format(gangId)) return false, "no_pad" end if ActiveVeh[src] then deleteVehicleFor(src) end local r, g, b = getGangRGB(gangId) local prefix = opts.platePrefix or ((Config.Vehicles and Config.Vehicles.platePrefix and Config.Vehicles.platePrefix[gangId]) or ("G"..tostring(gangId))) local plate = (prefix .. tostring(src)):sub(1, 8) PendingSpawn[src] = { gangId = gangId, model = modelName, plate = plate } setPendingTimeout(src) print(("^3[turfwar]^7 doSpawn(shop) -> src=%d gang=%d model=%s plate=%s"):format(src, gangId, modelName, plate)) TriggerClientEvent("turfwar:veh:doSpawn", src, { gangId = gangId, model = modelName, x = pad.x, y = pad.y, z = pad.z, heading = pad.h, rgb = { r, g, b }, plate = plate }) return true end RegisterNetEvent("turfwar:veh:spawned", function(netId) local src = source netId = tonumber(netId) or 0 local req = PendingSpawn[src] PendingSpawn[src] = nil if not req then TriggerClientEvent("turfwar:veh:notify", src, "~r~Spawn rejected (no pending request).~s~") return end if netId == 0 then TriggerClientEvent("turfwar:veh:notify", src, "~r~Vehicle spawn failed (netId=0).~s~") return end ActiveVeh[src] = { netId = netId, gangId = req.gangId } TriggerClientEvent("turfwar:veh:setLocal", src, netId, req.gangId) TriggerClientEvent("turfwar:veh:notify", src, "~g~Gang vehicle spawned.~s~") end) AddEventHandler("playerDropped", function() local src = source deleteVehicleFor(src) PendingSpawn[src] = nil end)