Upload files to "client"

This commit is contained in:
tanthius 2026-02-12 04:15:37 +00:00
parent 4cfaeceae8
commit 4910c39102
5 changed files with 1942 additions and 0 deletions

558
client/guards.lua Normal file
View File

@ -0,0 +1,558 @@
-- client/guards.lua
print("^2[turfwar]^7 CLIENT guards.lua LOADED (invincible guards + manual damage both ways)")
local DEBUG = false
local function dbg(msg) if DEBUG then print(("^3[turfwar]^7 [guards] %s"):format(msg)) end end
-- ---------------------------------------------------------------------------
-- Handshake + request gang snapshot
-- ---------------------------------------------------------------------------
CreateThread(function()
while not NetworkIsSessionStarted() do Wait(250) end
TriggerServerEvent("turfwar:guardsClientReady")
Wait(1500)
TriggerServerEvent("turfwar:requestAllPlayerGangs")
end)
-- ---------------------------------------------------------------------------
-- Local gang id (for colours only)
-- ---------------------------------------------------------------------------
local LocalGangId = 0
RegisterNetEvent("turfwar:gangUpdate", function(g) LocalGangId = tonumber(g) or 0 end)
RegisterNetEvent("turfwar:setFaction", function(g) LocalGangId = tonumber(g) or LocalGangId or 0 end)
-- ---------------------------------------------------------------------------
-- All players' gang cache (server must broadcast turfwar:playerGang)
-- ---------------------------------------------------------------------------
local PlayerGangByServerId = {} -- [serverId] = gangId
RegisterNetEvent("turfwar:playerGang", function(serverId, gangId)
serverId = tonumber(serverId)
gangId = tonumber(gangId)
if not serverId then return end
if not gangId or gangId < 0 then
PlayerGangByServerId[serverId] = nil
else
PlayerGangByServerId[serverId] = gangId
end
end)
RegisterCommand("tw_gangcache", function()
print("^6[turfwar]^7 PlayerGangByServerId cache:")
for k,v in pairs(PlayerGangByServerId) do
print((" %s => %s"):format(k, v))
end
end, false)
-- ---------------------------------------------------------------------------
-- Relationship group (behaviour only; damage is manual)
-- ---------------------------------------------------------------------------
local GUARD_REL = nil
local REL_INIT = false
local function ensureRel()
if REL_INIT then return end
REL_INIT = true
GUARD_REL = AddRelationshipGroup("TW_GUARDS")
SetRelationshipBetweenGroups(0, GUARD_REL, GUARD_REL)
-- neutral to players by default; we script combat
SetRelationshipBetweenGroups(3, GUARD_REL, `PLAYER`)
SetRelationshipBetweenGroups(3, `PLAYER`, GUARD_REL)
local neutralGroups = {
`AMBIENT_GANG_LOST`,
`AMBIENT_GANG_BALLAS`,
`AMBIENT_GANG_FAMILY`,
`AMBIENT_GANG_MEXICAN`,
`AMBIENT_GANG_SALVA`,
`AMBIENT_GANG_WEICHENG`,
`COP`,
`SECURITY_GUARD`,
}
for _, grp in ipairs(neutralGroups) do
SetRelationshipBetweenGroups(3, GUARD_REL, grp)
SetRelationshipBetweenGroups(3, grp, GUARD_REL)
end
end
-- ---------------------------------------------------------------------------
-- UI config
-- ---------------------------------------------------------------------------
local MARKER_MAX_DIST = 70.0
local BLIP_SPRITE = 270
local BLIP_SCALE = 0.65
local ENEMY_BLIP_COLOUR = 1
local FRIENDLY_ALPHA = 160
local FACTION_BLIP_COLOUR = { [1]=5,[2]=2,[3]=3,[4]=40,[5]=7 }
local FACTION_RGB = {
[1]={r=255,g=220,b=0},
[2]={r=0,g=200,b=0},
[3]={r=0,g=130,b=255},
[4]={r=60,g=60,b=60},
[5]={r=160,g=80,b=255}
}
local ENEMY_RGB = { r=255, g=0, b=0 }
local function isFriendly(ownerFaction)
return (LocalGangId ~= 0 and ownerFaction ~= 0 and LocalGangId == ownerFaction)
end
local function getColours(ownerFaction)
if isFriendly(ownerFaction) then
return (FACTION_RGB[ownerFaction] or {r=255,g=255,b=255}), (FACTION_BLIP_COLOUR[ownerFaction] or 0)
end
return ENEMY_RGB, ENEMY_BLIP_COLOUR
end
-- ---------------------------------------------------------------------------
-- State
-- ---------------------------------------------------------------------------
local GuardState = {} -- [turfId] = { ownerFaction, peds, blips, spawnPoints }
local GuardMeta = {} -- [ped] = { turfId, ownerFaction, spawn=vector3(...) }
-- ---------------------------------------------------------------------------
-- Helpers
-- ---------------------------------------------------------------------------
local function loadModel(model)
local h = GetHashKey(model)
if not IsModelInCdimage(h) then return nil end
RequestModel(h)
local timeout = GetGameTimer() + 5000
while not HasModelLoaded(h) do
Wait(10)
if GetGameTimer() > timeout then return nil end
end
return h
end
local function delPed(p)
if p and DoesEntityExist(p) then
GuardMeta[p] = nil
SetEntityAsMissionEntity(p, true, true)
DeleteEntity(p)
end
end
local function delBlip(b)
if b and DoesBlipExist(b) then RemoveBlip(b) end
end
local function clearTurf(turfId)
local st = GuardState[turfId]
if not st then return end
for _, p in pairs(st.peds or {}) do delPed(p) end
for _, b in pairs(st.blips or {}) do delBlip(b) end
GuardState[turfId] = nil
end
local function makeBlip(ped, ownerFaction)
local blip = AddBlipForEntity(ped)
SetBlipSprite(blip, BLIP_SPRITE)
SetBlipScale(blip, BLIP_SCALE)
SetBlipAsShortRange(blip, true)
local _, col = getColours(ownerFaction)
SetBlipColour(blip, col)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("Guard")
EndTextCommandSetBlipName(blip)
return blip
end
local function isGuardPed(ped)
return ped and ped ~= 0 and GuardMeta[ped] ~= nil
end
local function standAt(ped, pos)
ClearPedTasks(ped)
TaskStandGuard(ped, pos.x, pos.y, pos.z, 0.0, "WORLD_HUMAN_GUARD_STAND", 0)
end
local function configureGuard(ped)
ensureRel()
SetEntityAsMissionEntity(ped, true, true)
SetPedRelationshipGroupHash(ped, GUARD_REL)
SetEntityMaxHealth(ped, 220)
SetEntityHealth(ped, 220)
SetPedArmour(ped, 75)
SetPedAccuracy(ped, 55)
SetPedAlertness(ped, 1)
SetPedSeeingRange(ped, 120.0)
SetPedHearingRange(ped, 120.0)
SetPedCombatAbility(ped, 2)
SetPedCombatMovement(ped, 2)
SetPedCombatRange(ped, 2)
SetPedCombatAttributes(ped, 46, true)
SetPedDropsWeaponsWhenDead(ped, false)
SetCanAttackFriendly(ped, false, false)
SetBlockingOfNonTemporaryEvents(ped, true)
SetPedKeepTask(ped, true)
SetPedFleeAttributes(ped, 0, false)
SetPedSuffersCriticalHits(ped, false)
SetPedShootRate(ped, 450)
-- KEY: they never take native damage (prevents guard→guard deaths / weird ownership)
SetEntityInvincible(ped, true)
end
local function applyNoCollisionAmongGuards(st)
local peds = st.peds or {}
for i = 1, #peds do
local a = peds[i]
if a and DoesEntityExist(a) then
for j = i + 1, #peds do
local b = peds[j]
if b and DoesEntityExist(b) then
SetEntityNoCollisionEntity(a, b, true)
SetEntityNoCollisionEntity(b, a, true)
end
end
end
end
end
-- ---------------------------------------------------------------------------
-- Nearest enemy player
-- ---------------------------------------------------------------------------
local AGGRO_RANGE = 90.0
local function findNearestEnemyPlayerPed(ownerFaction, fromPos)
local bestPed, bestDist = nil, AGGRO_RANGE + 0.01
for _, pid in ipairs(GetActivePlayers()) do
local spid = GetPlayerServerId(pid)
local gang = PlayerGangByServerId[spid]
if gang and gang ~= 0 and gang ~= ownerFaction then
local ped = GetPlayerPed(pid)
if ped and ped ~= 0 and DoesEntityExist(ped) and not IsEntityDead(ped) then
local d = #(GetEntityCoords(ped) - fromPos)
if d < bestDist then
bestDist = d
bestPed = ped
end
end
end
end
return bestPed, bestDist
end
-- ---------------------------------------------------------------------------
-- Spawn/clear events (host only)
-- ---------------------------------------------------------------------------
RegisterNetEvent("turfwar:spawnGuards", function(payload)
if type(payload) ~= "table" then return end
local turfId = tostring(payload.turfId)
local ownerFaction = tonumber(payload.ownerFaction) or 0
local count = tonumber(payload.count) or 0
local spawns = payload.spawns or {}
if ownerFaction == 0 or count <= 0 then
clearTurf(turfId)
return
end
clearTurf(turfId)
GuardState[turfId] = {
ownerFaction = ownerFaction,
peds = {},
blips = {},
spawnPoints = spawns
}
local model = loadModel(payload.model or "g_m_y_lost_01")
if not model then
print("^1[turfwar]^7 Guard model failed to load")
return
end
for i = 1, count do
local pos = spawns[i]
if not pos then break end
local ped = CreatePed(4, model, pos.x, pos.y, pos.z, 0.0, true, true)
if ped and DoesEntityExist(ped) then
configureGuard(ped)
local weapon = payload.weapon or "WEAPON_PISTOL"
GiveWeaponToPed(ped, GetHashKey(weapon), 250, false, true)
SetCurrentPedWeapon(ped, GetHashKey(weapon), true)
standAt(ped, pos)
GuardState[turfId].peds[i] = ped
GuardState[turfId].blips[i] = makeBlip(ped, ownerFaction)
GuardMeta[ped] = {
turfId = turfId,
ownerFaction = ownerFaction,
spawn = vector3(pos.x, pos.y, pos.z)
}
if not NetworkHasControlOfEntity(ped) then
NetworkRequestControlOfEntity(ped)
end
end
end
SetModelAsNoLongerNeeded(model)
applyNoCollisionAmongGuards(GuardState[turfId])
end)
RegisterNetEvent("turfwar:clearGuards", function(turfId)
clearTurf(tostring(turfId))
end)
-- ---------------------------------------------------------------------------
-- Manual damage BOTH ways
-- - Enemy player -> guard
-- - Guard -> enemy player
-- Notes:
-- - We DO NOT trust weapon hash indexes across builds; use fixed damage values.
-- ---------------------------------------------------------------------------
local GUARD_HIT_DMG_TO_PLAYER = 18 -- per hit (tune)
local PLAYER_HIT_DMG_TO_GUARD = 25 -- per hit (tune)
-- per-attacker throttle (prevents one bullet generating multiple events)
local lastHitTick = {} -- [attackerNetId .. ":" .. victimNetId] = gameTimer
local function shouldThrottle(attacker, victim)
local a = tostring(attacker or 0)
local v = tostring(victim or 0)
local key = a .. ":" .. v
local now = GetGameTimer()
local last = lastHitTick[key] or 0
if now - last < 90 then return true end -- ~11 hits/sec max
lastHitTick[key] = now
return false
end
local function gangFromPlayerPed(attackerPed)
if not attackerPed or attackerPed == 0 or not IsPedAPlayer(attackerPed) then return nil end
local playerIndex = NetworkGetPlayerIndexFromPed(attackerPed)
if not playerIndex or playerIndex == -1 then return nil end
local serverId = GetPlayerServerId(playerIndex)
if not serverId then return nil end
return PlayerGangByServerId[serverId]
end
local function applyDamageToPed(victimPed, amount)
if not victimPed or victimPed == 0 or not DoesEntityExist(victimPed) then return end
if amount <= 0 then return end
local hp = GetEntityHealth(victimPed)
local newHp = hp - amount
if newHp <= 0 then
-- kill
SetEntityHealth(victimPed, 0)
else
SetEntityHealth(victimPed, newHp)
end
end
AddEventHandler("gameEventTriggered", function(name, args)
if name ~= "CEventNetworkEntityDamage" then return end
local victim = args[1]
local attacker = args[2]
if not victim or victim == 0 or not DoesEntityExist(victim) then return end
if not attacker or attacker == 0 or not DoesEntityExist(attacker) then return end
if shouldThrottle(attacker, victim) then return end
local victimIsGuard = isGuardPed(victim)
local attackerIsGuard = isGuardPed(attacker)
local attackerIsPlayer = IsPedAPlayer(attacker)
-- --------------------------------------------------------
-- Guard vs Guard: ignore + calm
-- --------------------------------------------------------
if victimIsGuard and attackerIsGuard then
ClearEntityLastDamageEntity(victim)
local meta = GuardMeta[victim]
if meta and meta.spawn then
ClearPedTasksImmediately(victim)
standAt(victim, meta.spawn)
end
return
end
-- --------------------------------------------------------
-- Player -> Guard (manual damage if ENEMY)
-- --------------------------------------------------------
if victimIsGuard and attackerIsPlayer then
ClearEntityLastDamageEntity(victim)
local meta = GuardMeta[victim]
if not meta then return end
local attackerGang = gangFromPlayerPed(attacker)
if not attackerGang or attackerGang == 0 then
-- if your gang cache isnt populated, guards will appear immune.
-- run /tw_gangcache on host to confirm.
return
end
if attackerGang == meta.ownerFaction then
-- friendly player: no damage
return
end
-- manual damage: temporarily allow kill by turning off invincibility only at death
local hp = GetEntityHealth(victim)
local newHp = hp - PLAYER_HIT_DMG_TO_GUARD
if newHp <= 0 then
SetEntityInvincible(victim, false)
SetEntityHealth(victim, 0)
else
SetEntityHealth(victim, newHp)
end
return
end
-- --------------------------------------------------------
-- Guard -> Player (manual damage if ENEMY)
-- --------------------------------------------------------
if attackerIsGuard and IsPedAPlayer(victim) then
-- Determine which turf/owner this guard belongs to
local meta = GuardMeta[attacker]
if not meta then return end
local victimGang = gangFromPlayerPed(victim)
if not victimGang or victimGang == 0 then
-- neutral players don't get shot by guards (tune if you want)
return
end
if victimGang == meta.ownerFaction then
-- friendly player
return
end
applyDamageToPed(victim, GUARD_HIT_DMG_TO_PLAYER)
return
end
end)
-- ---------------------------------------------------------------------------
-- Combat gate:
-- If enemy exists nearby -> fight that enemy
-- Else -> stand down (stops spraying)
-- ---------------------------------------------------------------------------
CreateThread(function()
while true do
Wait(300)
for _, st in pairs(GuardState) do
local owner = tonumber(st.ownerFaction) or 0
local spawns = st.spawnPoints or {}
for idx, g in pairs(st.peds or {}) do
if g and DoesEntityExist(g) and not IsEntityDead(g) and NetworkHasControlOfEntity(g) then
local gPos = GetEntityCoords(g)
local enemyPed = nil
if owner ~= 0 then
enemyPed = select(1, findNearestEnemyPlayerPed(owner, gPos))
end
if enemyPed then
TaskCombatPed(g, enemyPed, 0, 16)
else
if IsPedInCombat(g, 0) or IsPedShooting(g) then
local pos = spawns[idx] and vector3(spawns[idx].x, spawns[idx].y, spawns[idx].z) or gPos
ClearPedTasksImmediately(g)
standAt(g, pos)
end
end
end
end
end
end
end)
-- ---------------------------------------------------------------------------
-- Marker + blip colour update
-- ---------------------------------------------------------------------------
CreateThread(function()
while true do
Wait(0)
local me = PlayerPedId()
if not me or me == 0 then goto cont end
local myC = GetEntityCoords(me)
for _, st in pairs(GuardState) do
local rgb, col = getColours(st.ownerFaction)
for idx, ped in pairs(st.peds or {}) do
if ped and DoesEntityExist(ped) and not IsEntityDead(ped) then
local p = GetEntityCoords(ped)
local d = #(myC - p)
if d <= MARKER_MAX_DIST then
DrawMarker(
2, p.x, p.y, p.z + 1.1,
0.0,0.0,0.0,
0.0,0.0,0.0,
0.25,0.25,0.25,
rgb.r,rgb.g,rgb.b, FRIENDLY_ALPHA,
false,true,2,false,nil,nil,false
)
end
local b = st.blips and st.blips[idx]
if b and DoesBlipExist(b) then
SetBlipColour(b, col)
end
end
end
end
::cont::
end
end)
-- ---------------------------------------------------------------------------
-- Cleanup: delete dead guards, notify server when turf empty
-- ---------------------------------------------------------------------------
CreateThread(function()
while true do
Wait(800)
for turfId, st in pairs(GuardState) do
local alive = 0
for idx = #st.peds, 1, -1 do
local ped = st.peds[idx]
local blip = st.blips and st.blips[idx]
local deadOrGone = (not ped) or (not DoesEntityExist(ped)) or IsEntityDead(ped)
if deadOrGone then
if blip then delBlip(blip) end
if ped and DoesEntityExist(ped) then delPed(ped) end
table.remove(st.peds, idx)
if st.blips then table.remove(st.blips, idx) end
else
alive = alive + 1
-- keep invincible true (prevents guard deaths from guard shots)
SetEntityInvincible(ped, true)
end
end
if alive == 0 then
TriggerServerEvent("turfwar:guardsEmpty", turfId)
GuardState[turfId] = nil
end
end
end
end)

