Upload files to "client"

This commit is contained in:
tanthius 2026-02-12 04:15:51 +00:00
parent 4910c39102
commit 293cf701c4
5 changed files with 1023 additions and 0 deletions

37
client/money_hud.lua Normal file
View File

@ -0,0 +1,37 @@
print("^2[turfwar]^7 money_hud.lua loaded")
CreateThread(function()
Wait(1000)
SendNUIMessage({ type = "money:show" })
end)
RegisterNetEvent('turfwar:money:update', function(cash, bank)
SendNUIMessage({
type = "money:update",
cash = cash or 0,
bank = bank or 0
})
end)
-- Optional: hide HUD when paused
CreateThread(function()
while true do
Wait(250)
if IsPauseMenuActive() then
SendNUIMessage({ type = "money:hide" })
else
SendNUIMessage({ type = "money:show" })
end
end
end)
AddEventHandler('onClientResourceStart', function(resName)
if resName ~= GetCurrentResourceName() then return end
Wait(500)
TriggerServerEvent('turfwar:money:request')
end)
AddEventHandler('playerSpawned', function()
Wait(500)
TriggerServerEvent('turfwar:money:request')
end)

201
client/player_police.lua Normal file
View File

@ -0,0 +1,201 @@
-- client/player_police.lua
print("^2[turfwar]^7 client/player_police.lua loaded (police tracking)")
local PP = Config.PlayerPolice or {}
local POLICE_GANG_ID = tonumber(PP.POLICE_GANG_ID) or 3
local isPolice = false
local myStars = 0
local myInterior = false
-- targets[serverId] = {stars, interior, lastPos, circleBlip, realtimeBlip, lastPingAt}
local targets = {}
local function vec3(x,y,z) return vector3(x+0.0, y+0.0, z+0.0) end
local function InInterior(ped) return (GetInteriorFromEntity(ped) ~= 0) end
local function ClearBlipSafe(blip)
if blip and DoesBlipExist(blip) then RemoveBlip(blip) end
end
local function EnsureTarget(src)
if not targets[src] then
targets[src] = {
stars = 0,
interior = false,
lastPos = nil,
circleBlip = nil,
realtimeBlip = nil,
lastPingAt = 0
}
end
return targets[src]
end
local function ClearTarget(src)
local t = targets[src]
if not t then return end
ClearBlipSafe(t.circleBlip)
ClearBlipSafe(t.realtimeBlip)
targets[src] = nil
end
local function UpdateVisual(src)
if not isPolice then return end
local t = targets[src]
if not t then return end
local stars = tonumber(t.stars) or 0
local interior = (t.interior == true)
-- 4-5 stars realtime, but interior forces "2-star mode"
local effective = stars
if stars >= 4 and interior then
effective = 2
end
if stars <= 0 then
ClearBlipSafe(t.circleBlip); t.circleBlip = nil
ClearBlipSafe(t.realtimeBlip); t.realtimeBlip = nil
return
end
if not t.lastPos then return end
-- clear visuals that don't match mode
if effective <= 3 then
ClearBlipSafe(t.realtimeBlip); t.realtimeBlip = nil
end
if effective >= 4 then
ClearBlipSafe(t.circleBlip); t.circleBlip = nil
end
if effective == 1 or effective == 2 then
local radius = (effective == 1) and (PP.CIRCLE_RADIUS_1STAR or 220.0) or (PP.CIRCLE_RADIUS_2STAR or 160.0)
ClearBlipSafe(t.circleBlip)
t.circleBlip = AddBlipForRadius(t.lastPos.x, t.lastPos.y, t.lastPos.z, radius)
SetBlipAlpha(t.circleBlip, 90)
elseif effective == 3 then
-- ping exact position every update (client sends every 2s)
local now = GetGameTimer()
if (now - (t.lastPingAt or 0)) > 250 then
t.lastPingAt = now
local blip = AddBlipForCoord(t.lastPos.x, t.lastPos.y, t.lastPos.z)
SetBlipSprite(blip, PP.PING_BLIP_SPRITE or 161)
SetBlipScale(blip, PP.PING_BLIP_SCALE or 1.0)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("Wanted Suspect (Ping)")
EndTextCommandSetBlipName(blip)
CreateThread(function()
Wait(PP.PING_BLIP_LIFETIME_MS or 1500)
ClearBlipSafe(blip)
end)
end
elseif effective >= 4 then
-- realtime blip
if not t.realtimeBlip or not DoesBlipExist(t.realtimeBlip) then
t.realtimeBlip = AddBlipForCoord(t.lastPos.x, t.lastPos.y, t.lastPos.z)
SetBlipSprite(t.realtimeBlip, PP.REALTIME_BLIP_SPRITE or 1)
SetBlipScale(t.realtimeBlip, PP.REALTIME_BLIP_SCALE or 0.9)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("Wanted Suspect")
EndTextCommandSetBlipName(t.realtimeBlip)
else
SetBlipCoords(t.realtimeBlip, t.lastPos.x, t.lastPos.y, t.lastPos.z)
end
end
end
-- Police-only chat from server
RegisterNetEvent('turfwar:pp:chat', function(msg)
if not isPolice then return end
TriggerEvent('chat:addMessage', { args = { msg } })
end)
-- Police-only tracking updates
RegisterNetEvent('turfwar:pp:update', function(offenderSrc, data)
if not isPolice then return end
if type(data) ~= "table" then return end
local t = EnsureTarget(offenderSrc)
if data.type == "mode" then
t.stars = tonumber(data.stars) or 0
t.interior = (data.interior == true)
UpdateVisual(offenderSrc)
return
end
if data.type == "pos" then
t.stars = tonumber(data.stars) or 0
t.interior = (data.interior == true)
t.lastPos = vec3(data.x, data.y, data.z)
UpdateVisual(offenderSrc)
return
end
end)
RegisterNetEvent('turfwar:pp:clear', function(offenderSrc)
ClearTarget(offenderSrc)
end)
-- Detect if THIS player is police (from your existing broadcast)
RegisterNetEvent('turfwar:playerGang', function(src, gangId)
if src ~= GetPlayerServerId(PlayerId()) then return end
isPolice = (tonumber(gangId) or 0) == POLICE_GANG_ID
if not isPolice then
for id, t in pairs(targets) do
ClearBlipSafe(t.circleBlip)
ClearBlipSafe(t.realtimeBlip)
end
targets = {}
end
end)
-- How often offenders send updates (based on their own stars)
local function CurrentInterval(stars)
if stars <= 0 then return 1000 end
if stars == 1 then return PP.UPDATE_MS_1STAR or 5000 end
if stars == 2 then return PP.UPDATE_MS_2STAR or 2000 end
if stars == 3 then return PP.UPDATE_MS_3STAR or 2000 end
return PP.UPDATE_MS_45STAR_REALTIME or 500
end
-- Watch stars/interior changes (all players)
CreateThread(function()
while true do
Wait(500)
local ped = PlayerPedId()
local stars = GetPlayerWantedLevel(PlayerId()) or 0
local interior = InInterior(ped)
if stars ~= myStars or interior ~= myInterior then
myStars = stars
myInterior = interior
TriggerServerEvent('turfwar:pp:starsChanged', myStars, myInterior)
end
end
end)
-- Stream offender position updates (all players with stars)
CreateThread(function()
while true do
Wait(CurrentInterval(myStars))
if myStars > 0 then
local ped = PlayerPedId()
local c = GetEntityCoords(ped)
local h = GetEntityHeading(ped)
local interior = InInterior(ped)
myInterior = interior
TriggerServerEvent('turfwar:pp:posUpdate', myStars, interior, c.x, c.y, c.z, h)
end
end
end)

