Upload files to "client"

This commit is contained in:
tanthius 2026-02-12 04:14:54 +00:00
parent 322a3d23e8
commit 72bf3dbec1
5 changed files with 478 additions and 0 deletions

201
client/appearance.lua Normal file
View File

@ -0,0 +1,201 @@
-- client/appearance.lua
print("^2[turfwar]^7 appearance.lua loaded (client)")
local appearance = nil
local function IsFreemodeModel(hash)
return hash == GetHashKey("mp_m_freemode_01") or hash == GetHashKey("mp_f_freemode_01")
end
local function ForcePlayerModel(modelName)
local model = GetHashKey(modelName)
if not IsModelInCdimage(model) or not IsModelValid(model) then
print(("^1[turfwar]^7 INVALID model: %s"):format(modelName))
return false
end
RequestModel(model)
local timeout = GetGameTimer() + 8000
while not HasModelLoaded(model) do
Wait(0)
if GetGameTimer() > timeout then
print(("^1[turfwar]^7 TIMEOUT loading model: %s"):format(modelName))
return false
end
end
SetPlayerModel(PlayerId(), model)
SetModelAsNoLongerNeeded(model)
Wait(200)
return (GetEntityModel(PlayerPedId()) == model)
end
local function ApplyAppearanceNow()
if not appearance then return end
local ped = PlayerPedId()
if not ped or ped == 0 then return end
-- 1) Force model
local desired = (appearance.gender == "f") and "mp_f_freemode_01" or "mp_m_freemode_01"
if GetEntityModel(ped) ~= GetHashKey(desired) then
ForcePlayerModel(desired)
ped = PlayerPedId()
if not ped or ped == 0 then return end
end
local function ApplyDefaultHeadBlend(ped, gender)
if gender == "f" then
-- softer defaults for female
SetPedHeadBlendData(ped,
21, 0, 0, -- shape (mother-ish)
21, 0, 0, -- skin
0.55, 0.0, 0.0,
false
)
else
SetPedHeadBlendData(ped,
0, 0, 0,
0, 0, 0,
0.5, 0.5, 0.0,
false
)
end
-- neutralize extreme face features (optional)
for i = 0, 19 do
SetPedFaceFeature(ped, i, 0.0)
end
end
ApplyDefaultHeadBlend(ped, appearance.gender)
-- 2) Ensure freemode
if not IsFreemodeModel(GetEntityModel(ped)) then
print("^1[turfwar]^7 ApplyAppearanceNow: not freemode even after force (something else is overriding model).")
return
end
-- 3) Hair style validation (component 2)
local hairDrawable = tonumber(appearance.hair_drawable) or 0
local hairTexture = tonumber(appearance.hair_texture) or 0
local maxHair = GetNumberOfPedDrawableVariations(ped, 2)
if maxHair > 0 then
if hairDrawable < 0 then hairDrawable = 0 end
if hairDrawable >= maxHair then hairDrawable = maxHair - 1 end
local maxTex = GetNumberOfPedTextureVariations(ped, 2, hairDrawable)
if maxTex <= 0 then maxTex = 1 end
if hairTexture < 0 then hairTexture = 0 end
if hairTexture >= maxTex then hairTexture = maxTex - 1 end
SetPedComponentVariation(ped, 2, hairDrawable, hairTexture, 0)
end
-- 4) Hair color
local c = tonumber(appearance.hair_color) or 0
local h = tonumber(appearance.hair_highlight)
if h == nil then h = c end
local numColors = GetNumHairColors()
if numColors and numColors > 0 then
c = math.max(0, math.min(c, numColors - 1))
h = math.max(0, math.min(h, numColors - 1))
else
-- fallback clamp
c = math.max(0, math.min(c, 63))
h = math.max(0, math.min(h, 63))
end
SetPedHairColor(ped, c, h)
print(("[turfwar] appearance applied: gender=%s hair=%d/%d color=%d/%d")
:format(tostring(appearance.gender), hairDrawable, hairTexture, c, h))
-- Tell main.lua (optional) that appearance has been applied
TriggerEvent("turfwar:appearance:applied")
end
RegisterNetEvent("turfwar:appearance:update", function(row)
appearance = row
ApplyAppearanceNow()
end)
-- Request on resource start & spawn
AddEventHandler("onClientResourceStart", function(res)
if res ~= GetCurrentResourceName() then return end
CreateThread(function()
Wait(500)
TriggerServerEvent("turfwar:appearance:request")
end)
end)
AddEventHandler("playerSpawned", function()
CreateThread(function()
Wait(700)
TriggerServerEvent("turfwar:appearance:request")
end)
end)
-- Commands
RegisterCommand("tw_gender", function(_, args)
local g = args[1] and args[1]:lower() or ""
if g ~= "m" and g ~= "f" then
print("^3[turfwar]^7 Usage: /tw_gender m OR /tw_gender f")
return
end
TriggerServerEvent("turfwar:appearance:setGender", g)
end, false)
RegisterCommand("tw_hair_next", function()
if not appearance then return end
local ped = PlayerPedId()
if ped == 0 then return end
local maxHair = GetNumberOfPedDrawableVariations(ped, 2)
if maxHair <= 0 then
print("^1[turfwar]^7 No hair variations on this model.")
return
end
local cur = GetPedDrawableVariation(ped, 2)
local nxt = (cur + 1) % maxHair
local maxTex = GetNumberOfPedTextureVariations(ped, 2, nxt)
if maxTex <= 0 then maxTex = 1 end
local tex = 0
TriggerServerEvent("turfwar:appearance:setHair", nxt, tex, appearance.hair_color, appearance.hair_highlight)
end, false)
RegisterCommand("tw_hair_prev", function()
if not appearance then return end
local ped = PlayerPedId()
if ped == 0 then return end
local maxHair = GetNumberOfPedDrawableVariations(ped, 2)
if maxHair <= 0 then return end
local cur = GetPedDrawableVariation(ped, 2)
local prv = (cur - 1)
if prv < 0 then prv = maxHair - 1 end
TriggerServerEvent("turfwar:appearance:setHair", prv, 0, appearance.hair_color, appearance.hair_highlight)
end, false)
RegisterCommand("tw_haircolor", function(_, args)
if not appearance then return end
local c = tonumber(args[1])
local h = tonumber(args[2])
if c == nil then
print("^3[turfwar]^7 Usage: /tw_haircolor <color> [highlight]")
return
end
if h == nil then h = c end
TriggerServerEvent("turfwar:appearance:setHair", appearance.hair_drawable, appearance.hair_texture, c, h)
end, false)
RegisterCommand("tw_appearance", function()
print("^3[turfwar]^7 Commands:")
print("^3[turfwar]^7 /tw_gender m|f")
print("^3[turfwar]^7 /tw_hair_next (cycle)")
print("^3[turfwar]^7 /tw_hair_prev (cycle)")
print("^3[turfwar]^7 /tw_haircolor <color> [highlight]")
end, false)

