Turfwar/client/player_police.lua

202 lines
6.1 KiB
Lua

-- 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)