Turfwar/client/environment.lua

203 lines
6.6 KiB
Lua

print("^2[environment]^7 Cash drop system loaded")
-- =====================
-- CONFIG
-- =====================
local MAX_CASH = 200 -- Maximum possible drop
local WEIGHT_POWER = 2.2 -- Higher = lower values more common
local CHECK_INTERVAL = 1000 -- How often we scan peds (ms)
local PICKUP_RADIUS = 1.25
local PICKUP_CHECK_MS = 200
local PICKUP_COOLDOWN = 750 -- ms between pickup attempts per drop
-- Prevent duplicate drops per ped handle
local processedPeds = {}
-- dropId -> { obj=entity, coords=vector3, amount=number }
local spawnedDrops = {}
local pickupCooldown = {} -- dropId -> lastAttempt GetGameTimer()
-- =====================
-- WEIGHTED RANDOM CASH
-- =====================
local function getWeightedCash()
local roll = math.random()
local weighted = roll ^ WEIGHT_POWER
return math.floor(weighted * MAX_CASH)
end
-- =====================
-- PED DEATH MONITOR
-- =====================
CreateThread(function()
math.randomseed(GetGameTimer())
while true do
Wait(CHECK_INTERVAL)
local peds = GetGamePool("CPed")
for _, ped in ipairs(peds) do
if DoesEntityExist(ped)
and not IsPedAPlayer(ped)
and IsEntityDead(ped)
and not processedPeds[ped] then
processedPeds[ped] = true
local cash = getWeightedCash()
if cash > 0 then
local c = GetEntityCoords(ped)
TriggerServerEvent("environment:pedCashDrop", cash, { x = c.x, y = c.y, z = c.z })
end
end
end
end
end)
-- =====================
-- ARG PARSER (server compatibility)
-- =====================
local function parseSpawnArgs(p1, p2, p3, p4, p5)
-- Preferred (your server currently uses):
-- (dropId, amount, coordsTable)
-- Also supports:
-- (dropId, amount, x, y, z)
-- (amount, coordsTable) [fallback id]
-- (amount, x, y, z) [fallback id]
local dropId, amount, coords
-- (dropId, amount, coordsTable) OR (dropId, amount, x, y, z)
if type(p1) == "number" and (type(p2) == "number" or type(p2) == "string") then
local maybeDropId = tonumber(p1)
local maybeAmount = tonumber(p2) or 0
if type(p3) == "table" and p3.x and p3.y and p3.z then
dropId = maybeDropId
amount = maybeAmount
coords = vector3(p3.x, p3.y, p3.z)
return dropId, amount, coords
elseif type(p3) == "number" and type(p4) == "number" and type(p5) == "number" then
dropId = maybeDropId
amount = maybeAmount
coords = vector3(p3, p4, p5)
return dropId, amount, coords
end
end
-- (amount, coordsTable) OR (amount, x, y, z) -> local fallback id
local maybeAmount = tonumber(p1) or 0
if type(p2) == "table" and p2.x and p2.y and p2.z then
dropId = GetGameTimer() + math.random(1, 999999)
amount = maybeAmount
coords = vector3(p2.x, p2.y, p2.z)
return dropId, amount, coords
elseif type(p2) == "number" and type(p3) == "number" and type(p4) == "number" then
dropId = GetGameTimer() + math.random(1, 999999)
amount = maybeAmount
coords = vector3(p2, p3, p4)
return dropId, amount, coords
end
return nil, nil, nil
end
-- =====================
-- CASH PICKUP SPAWN
-- =====================
RegisterNetEvent("environment:spawnCashPickup", function(p1, p2, p3, p4, p5)
local dropId, amount, coords = parseSpawnArgs(p1, p2, p3, p4, p5)
if not dropId or not coords then
print(("^1[environment]^7 spawnCashPickup: bad args types: %s %s %s %s %s")
:format(type(p1), type(p2), type(p3), type(p4), type(p5)))
return
end
amount = tonumber(amount) or 0
if amount <= 0 then return end
-- If already exists, delete old one first
local existing = spawnedDrops[dropId]
if existing and existing.obj and DoesEntityExist(existing.obj) then
DeleteEntity(existing.obj)
end
local model = GetHashKey("prop_cash_pile_01")
RequestModel(model)
while not HasModelLoaded(model) do Wait(0) end
local obj = CreateObject(model, coords.x, coords.y, coords.z - 0.9, false, false, false)
PlaceObjectOnGroundProperly(obj)
FreezeEntityPosition(obj, true)
SetEntityAsMissionEntity(obj, true, true)
spawnedDrops[dropId] = { obj = obj, coords = coords, amount = amount }
-- print(("[environment] Cash drop #%s: $%s at %.2f %.2f %.2f"):format(dropId, amount, coords.x, coords.y, coords.z))
end)
-- =====================
-- WALK-OVER COLLECT LOOP
-- =====================
CreateThread(function()
while true do
Wait(PICKUP_CHECK_MS)
local ped = PlayerPedId()
if not ped or ped == 0 then goto continue end
if IsEntityDead(ped) then goto continue end
local pcoords = GetEntityCoords(ped)
local now = GetGameTimer()
for dropId, data in pairs(spawnedDrops) do
if data and data.coords and data.obj and DoesEntityExist(data.obj) then
local dist = #(pcoords - data.coords)
if dist <= PICKUP_RADIUS then
local last = pickupCooldown[dropId] or 0
if (now - last) > PICKUP_COOLDOWN then
pickupCooldown[dropId] = now
TriggerServerEvent("environment:pickupCash", dropId)
end
end
end
end
::continue::
end
end)
-- =====================
-- FEEDBACK
-- =====================
RegisterNetEvent("environment:pickupFeedback", function(amount)
amount = tonumber(amount) or 0
if amount <= 0 then return end
PlaySoundFrontend(-1, "PICK_UP", "HUD_FRONTEND_DEFAULT_SOUNDSET", true)
BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringPlayerName(("+~g~$%d~s~"):format(amount))
EndTextCommandThefeedPostTicker(false, true)
end)
-- =====================
-- REMOVE / DESPAWN
-- =====================
RegisterNetEvent("environment:removeCashPickup", function(dropId)
dropId = tonumber(dropId)
if not dropId then return end
local data = spawnedDrops[dropId]
if data and data.obj and DoesEntityExist(data.obj) then
-- Helpful in some cases where entity control is finicky
NetworkRequestControlOfEntity(data.obj)
SetEntityAsMissionEntity(data.obj, true, true)
DeleteEntity(data.obj)
end
spawnedDrops[dropId] = nil
pickupCooldown[dropId] = nil
end)