160
client/killfeed_chat.lua Normal file
View File

@ -0,0 +1,160 @@
print("^2[turfwar]^7 killfeed_chat.lua loaded (client)")
local PlayerGang = {} -- [serverId] = gangId
RegisterNetEvent('turfwar:playerGang', function(serverId, gangId)
PlayerGang[tonumber(serverId)] = tonumber(gangId) or 0
end)
-- Optional snapshot
CreateThread(function()
Wait(1500)
TriggerServerEvent('turfwar:requestAllPlayerGangs')
end)
RegisterNetEvent('turfwar:allPlayerGangs', function(map)
if type(map) ~= "table" then return end
for sid, gid in pairs(map) do
PlayerGang[tonumber(sid)] = tonumber(gid) or 0
end
end)
local function weaponLabelFromHash(hash)
local t = {
[`WEAPON_UNARMED`] = "Fists",
[`WEAPON_KNIFE`] = "Knife",
[`WEAPON_PISTOL`] = "Pistol",
[`WEAPON_COMBATPISTOL`] = "Combat Pistol",
[`WEAPON_APPISTOL`] = "AP Pistol",
[`WEAPON_SMG`] = "SMG",
[`WEAPON_ASSAULTRIFLE`] = "Assault Rifle",
[`WEAPON_CARBINERIFLE`] = "Carbine Rifle",
[`WEAPON_PUMPSHOTGUN`] = "Pump Shotgun",
[`WEAPON_SNIPERRIFLE`] = "Sniper Rifle",
[`WEAPON_HEAVYSNIPER`] = "Heavy Sniper",
[`WEAPON_GRENADE`] = "Grenade",
[`WEAPON_EXPLOSION`] = "Explosion",
[`WEAPON_RUN_OVER_BY_CAR`] = "Vehicle",
}
if t[hash] then return t[hash] end
local disp = GetWeaponDisplayNameFromHash(hash)
if disp and disp ~= "" then
local label = GetLabelText(disp)
if label and label ~= "NULL" then return label end
end
return tostring(hash)
end
local function gangRgbTable(gangId)
gangId = tonumber(gangId) or 0
local g = (Config and Config.GANGS and Config.GANGS[gangId]) or nil
if g and type(g.rgb) == "table" then
local r = tonumber(g.rgb[1]) or 255
local gg = tonumber(g.rgb[2]) or 255
local b = tonumber(g.rgb[3]) or 255
return { r, gg, b }
end
return {255,255,255}
end
-- =========================================================
-- Victim reports once (PvP + non-player deaths)
-- =========================================================
local lastSentAt = 0
CreateThread(function()
while true do
Wait(0)
local ped = PlayerPedId()
if ped ~= 0 and IsEntityDead(ped) then
local now = GetGameTimer()
if now - lastSentAt > 2000 then
lastSentAt = now
local killerPed = GetPedSourceOfDeath(ped)
local weaponHash = GetPedCauseOfDeath(ped)
local victimServerId = GetPlayerServerId(PlayerId())
local reported = false
-- PvP killer?
if killerPed and killerPed ~= 0 and IsEntityAPed(killerPed) and IsPedAPlayer(killerPed) then
local killerPlayer = NetworkGetPlayerIndexFromPed(killerPed)
local killerServerId = killerPlayer and GetPlayerServerId(killerPlayer) or 0
if killerServerId and killerServerId > 0 then
local myCoords = GetEntityCoords(ped)
local kCoords = GetEntityCoords(killerPed)
local dist = #(myCoords - kCoords)
local ok, bone = GetPedLastDamageBone(ped)
local headshot = (ok and bone == 31086)
TriggerServerEvent('turfwar:killfeed:report', {
killer = killerServerId,
victim = victimServerId,
weapon = weaponHash,
headshot = headshot,
distance = dist,
killerGang = PlayerGang[killerServerId] or 0,
victimGang = PlayerGang[victimServerId] or 0,
})
reported = true
end
end
-- Non-player death fallback
if not reported then
TriggerServerEvent('turfwar:killfeed:report', {
killer = 0,
victim = victimServerId,
weapon = weaponHash or 0,
headshot = false,
distance = 0,
killerGang = 0,
victimGang = PlayerGang[victimServerId] or 0,
})
end
end
while IsEntityDead(PlayerPedId()) do Wait(200) end
Wait(500)
end
end
end)
-- =========================================================
-- Receive broadcast -> send to NUI
-- =========================================================
RegisterNetEvent('turfwar:killfeed:chat', function(data)
if type(data) ~= "table" then return end
local victimRgb = gangRgbTable(data.victimGang)
local killerRgb = gangRgbTable(data.killerGang)
local deathOnly = (data.isDeathOnly == true) or (tonumber(data.killer or 0) <= 0)
if deathOnly then
SendNUIMessage({
type = "killfeed:add",
isDeathOnly = true,
victimName = data.victimName or "Unknown",
victimRgb = victimRgb,
})
return
end
SendNUIMessage({
type = "killfeed:add",
killerName = data.killerName or "Unknown",
victimName = data.victimName or "Unknown",
weapon = weaponLabelFromHash(tonumber(data.weapon or 0) or 0),
headshot = (data.headshot == true),
distance = tonumber(data.distance or 0) or 0,
killerRgb = killerRgb,
victimRgb = victimRgb,
})
end)