108
client/atm_blips.lua Normal file
View File

@ -0,0 +1,108 @@
-- client/atm_blips.lua
print("^2[turfwar]^7 atm_blips.lua loaded")
if IsDuplicityVersion() then
print("^1[turfwar]^7 atm_blips.lua loaded on SERVER by mistake!")
return
end
Config = Config or {}
Config.Finance = Config.Finance or {}
-- =========================
-- Config defaults (blips)
-- =========================
Config.Finance.ATM_BLIPS_ENABLED = (Config.Finance.ATM_BLIPS_ENABLED ~= false)
Config.Finance.ATM_BLIP_SPRITE = Config.Finance.ATM_BLIP_SPRITE or 277
Config.Finance.ATM_BLIP_SCALE = Config.Finance.ATM_BLIP_SCALE or 0.55
Config.Finance.ATM_BLIP_COLOR = Config.Finance.ATM_BLIP_COLOR or 2
Config.Finance.ATM_BLIP_SHORT_RANGE = (Config.Finance.ATM_BLIP_SHORT_RANGE ~= false)
-- =========================
-- State
-- =========================
local ATM_BLIPS = {} -- array for easy cleanup
local ATM_BY_KEY = {} -- coordKey -> blip
-- =========================
-- Helpers
-- =========================
local function coordKey(x, y, z)
-- round to 0.5m to dedupe close matches
local rx = math.floor((x * 2.0) + 0.5) / 2.0
local ry = math.floor((y * 2.0) + 0.5) / 2.0
local rz = math.floor((z * 2.0) + 0.5) / 2.0
return (("%0.1f|%0.1f|%0.1f"):format(rx, ry, rz))
end
local function ClearATMBlips()
for _, b in ipairs(ATM_BLIPS) do
if DoesBlipExist(b) then RemoveBlip(b) end
end
ATM_BLIPS = {}
ATM_BY_KEY = {}
end
local function AddATMBlip(coords)
local key = coordKey(coords.x, coords.y, coords.z)
if ATM_BY_KEY[key] and DoesBlipExist(ATM_BY_KEY[key]) then return end
local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
SetBlipSprite(blip, Config.Finance.ATM_BLIP_SPRITE)
SetBlipScale(blip, Config.Finance.ATM_BLIP_SCALE)
SetBlipColour(blip, Config.Finance.ATM_BLIP_COLOR)
SetBlipDisplay(blip, 4)
SetBlipAsShortRange(blip, Config.Finance.ATM_BLIP_SHORT_RANGE)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("ATM")
EndTextCommandSetBlipName(blip)
ATM_BLIPS[#ATM_BLIPS + 1] = blip
ATM_BY_KEY[key] = blip
end
local function BuildATMBlips(atms)
ClearATMBlips()
if not atms or #atms == 0 then
print("^1[turfwar]^7 atm_blips.lua: Config.Finance.ATMS is empty (0) -> no ATM blips.")
return
end
for _, c in ipairs(atms) do
if type(c) == "vector3" then
AddATMBlip(c)
elseif type(c) == "table" and c.x and c.y and c.z then
AddATMBlip(vector3(c.x, c.y, c.z))
end
end
print(("^2[turfwar]^7 ATM blips created: %d"):format(#ATM_BLIPS))
end
-- If you still have an atm_coords.lua that fires this:
AddEventHandler("turfwar:atm:listReady", function(atms)
if not Config.Finance.ATM_BLIPS_ENABLED then return end
BuildATMBlips(atms)
end)
-- Boot: Mode B static list from config.lua
CreateThread(function()
Wait(500)
if not Config.Finance.ATM_BLIPS_ENABLED then
print("^3[turfwar]^7 ATM blips disabled in config")
return
end
local atms = Config.Finance.ATMS or {}
print(("^3[turfwar]^7 atm_blips.lua: boot -> ATMS=%d"):format(#atms))
BuildATMBlips(atms)
end)
AddEventHandler("onClientResourceStop", function(res)
if res ~= GetCurrentResourceName() then return end
ClearATMBlips()
end)

39
client/atm_coords.lua Normal file
View File

@ -0,0 +1,39 @@
-- client/atm_coords.lua
print("^2[turfwar]^7 atm_coords.lua loaded")
if IsDuplicityVersion() then
print("^1[turfwar]^7 atm_coords.lua loaded on SERVER by mistake!")
return
end
Config = Config or {}
Config.Finance = Config.Finance or {}
--========================================================
-- Static ATM coordinates (Mode B)
--========================================================
Config.Finance.ATMS = Config.Finance.ATMS or {
vector3(150.266, -1040.203, 29.374),
vector3(-1212.980, -330.841, 37.787),
vector3(-2962.582, 482.627, 15.703),
vector3(-112.202, 6469.295, 31.626),
vector3(314.187, -278.621, 54.170),
vector3(-351.534, -49.529, 49.042),
vector3(241.727, 220.706, 106.286),
vector3(1171.523, 2702.448, 38.175),
vector3(-1091.887, 2708.560, 18.955),
vector3(2683.011, 3286.536, 55.241),
vector3(-386.733, 6045.953, 31.501),
vector3(1703.138, 4933.593, 42.051),
vector3(540.042, 2671.007, 42.177),
vector3(2564.399, 2585.100, 38.016),
vector3(1822.639, 3683.095, 34.276),
}
print(("^2[turfwar]^7 atm_coords.lua: %d ATM coords ready"):format(#Config.Finance.ATMS))
-- Fire event so atm_blips.lua builds them
CreateThread(function()
Wait(250) -- give atm_blips.lua time to register handler
TriggerEvent("turfwar:atm:listReady", Config.Finance.ATMS)
end)

46
client/atm_nui.lua Normal file
View File

@ -0,0 +1,46 @@
-- client/atm_nui.lua (recommended: put callbacks in a dedicated file)
local atmToken = nil
local atmOpen = false
RegisterNetEvent("turfwar:atm:openGranted", function(token)
atmToken = token
atmOpen = true
SetNuiFocus(true, true)
SendNUIMessage({ type = "atm:open" })
end)
local function CloseATM()
atmOpen = false
atmToken = nil
SetNuiFocus(false, false)
SendNUIMessage({ type = "atm:close" })
end
RegisterNUICallback("atm:ready", function(_, cb)
-- optional: can be used if you want to delay open until UI loaded
cb({ ok = true })
end)
RegisterNUICallback("atm:close", function(_, cb)
CloseATM()
cb({ ok = true })
end)
RegisterNUICallback("atm:deposit", function(data, cb)
local amount = tonumber(data.amount) or 0
if amount <= 0 then cb({ ok = false, error = "bad_amount" }); return end
if not atmToken then cb({ ok = false, error = "no_token" }); return end
TriggerServerEvent("turfwar:finance:deposit", atmToken, amount)
cb({ ok = true })
end)
RegisterNUICallback("atm:withdraw", function(data, cb)
local amount = tonumber(data.amount) or 0
if amount <= 0 then cb({ ok = false, error = "bad_amount" }); return end
if not atmToken then cb({ ok = false, error = "no_token" }); return end
TriggerServerEvent("turfwar:finance:withdraw", atmToken, amount)
cb({ ok = true })
end)

84
client/dev.lua Normal file
View File

@ -0,0 +1,84 @@
-- dev.lua
RegisterCommand("tw_coords_turf", function()
local ped = PlayerPedId()
local c = GetEntityCoords(ped)
local found, groundZ = GetGroundZFor_3dCoord(c.x, c.y, c.z + 200.0, 0)
local z = found and groundZ or c.z
local msg = ("center = vector3(%.3f, %.3f, %.3f),"):format(c.x, c.y, z)
print("^3[turfwar]^7 " .. msg)
BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringPlayerName(msg)
EndTextCommandThefeedPostTicker(false, false)
end, false)
-- =========================================================
-- Killfeed NUI Self-Test (no PvP needed)
-- =========================================================
RegisterCommand("tw_kf", function(_, args)
local killer = args[1] or "O'Neil Tester"
local victim = args[2] or "Malita Tester"
-- Example: Gang 1 (yellow) kills Gang 2 (green)
local killerGang = tonumber(args[3]) or 1
local victimGang = tonumber(args[4]) or 2
local function rgbForGang(gangId)
local g = Config and Config.GANGS and Config.GANGS[tonumber(gangId) or 0]
if g and type(g.rgb) == "table" then
return { tonumber(g.rgb[1]) or 255, tonumber(g.rgb[2]) or 255, tonumber(g.rgb[3]) or 255 }
end
return {255,255,255}
end
SendNUIMessage({
type = "killfeed:add",
killerName= killer,
victimName= victim,
weapon = "Carbine Rifle",
headshot = true,
distance = 42,
killerRgb = rgbForGang(killerGang),
victimRgb = rgbForGang(victimGang),
})
print(("^2[turfwar]^7 Killfeed test sent -> %s (g%d) killed %s (g%d)"):format(killer, killerGang, victim, victimGang))
end, false)
RegisterCommand("tw_coords_ground", function()
local ped = PlayerPedId()
local c = GetEntityCoords(ped)
local h = GetEntityHeading(ped)
-- Raycast straight down from slightly above the player
local from = vector3(c.x, c.y, c.z + 1.0)
local to = vector3(c.x, c.y, c.z - 200.0)
-- Flags: 1=world, 16=objects, 256=vehicles; 511 is a good "hit most things"
local ray = StartShapeTestRay(from.x, from.y, from.z, to.x, to.y, to.z, 511, ped, 0)
local _, hit, hitCoords = GetShapeTestResult(ray)
local z = c.z
if hit == 1 and hitCoords then
z = hitCoords.z
end
local msg = ("Ground Coords (ray): vector4(%.3f, %.3f, %.3f, %.1f)"):format(c.x, c.y, z, h)
print("^3[turfwar]^7 " .. msg)
BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringPlayerName(msg)
EndTextCommandThefeedPostTicker(false, false)
end, false)
RegisterCommand("tw_hudtest", function()
SendNUIMessage({ type="capture:start", turfName="HUD TEST", fill="rgba(255,0,0,0.9)", bg="rgba(255,255,255,0.14)" })
Wait(250)
SendNUIMessage({ type="capture:set", t=0.5 })
end)