View File

@ -0,0 +1,136 @@
print("^2[turfwar]^7 police_wanted_escalate.lua loaded (client)")
-- -------------------------
-- Tunables
-- -------------------------
local DMG_POLL_MS = 200
local DMG_COOLDOWN_MS = 2500 -- per killer, per victim
-- -------------------------
-- State
-- -------------------------
local sentThisDeath = false
local lastDeathAt = 0
local dmgCooldown = {} -- [killerSid] = lastSentAt (GetGameTimer ms)
-- -------------------------
-- Helpers
-- -------------------------
local function killerServerIdFromEntity(ent)
if not ent or ent == 0 or not DoesEntityExist(ent) then return 0 end
local killerPed = ent
if IsEntityAVehicle(ent) then
killerPed = GetPedInVehicleSeat(ent, -1)
end
if not killerPed or killerPed == 0 or not DoesEntityExist(killerPed) then return 0 end
if not IsEntityAPed(killerPed) then return 0 end
if not IsPedAPlayer(killerPed) then return 0 end
local idx = NetworkGetPlayerIndexFromPed(killerPed)
if idx == -1 then return 0 end
return tonumber(GetPlayerServerId(idx)) or 0
end
-- Reset death flag when alive again
CreateThread(function()
while true do
Wait(250)
if sentThisDeath and not IsEntityDead(PlayerPedId()) then
sentThisDeath = false
end
end
end)
-- -------------------------
-- KILL detection (keep what works)
-- -------------------------
AddEventHandler('gameEventTriggered', function(name, args)
if name ~= 'CEventNetworkEntityDamage' then return end
local victimEntity = args[1]
local attackerEntity = args[2]
local victimDied = args[6]
if victimEntity ~= PlayerPedId() then return end
if not victimDied then return end
if sentThisDeath then return end
if not IsEntityDead(PlayerPedId()) then return end
local now = GetGameTimer()
if now - lastDeathAt < 1500 then return end
lastDeathAt = now
sentThisDeath = true
local killerSid = killerServerIdFromEntity(attackerEntity)
if killerSid <= 0 then
-- fallback
killerSid = killerServerIdFromEntity(GetEntityLastDamageEntity(PlayerPedId()))
end
if killerSid > 0 then
TriggerServerEvent('turfwar:wanted:policeKilledBy', killerSid)
end
end)
-- -------------------------
-- DAMAGE detection (reliable polling)
-- -------------------------
CreateThread(function()
while true do
Wait(DMG_POLL_MS)
local ped = PlayerPedId()
if ped == 0 or not DoesEntityExist(ped) then goto continue end
if IsEntityDead(ped) then goto continue end
-- Was I damaged by any ped since last clear?
if HasEntityBeenDamagedByAnyPed(ped) then
local lastEnt = GetEntityLastDamageEntity(ped)
local killerSid = killerServerIdFromEntity(lastEnt)
if killerSid > 0 then
local now = GetGameTimer()
local last = dmgCooldown[killerSid] or 0
if (now - last) >= DMG_COOLDOWN_MS then
dmgCooldown[killerSid] = now
TriggerServerEvent('turfwar:wanted:policeDamagedBy', killerSid)
end
end
-- Clear flags so we don't spam forever
ClearEntityLastDamageEntity(ped)
ClearPedLastWeaponDamage(ped)
end
::continue::
end
end)
-- -------------------------
-- Wanted application (killer client)
-- -------------------------
RegisterNetEvent('turfwar:wanted:setMinimum', function(minStars)
local pid = PlayerId()
local current = GetPlayerWantedLevel(pid)
local newStars = math.max(current, tonumber(minStars) or 0)
if newStars ~= current then
ClearPlayerWantedLevel(pid)
SetPlayerWantedLevel(pid, newStars, false)
SetPlayerWantedLevelNow(pid, true)
end
end)
RegisterNetEvent('turfwar:wanted:setEscalated', function(targetStars, count, windowSec)
local pid = PlayerId()
local current = GetPlayerWantedLevel(pid)
local newStars = math.max(current, tonumber(targetStars) or 0)
ClearPlayerWantedLevel(pid)
SetPlayerWantedLevel(pid, newStars, false)
SetPlayerWantedLevelNow(pid, true)
end)

