--[[ https://github.com/alp1x/um-ped-scale Main scaling function based off of this script ███╗ ██╗ █████╗ ███████╗███████╗ ██████╗ ███████╗██████╗ ███████╗ ██████╗ █████╗ ██╗ ███████╗██████╗ ████╗ ██║██╔══██╗██╔════╝██╔════╝ ██╔══██╗██╔════╝██╔══██╗██╔════╝██╔════╝██╔══██╗██║ ██╔════╝██╔══██╗ ██╔██╗ ██║███████║███████╗███████╗ ██████╔╝█████╗ ██║ ██║███████╗██║ ███████║██║ █████╗ ██████╔╝ ██║╚██╗██║██╔══██║╚════██║╚════██║ ██╔═══╝ ██╔══╝ ██║ ██║╚════██║██║ ██╔══██║██║ ██╔══╝ ██╔══██╗ ██║ ╚████║██║ ██║███████║███████║███████╗██║ ███████╗██████╔╝███████║╚██████╗██║ ██║███████╗███████╗██║ ██║ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚══════╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ https://discord.gg/nass Please support the development of this script by joining our discord server. ]] local function norm(vec) local mag = math.sqrt(vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) if mag > 0 then return vec / mag end return vec end -- Per-ped smoothed Z, keyed by ped entity handle local smoothedZ = {} local function applyScaleToEntity(ped, scale) local forward, right, upVector, position = GetEntityMatrix(ped) local forwardNorm = norm(forward) * scale local rightNorm = norm(right) * scale local upNorm = norm(upVector) * scale -- Only look ahead when moving up, cast straight down when descending local vel = GetEntityVelocity(ped) local isMovingUp = vel.z > 0.1 local lookAhead = isMovingUp and 0.3 or 0.0 local rayStart = vector3(position.x + vel.x * lookAhead, position.y + vel.y * lookAhead, position.z + 2.0) local rayEnd = vector3(position.x + vel.x * lookAhead, position.y + vel.y * lookAhead, position.z - 2.5) local ray = StartShapeTestRay(rayStart.x, rayStart.y, rayStart.z, rayEnd.x, rayEnd.y, rayEnd.z, 1 | 16, ped, 0) local _, hit, groundPos = GetShapeTestResult(ray) -- Fallback: straight down with longer range for flat ground / last step if hit ~= 1 then local rayStart2 = vector3(position.x, position.y, position.z + 2.0) local rayEnd2 = vector3(position.x, position.y, position.z - 5.0) local ray2 = StartShapeTestRay(rayStart2.x, rayStart2.y, rayStart2.z, rayEnd2.x, rayEnd2.y, rayEnd2.z, 1 | 16, ped, 0) _, hit, groundPos = GetShapeTestResult(ray2) end local groundZ if hit == 1 then groundZ = groundPos.z else local found, gz = GetGroundZFor_3dCoord(position.x, position.y, position.z, false) groundZ = found and gz or position.z end local targetZ = groundZ + scale + 0.04 local pedKey = tostring(ped) if smoothedZ[pedKey] == nil then smoothedZ[pedKey] = targetZ end if targetZ > smoothedZ[pedKey] then -- Going up: snap for large steps, lerp for small ones local diff = targetZ - smoothedZ[pedKey] if diff > 0.3 then smoothedZ[pedKey] = targetZ else smoothedZ[pedKey] = smoothedZ[pedKey] + diff * 0.3 end else -- Going down: always lerp smoothly smoothedZ[pedKey] = smoothedZ[pedKey] + (targetZ - smoothedZ[pedKey]) * 0.18 end SetEntityMatrix(ped, forwardNorm.x, forwardNorm.y, forwardNorm.z, rightNorm.x, rightNorm.y, rightNorm.z, upNorm.x, upNorm.y, upNorm.z, position.x, position.y, smoothedZ[pedKey] ) end local syncedScales = {} -- Resolve the ped that belongs to a server id. Returns 0 when that player is -- not currently in scope. IMPORTANT: never fall through to GetPlayerPed(-1), -- which returns the LOCAL player's ped and would scale the wrong ped. local function getPedForServerId(src) local playerIdx = GetPlayerFromServerId(src) if playerIdx == -1 then return 0 end return GetPlayerPed(playerIdx) end RegisterNetEvent('nass_pedscaler:syncCurrentScaling', function(players) for k, v in pairs(players) do TriggerEvent('nass_pedscaler:syncScale', tonumber(k), v) end end) RegisterNetEvent('nass_pedscaler:syncScale', function(src, scale) if syncedScales[tostring(src)] ~= nil then syncedScales[tostring(src)] = nil local ped = getPedForServerId(src) if ped ~= 0 and DoesEntityExist(ped) then applyScaleToEntity(ped, 1.0) end Wait(100) end syncedScales[tostring(src)] = scale if scale ~= nil then startScale(src, scale) else local ped = getPedForServerId(src) if ped ~= 0 and DoesEntityExist(ped) then applyScaleToEntity(ped, 1.0) end end end) function startScale(src, scale) CreateThread(function() while syncedScales[tostring(src)] ~= nil do -- Re-resolve every frame: the target player may not be in scope -- yet (e.g. right after joining) and only stream in later. local ped = getPedForServerId(src) if ped ~= 0 and DoesEntityExist(ped) then applyScaleToEntity(ped, scale) if Config.scaling.scaleSpeed.enabled then SetPedMoveRateOverride(ped, Config.scaling.scaleSpeed.inverse and (1 / scale) or scale) end end Wait(0) end end) end RegisterNUICallback('slider_updated', function(data, cb) local serverId = GetPlayerServerId(PlayerId()) if syncedScales[tostring(serverId)] ~= nil then syncedScales[tostring(serverId)] = nil end local playerPed = PlayerPedId() if DoesEntityExist(playerPed) then applyScaleToEntity(playerPed, data.value) end cb('ok') end) RegisterNUICallback('save_scale', function(data, cb) TriggerServerEvent('nass_pedscaler:syncScale', tonumber(data.scale)) closeMenu() cb('ok') end) RegisterNUICallback('close_menu', function(data, cb) closeMenu() cb('ok') end) function closeMenu() SetNuiFocus(false, false) SendNUIMessage({ action = "visible", data = false }) end RegisterNUICallback('get_config', function(data, cb) local serverId = GetPlayerServerId(PlayerId()) SendNUIMessage({ action = "config_data", data = { scaling = Config.scaling, currentScale = syncedScales[tostring(serverId)] or 1.0 } }) cb('ok') end) RegisterNUICallback('getLocale', function(data, cb) cb(Config.locale) end) RegisterNetEvent('nass_pedscaler:openMenu', function() SetNuiFocus(true, true) SendNUIMessage({ action = "visible", data = true, }) end)