From 72bf3dbec15593902de3fb00ffbcc67012ab65b8 Mon Sep 17 00:00:00 2001 From: tanthius Date: Thu, 12 Feb 2026 04:14:54 +0000 Subject: [PATCH] Upload files to "client" --- client/appearance.lua | 201 ++++++++++++++++++++++++++++++++++++++++++ client/atm_blips.lua | 108 +++++++++++++++++++++++ client/atm_coords.lua | 39 ++++++++ client/atm_nui.lua | 46 ++++++++++ client/dev.lua | 84 ++++++++++++++++++ 5 files changed, 478 insertions(+) create mode 100644 client/appearance.lua create mode 100644 client/atm_blips.lua create mode 100644 client/atm_coords.lua create mode 100644 client/atm_nui.lua create mode 100644 client/dev.lua diff --git a/client/appearance.lua b/client/appearance.lua new file mode 100644 index 0000000..6f671fa --- /dev/null +++ b/client/appearance.lua @@ -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 [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 [highlight]") +end, false) diff --git a/client/atm_blips.lua b/client/atm_blips.lua new file mode 100644 index 0000000..6e64bbb --- /dev/null +++ b/client/atm_blips.lua @@ -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) diff --git a/client/atm_coords.lua b/client/atm_coords.lua new file mode 100644 index 0000000..a895c05 --- /dev/null +++ b/client/atm_coords.lua @@ -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) diff --git a/client/atm_nui.lua b/client/atm_nui.lua new file mode 100644 index 0000000..7234394 --- /dev/null +++ b/client/atm_nui.lua @@ -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) diff --git a/client/dev.lua b/client/dev.lua new file mode 100644 index 0000000..619d3c4 --- /dev/null +++ b/client/dev.lua @@ -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) +