11
client/pvp.lua Normal file
View File

@ -0,0 +1,11 @@
-- client/pvp.lua
-- Ensures PvP stays enabled (some resources toggle it off)
print("^2[turfwar]^7 client/pvp.lua loaded")
CreateThread(function()
while true do
NetworkSetFriendlyFireOption(true)
SetCanAttackFriendly(PlayerPedId(), true, true)
Wait(1000)
end
end)

638
client/shop.lua Normal file
View File

@ -0,0 +1,638 @@
-- client/shop.lua
print("^2[turfwar]^7 client/shop.lua loaded (gang+police shop NUI + markers + blips + shop vehicle ownership + debug)")
local currentGang = 0
local shopOpen = false
local lastOpenAttempt = 0
-- Debug toggles
local DEBUG_MARKERS = false -- /tw_shopdebug
-- Shop blips (map icons)
local ShopBlips = {}
-- Shop vehicle (1 per player) + blip
local myShopVeh = 0
local myShopVehBlip = 0
-- Server sync throttle
local lastSyncedGang = -1
local lastSyncAt = 0
-- =========================
-- Helpers
-- =========================
local function Notify(msg)
BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringPlayerName(msg)
EndTextCommandThefeedPostTicker(false, false)
end
local function v3(pos)
if not pos then return vector3(0.0, 0.0, 0.0) end
return vector3(pos.x + 0.0, pos.y + 0.0, pos.z + 0.0)
end
local function dist(a, b)
local dx = (a.x + 0.0) - (b.x + 0.0)
local dy = (a.y + 0.0) - (b.y + 0.0)
local dz = (a.z + 0.0) - (b.z + 0.0)
return math.sqrt(dx * dx + dy * dy + dz * dz)
end
local function iterList(t)
if type(t) ~= "table" then
return function() return nil end
end
local n = #t
if n > 0 then
local i = 0
return function()
i = i + 1
if i <= n then return i, t[i] end
end
end
return pairs(t)
end
local function getGangRGB(gangId)
local fallback = { r = 10, g = 10, b = 10 }
local gangs = (Config and Config.GANGS) or nil
if type(gangs) ~= "table" then return fallback end
local g = gangs[tonumber(gangId) or 0]
if type(g) ~= "table" then return fallback end
local rgb = g.rgb
if type(rgb) ~= "table" then return fallback end
local r = tonumber(rgb.r or rgb[1])
local gg = tonumber(rgb.g or rgb[2])
local b = tonumber(rgb.b or rgb[3])
if not r or not gg or not b then return fallback end
return { r = r, g = gg, b = b }
end
local function drawMarkerAt(posIn)
local shops = Config.Shops or {}
local m = shops.Marker or {}
local pos = v3(posIn)
local t = tonumber(m.type) or 1
local sc = m.scale
if sc == nil then
sc = vector3(1.2, 1.2, 0.9)
elseif type(sc) == "table" and sc.x == nil then
sc = vector3(
tonumber(sc[1]) or 1.2,
tonumber(sc[2]) or 1.2,
tonumber(sc[3]) or 0.9
)
end
local rgba = m.rgba or { 80, 160, 255, 140 }
local z = pos.z
local found, gz
for _, off in ipairs({ 300.0, 100.0, 30.0, 10.0 }) do
found, gz = GetGroundZFor_3dCoord(pos.x, pos.y, pos.z + off, true)
if found then
z = gz + 0.05
break
end
end
DrawMarker(
t,
pos.x, pos.y, z,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
sc.x, sc.y, sc.z,
rgba[1] or 80,
rgba[2] or 160,
rgba[3] or 255,
rgba[4] or 140,
false, true, 2, false, nil, nil, false
)
end
-- =========================================================
-- NEW: Server->Client gang sync responder
-- =========================================================
RegisterNetEvent("turfwar:shop:syncGangRequest", function(nonce)
print(("[turfwar:shop] syncGangRequest nonce=%s -> reply gang=%d"):format(tostring(nonce), tonumber(currentGang) or 0))
TriggerServerEvent("turfwar:shop:syncGangResponse", nonce, currentGang or 0)
end)
-- =========================
-- Vehicle ownership (1 per player) + blip + gang colour
-- =========================
local function clearMyShopVehicle()
if myShopVehBlip ~= 0 and DoesBlipExist(myShopVehBlip) then
RemoveBlip(myShopVehBlip)
end
myShopVehBlip = 0
if myShopVeh ~= 0 and DoesEntityExist(myShopVeh) then
SetEntityAsMissionEntity(myShopVeh, true, true)
DeleteEntity(myShopVeh)
end
myShopVeh = 0
end
local function createMyShopVehBlip(veh)
if myShopVehBlip ~= 0 and DoesBlipExist(myShopVehBlip) then
RemoveBlip(myShopVehBlip)
end
local blip = AddBlipForEntity(veh)
SetBlipSprite(blip, 225)
SetBlipScale(blip, 0.85)
SetBlipAsShortRange(blip, false)
local g = Config and Config.GANGS and Config.GANGS[currentGang]
if g and g.blipColor then
SetBlipColour(blip, g.blipColor)
end
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(currentGang == 3 and "Police Vehicle" or "My Vehicle")
EndTextCommandSetBlipName(blip)
myShopVehBlip = blip
end
local function applyGangColorsToVehicle(veh, gangId)
local rgb = getGangRGB(gangId)
SetVehicleModKit(veh, 0)
SetVehicleCustomPrimaryColour(veh, rgb.r, rgb.g, rgb.b)
SetVehicleCustomSecondaryColour(veh, rgb.r, rgb.g, rgb.b)
SetVehicleDirtLevel(veh, 0.0)
end
local function resolveVehicleModel(modelIn)
local model = tostring(modelIn or "")
if model ~= "" then return model end
if Config and Config.Shops and Config.Shops.GangVehicleModels and Config.Shops.GangVehicleModels[currentGang] then
return tostring(Config.Shops.GangVehicleModels[currentGang])
end
if Config and Config.Vehicles and Config.Vehicles.gangModels and Config.Vehicles.gangModels[currentGang] then
return tostring(Config.Vehicles.gangModels[currentGang])
end
return "sultan" -- fallback only
end
local function findBestVehicleSpawn()
local byGang = Config and Config.Shops and Config.Shops.LocationsByGang
local g = byGang and byGang[currentGang]
local pads = g and g.vehicles
local ped = PlayerPedId()
local p = GetEntityCoords(ped)
if type(pads) == "table" then
local best, bestD = nil, 999999.0
for _, v in pairs(pads) do
local pos = v3(v)
local d = dist(pos, p)
if d < bestD then
bestD = d
best = v
end
end
if best then
local pos = v3(best)
local heading = tonumber(best.w) or tonumber(best.h) or 0.0
return pos, heading
end
end
local fwd = GetEntityForwardVector(ped)
local pos = vector3(p.x + fwd.x * 6.0, p.y + fwd.y * 6.0, p.z)
return pos, GetEntityHeading(ped)
end
local function spawnVehicleClient(modelName)
clearMyShopVehicle()
local model = resolveVehicleModel(modelName)
local hash = GetHashKey(model)
if not IsModelInCdimage(hash) or not IsModelAVehicle(hash) then
Notify(("Invalid vehicle model: %s"):format(model))
return
end
RequestModel(hash)
local deadline = GetGameTimer() + 7000
while not HasModelLoaded(hash) do
Wait(0)
if GetGameTimer() > deadline then
Notify(("Failed to load model: %s"):format(model))
return
end
end
local pos, heading = findBestVehicleSpawn()
RequestCollisionAtCoord(pos.x, pos.y, pos.z)
for _ = 1, 20 do
local found, gz = GetGroundZFor_3dCoord(pos.x, pos.y, pos.z + 50.0, true)
if found then
pos = vector3(pos.x, pos.y, gz + 0.5)
break
end
Wait(0)
end
local veh = CreateVehicle(hash, pos.x, pos.y, pos.z, heading, true, false)
if veh == 0 then
Notify("Vehicle spawn failed.")
SetModelAsNoLongerNeeded(hash)
return
end
SetEntityAsMissionEntity(veh, true, true)
SetVehicleOnGroundProperly(veh)
SetVehicleEngineOn(veh, true, true, false)
SetVehRadioStation(veh, "OFF")
if tonumber(currentGang) == 3 then
local a = (Config and Config.Shops and Config.Shops.PoliceVehicleAppearance) or {}
if a.primary ~= nil and a.secondary ~= nil then
SetVehicleColours(veh, tonumber(a.primary) or 0, tonumber(a.secondary) or 0)
end
if a.pearlescent ~= nil or a.wheel ~= nil then
SetVehicleExtraColours(veh, tonumber(a.pearlescent) or 0, tonumber(a.wheel) or 0)
end
if a.livery ~= nil then
local liv = tonumber(a.livery)
if liv and liv >= 0 then SetVehicleLivery(veh, liv) end
end
if a.clean then
SetVehicleDirtLevel(veh, 0.0)
WashDecalsFromVehicle(veh, 1.0)
end
else
applyGangColorsToVehicle(veh, currentGang)
end
myShopVeh = veh
createMyShopVehBlip(veh)
TaskWarpPedIntoVehicle(PlayerPedId(), veh, -1)
SetModelAsNoLongerNeeded(hash)
Notify(("Spawned: %s"):format(model))
end
RegisterNetEvent("turfwar:shop:grantVehicle", function(model)
print(("[turfwar:shop] grantVehicle RECEIVED model=%s gang=%d"):format(tostring(model), tonumber(currentGang) or -1))
spawnVehicleClient(model)
end)
AddEventHandler("onResourceStop", function(res)
if res ~= GetCurrentResourceName() then return end
clearMyShopVehicle()
end)
-- =========================
-- Payload selector (Police vs Gang)
-- =========================
local function buildShopPayload()
local s = Config.Shops or {}
if currentGang == 3 then
return { weapons = s.PoliceWeapons or {}, ammo = s.PoliceAmmo or {}, vehicles = s.PoliceVehicles or {}, gangs = Config.GANGS or {} }
end
return { weapons = s.Weapons or {}, ammo = s.Ammo or {}, vehicles = s.Vehicles or {}, gangs = Config.GANGS or {} }
end
local function getGangShopLocations(gangId)
local s = Config.Shops or {}
local byGang = s.LocationsByGang or {}
local g = byGang[tonumber(gangId) or 0]
if type(g) ~= "table" then return nil end
return { shop = g.shop or {}, vehicles = g.vehicles or {} }
end
-- =========================
-- NUI Open/Close
-- =========================
local function openShop(tab, allowedTabs)
if shopOpen then return end
shopOpen = true
SetNuiFocus(true, true)
SetNuiFocusKeepInput(false)
SendNUIMessage({
type = "shop:open",
tab = tab or "weapons",
gangId = currentGang,
gangRGB = getGangRGB(currentGang),
payload = buildShopPayload(),
allowedTabs = allowedTabs or { "weapons", "ammo", "vehicles" },
})
TriggerServerEvent("turfwar:shop:requestBalance")
end
local function closeShop()
if not shopOpen then return end
shopOpen = false
SetNuiFocus(false, false)
SendNUIMessage({ type = "shop:close" })
end
-- =========================
-- Shop Blips
-- =========================
local function clearShopBlips()
for _, b in ipairs(ShopBlips) do
if DoesBlipExist(b) then RemoveBlip(b) end
end
ShopBlips = {}
end
local function addShopBlip(posIn, label)
local pos = v3(posIn)
local blip = AddBlipForCoord(pos.x, pos.y, pos.z)
SetBlipSprite(blip, 374)
SetBlipScale(blip, 0.75)
SetBlipAsShortRange(blip, true)
local g = Config.GANGS and Config.GANGS[currentGang]
SetBlipColour(blip, (g and g.blipColor) or 0)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(label)
EndTextCommandSetBlipName(blip)
ShopBlips[#ShopBlips + 1] = blip
end
local function buildShopBlips()
clearShopBlips()
if currentGang == 0 then
return
end
local gLocs = getGangShopLocations(currentGang)
if not gLocs then
print(("^1[turfwar]^7 No shop locations for gangId=%d. Check Config.Shops.LocationsByGang[%d] in shops.lua"):format(currentGang, currentGang))
return
end
local isPolice = (currentGang == 3)
for _, pos in iterList(gLocs.shop) do
addShopBlip(pos, isPolice and "Police Armory" or "Gang Shop")
end
for _, pos in iterList(gLocs.vehicles) do
addShopBlip(pos, isPolice and "Police Vehicles" or "Gang Vehicles")
end
end
-- =========================
-- Commands
-- =========================
RegisterCommand("tw_mygang", function()
Notify(("currentGang=%d"):format(currentGang or -1))
print(("^3[turfwar]^7 tw_mygang -> currentGang=%d"):format(currentGang or -1))
end, false)
RegisterCommand("tw_shopdebug", function()
DEBUG_MARKERS = not DEBUG_MARKERS
Notify(("Shop marker debug: %s"):format(DEBUG_MARKERS and "~g~ON~s~" or "~r~OFF~s~"))
print(("^3[turfwar]^7 DEBUG_MARKERS=%s"):format(tostring(DEBUG_MARKERS)))
end, false)
-- =========================
-- Server sync + gang setter
-- =========================
local function SyncGangToServer()
local now = GetGameTimer()
if currentGang == lastSyncedGang and (now - lastSyncAt) < 1000 then return end
lastSyncedGang = currentGang
lastSyncAt = now
TriggerServerEvent("turfwar:setFaction", currentGang)
end
local function setGang(newGang)
newGang = tonumber(newGang) or 0
if newGang == currentGang then
SyncGangToServer()
return
end
clearMyShopVehicle()
currentGang = newGang
SyncGangToServer()
if shopOpen then
SendNUIMessage({ type = "shop:gang", gangId = currentGang, gangRGB = getGangRGB(currentGang) })
TriggerServerEvent("turfwar:shop:requestBalance")
end
buildShopBlips()
end
-- =========================
-- Events from your gang system
-- =========================
RegisterNetEvent("turfwar:setMyGang", function(gangId)
setGang(gangId)
end)
RegisterNetEvent("turfwar:setFaction", function(gangId)
print(("^3[turfwar]^7 shop.lua got turfwar:setFaction -> %d"):format(tonumber(gangId) or 0))
setGang(gangId)
end)
RegisterNetEvent("turfwar:gangUpdate", function(gangId)
print(("^3[turfwar]^7 shop.lua got turfwar:gangUpdate -> %d"):format(tonumber(gangId) or 0))
setGang(gangId)
end)
-- =========================
-- Shop UI info events
-- =========================
RegisterNetEvent("turfwar:shop:balance", function(gangId, balance)
SendNUIMessage({
type = "shop:balance",
gangId = tonumber(gangId) or 0,
balance = tonumber(balance) or 0
})
end)
RegisterNetEvent("turfwar:shop:result", function(ok, message, newBalance)
if message and message ~= "" then Notify(message) end
if newBalance ~= nil then
SendNUIMessage({
type = "shop:balance",
gangId = currentGang,
balance = tonumber(newBalance) or 0
})
end
end)
RegisterNetEvent("turfwar:shop:grantWeapon", function(weaponName, ammo)
print(("[turfwar:shop] grantWeapon RECEIVED weapon=%s ammo=%s gang=%d"):format(
tostring(weaponName), tostring(ammo), tonumber(currentGang) or -1
))
local ped = PlayerPedId()
local wHash = GetHashKey(weaponName)
GiveWeaponToPed(ped, wHash, tonumber(ammo) or 0, false, true)
-- verify after a short delay (detects scripts stripping weapons)
CreateThread(function()
Wait(250)
local hasIt = HasPedGotWeapon(ped, wHash, false)
print(("[turfwar:shop] grantWeapon CHECK weapon=%s has=%s"):format(tostring(weaponName), tostring(hasIt)))
end)
end)
RegisterNetEvent("turfwar:shop:grantAmmo", function(weaponName, amount)
print(("[turfwar:shop] grantAmmo RECEIVED for=%s amount=%s gang=%d"):format(
tostring(weaponName), tostring(amount), tonumber(currentGang) or -1
))
local ped = PlayerPedId()
local wHash = GetHashKey(weaponName)
AddAmmoToPed(ped, wHash, tonumber(amount) or 0)
end)
RegisterNetEvent("turfwar:shop:debugOpen", function(tab)
openShop(tab or "weapons", { "weapons", "ammo", "vehicles" })
end)
-- =========================
-- NUI callbacks
-- =========================
RegisterNUICallback("shop:close", function(_, cb)
closeShop()
cb({ ok = true })
end)
RegisterNUICallback("shop:buyWeapon", function(data, cb)
TriggerServerEvent("turfwar:shop:buyWeapon", tostring(data.itemId or ""))
cb({ ok = true })
end)
RegisterNUICallback("shop:buyAmmo", function(data, cb)
TriggerServerEvent("turfwar:shop:buyAmmo", tostring(data.itemId or ""))
cb({ ok = true })
end)
RegisterNUICallback("shop:buyVehicle", function(data, cb)
TriggerServerEvent("turfwar:shop:buyVehicle", tostring(data.itemId or ""))
cb({ ok = true })
end)
-- =========================
-- Faction request (on join/spawn)
-- =========================
local function RequestFaction()
TriggerServerEvent("turfwar:requestFaction")
end
CreateThread(function()
Wait(1500)
RequestFaction()
end)
AddEventHandler("playerSpawned", function()
Wait(1500)
RequestFaction()
end)
-- =========================
-- Marker + Interact loop
-- =========================
CreateThread(function()
Wait(1000)
print("^3[turfwar]^7 shop.lua marker loop running")
buildShopBlips()
while true do
Wait(0)
local ped = PlayerPedId()
local p = GetEntityCoords(ped)
local shops = Config.Shops or {}
local md = tonumber(shops.MarkerDist) or 25.0
local id = tonumber(shops.InteractDist) or 2.0
local key = tonumber(shops.InteractKey) or 38
if DEBUG_MARKERS then
DrawMarker(1, p.x, p.y, p.z - 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.5, 0.5, 0.25, 255, 255, 255, 180, false, true, 2, false, nil, nil, false)
end
if currentGang ~= 0 then
local gLocs = getGangShopLocations(currentGang)
if gLocs then
local isPolice = (currentGang == 3)
local lists = {
{ tab = "weapons", locs = gLocs.shop, allowed = { "weapons", "ammo" } },
{ tab = "vehicles", locs = gLocs.vehicles, allowed = { "vehicles" } },
}
local canOpen = false
local openTab = nil
local openAllowed = nil
for _, entry in ipairs(lists) do
for _, posAny in iterList(entry.locs) do
local d = dist(v3(posAny), p)
if d <= md then
drawMarkerAt(posAny)
if d <= id then
canOpen = true
openTab = entry.tab
openAllowed = entry.allowed
end
end
end
end
if canOpen and not shopOpen then
BeginTextCommandDisplayHelp("STRING")
AddTextComponentSubstringPlayerName(
isPolice and "Press ~INPUT_CONTEXT~ to open Police Armory" or
"Press ~INPUT_CONTEXT~ to open Gang Shop"
)
EndTextCommandDisplayHelp(0, false, true, 0)
if IsControlJustPressed(0, key) then
local now = GetGameTimer()
if now - lastOpenAttempt > 400 then
lastOpenAttempt = now
openShop(openTab or "weapons", openAllowed)
end
end
end
end
end
if shopOpen and IsControlJustPressed(0, 322) then
closeShop()
end
end
end)