108
client/leaderboard.lua Normal file
View File

@ -0,0 +1,108 @@
print("^2[turfwar]^7 leaderboard client loaded")
local leaderboard = {}
-- Map FiveM blipColor IDs -> RGB (approx).
-- We only need the ones you use: 0,2,3,5,7,40.
local BLIP_RGB = {
[0] = {255, 255, 255}, -- White
[2] = { 60, 200, 60}, -- Green
[3] = { 70, 120, 255}, -- Blue
[5] = {255, 220, 60}, -- Yellow
[7] = {190, 90, 255}, -- Purple
[40] = { 40, 40, 40}, -- Dark grey / black
}
local function getGangName(gangId)
if Config and Config.GANGS and Config.GANGS[gangId] and Config.GANGS[gangId].name then
return Config.GANGS[gangId].name
end
return ("Gang %s"):format(gangId)
end
local function getGangRGB(gangId)
local blip = 0
if Config and Config.GANGS and Config.GANGS[gangId] and Config.GANGS[gangId].blipColor then
blip = tonumber(Config.GANGS[gangId].blipColor) or 0
end
return BLIP_RGB[blip] or {255, 255, 255}
end
local function drawText(x, y, text, r, g, b, a, scale)
SetTextFont(4)
SetTextScale(scale, scale)
SetTextColour(r, g, b, a)
SetTextOutline()
SetTextWrap(0.0, 1.0)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringPlayerName(text)
EndTextCommandDisplayText(x, y)
end
-- Ask server for current leaderboard on join/resource start
CreateThread(function()
Wait(1500)
TriggerServerEvent("turfwar:requestGangLeaderboard")
end)
RegisterNetEvent("turfwar:gangLeaderboard", function(rankedGangIds)
leaderboard = rankedGangIds or {}
-- debug:
-- print("^2[turfwar]^7 leaderboard: " .. json.encode(leaderboard))
end)
-- Compact HUD render (smaller box, slightly bigger text)
CreateThread(function()
while true do
Wait(0)
if not leaderboard or #leaderboard == 0 then
goto continue
end
local maxRows = math.min(#leaderboard, 8) -- fewer rows keeps it compact
-- Layout (top-left)
local x = 0.018
local y = 0.185
-- Slightly bigger than before (but still realistic)
local titleScale = 0.32
local rowScale = 0.28
-- Smaller/tighter panel
local rowH = 0.018
local padX = 0.008
local padY = 0.008
local panelW = 0.14 -- ~⅓ smaller than before (was 0.22)
local panelH = padY + 0.020 + (maxRows * rowH) + padY
-- Background panel (more compact)
DrawRect(x + panelW/2, y + panelH/2, panelW, panelH, 0, 0, 0, 105)
-- Title
drawText(x + padX, y + padY, "Top Ranked Gang", 255, 255, 255, 235, titleScale)
-- Rows
local startY = y + padY + 0.020
for i = 1, maxRows do
local gangId = leaderboard[i]
local name = getGangName(gangId)
local r,g,b = table.unpack(getGangRGB(gangId))
local rowY = startY + ((i-1) * rowH)
-- very faint row highlight
DrawRect(x + panelW/2, rowY + 0.009, panelW, rowH, 255, 255, 255, 8)
-- rank number (lighter grey / near-white, with outline)
drawText(x + padX, rowY, ("%d."):format(i), 235, 235, 235, 235, rowScale)
-- gang name (colored)
drawText(x + padX + 0.020, rowY, name, r, g, b, 245, rowScale)
end
::continue::
end
end)

28
client/loadouts.lua Normal file
View File

@ -0,0 +1,28 @@
-- client/loadouts.lua (STRIP-ONLY for neutrals)
print("^2[turfwar]^7 client/loadouts.lua LOADED (STRIP-ONLY - shop handles weapons)")
local currentGang = 0
RegisterNetEvent("turfwar:setFaction", function(gangId)
currentGang = tonumber(gangId) or 0
end)
RegisterNetEvent("turfwar:gangUpdate", function(gangId)
currentGang = tonumber(gangId) or 0
end)
RegisterNetEvent("turfwar:setMyGang", function(gangId)
currentGang = tonumber(gangId) or 0
end)
RegisterNetEvent("turfwar:stripWeapons", function()
if currentGang ~= 0 then
-- only strip neutral
return
end
local ped = PlayerPedId()
if not ped or ped == 0 then return end
RemoveAllPedWeapons(ped, true)
end)
RegisterNetEvent("turfwar:applyLoadout", function(loadout)
-- disabled: shop owns weapons
end)

1088
client/main.lua Normal file

File diff suppressed because it is too large Load Diff