294 lines
10 KiB
Lua
294 lines
10 KiB
Lua
-- Prevent running if menu is disabled
|
|
if not TX_MENU_ENABLED then return end
|
|
|
|
-- ===============
|
|
-- This file contains functionality purely related
|
|
-- to player modes (noclip, godmode, super jump)
|
|
-- ===============
|
|
|
|
local noClipEnabled = false
|
|
local superJumpEnabled = false
|
|
local moveRateOverride = IS_FIVEM and 1.75 or 1.15
|
|
local function toggleSuperJump(enabled)
|
|
superJumpEnabled = enabled
|
|
if enabled then
|
|
sendPersistentAlert('superJumpEnabled', 'info', 'nui_menu.page_main.player_mode.superjump.success', true)
|
|
CreateThread(function()
|
|
local Wait = Wait
|
|
local pid = PlayerId()
|
|
local ped = PlayerPedId()
|
|
-- loop to keep player fast
|
|
local frameCounter = 0
|
|
while superJumpEnabled do
|
|
frameCounter = frameCounter + 1
|
|
if frameCounter > 200 then
|
|
RestorePlayerStamina(pid, 100.0)
|
|
ped = PlayerPedId()
|
|
frameCounter = 0
|
|
end
|
|
SetPedMoveRateOverride(ped, moveRateOverride)
|
|
SetSuperJumpThisFrame(pid)
|
|
Wait(0)
|
|
end
|
|
end)
|
|
else
|
|
clearPersistentAlert('superJumpEnabled')
|
|
end
|
|
end
|
|
|
|
|
|
local function toggleGodMode(enabled)
|
|
if enabled then
|
|
sendPersistentAlert('godModeEnabled', 'info', 'nui_menu.page_main.player_mode.godmode.success', true)
|
|
else
|
|
clearPersistentAlert('godModeEnabled')
|
|
end
|
|
SetEntityInvincible(PlayerPedId(), enabled)
|
|
end
|
|
|
|
-- Calculated in python using sklearn.linear_model.LinearRegression with the points:
|
|
-- H: 10, F: 20
|
|
-- H: 150, F: 250
|
|
local function getFallImpulse(H)
|
|
local coefficient = 1.6428571428571428
|
|
local intercept = 3.5714285714285836
|
|
return coefficient * H + intercept
|
|
end
|
|
|
|
-- NOTE: this depends on toggleGodMode(false) running _first_ before disabling noclip
|
|
local function disableRagdollingWhileFall()
|
|
CreateThread(function()
|
|
-- Check if ped is too close to the ground
|
|
local ped = PlayerPedId()
|
|
local pedHeight = GetEntityHeightAboveGround(ped)
|
|
if pedHeight == nil or pedHeight < 4.0 then
|
|
debugPrint('Ped is too close to the ground, returning')
|
|
return
|
|
end
|
|
|
|
-- Disable ragdolling
|
|
local pid = PlayerId()
|
|
SetEntityInvincible(ped, true)
|
|
if IS_FIVEM then
|
|
SetPlayerFallDistance(pid, 9000.0)
|
|
else
|
|
SetRagdollBlockingFlags(ped, (1 << 9)) --RBF_FALLING
|
|
end
|
|
|
|
-- Fall from big heights take forever, so force the ped down
|
|
-- Note, force is clamped to -250.0 by the engine
|
|
-- Note2, this needs to be applied before the fall starts
|
|
local downForce = getFallImpulse(pedHeight)
|
|
ApplyForceToEntity(
|
|
ped,
|
|
3, --MaxForceRot2
|
|
vector3(0.0, 0.0, -downForce), --force
|
|
vector3(0.0, 0.0, 0.0), --offset
|
|
0, --boneIndex
|
|
true, --isDirectionRel
|
|
true, --ignoreUpVec
|
|
true, --isForceRel
|
|
false, --p12
|
|
true --p13
|
|
)
|
|
debugPrint('Ped should fall, applying down force:', downForce)
|
|
|
|
--Await for ped to start falling for up to 1 second
|
|
--If it doesn't, asume that it is not going to fall and return
|
|
local fallAwaitLimit = 1000
|
|
local fallAwaitStep = 25
|
|
local fallAwaitElapsed = 0
|
|
while not IsPedFalling(ped) do
|
|
if fallAwaitElapsed >= fallAwaitLimit then
|
|
debugPrint('Ped did not fall, returning')
|
|
SetEntityInvincible(ped, false)
|
|
SetPlayerFallDistance(pid, -1)
|
|
return
|
|
end
|
|
fallAwaitElapsed = fallAwaitElapsed + fallAwaitStep
|
|
Wait(fallAwaitStep)
|
|
end
|
|
debugPrint('Ped is falling, disabling ragdolling')
|
|
|
|
-- Wait until ped stops falling
|
|
repeat
|
|
Wait(50)
|
|
until not IsPedFalling(ped)
|
|
debugPrint('Ped stopped falling, re-enabling ragdolling')
|
|
|
|
-- Re-enable ragdolling
|
|
Wait(750) --Probably not needed, but just in case
|
|
debugPrint('Re-enabling ragdolling')
|
|
SetEntityInvincible(ped, false)
|
|
if IS_FIVEM then
|
|
SetPlayerFallDistance(pid, -1)
|
|
else
|
|
ClearRagdollBlockingFlags(ped, (1 << 9)) --RBF_FALLING
|
|
end
|
|
end)
|
|
end
|
|
|
|
local freecamVeh = 0
|
|
local isVehAHorse = false
|
|
local setLocallyInvisibleFunc = IS_FIVEM and SetEntityLocallyInvisible or SetPlayerInvisibleLocally
|
|
local function toggleFreecam(enabled)
|
|
noClipEnabled = enabled
|
|
local ped = PlayerPedId()
|
|
SetEntityVisible(ped, not enabled)
|
|
SetEntityInvincible(ped, enabled)
|
|
FreezeEntityPosition(ped, enabled)
|
|
|
|
if enabled then
|
|
freecamVeh = GetVehiclePedIsIn(ped, false)
|
|
if IsPedOnMount(ped) then
|
|
isVehAHorse = true
|
|
freecamVeh = GetMount(ped)
|
|
end
|
|
if freecamVeh > 0 then
|
|
NetworkSetEntityInvisibleToNetwork(freecamVeh, true)
|
|
SetEntityCollision(freecamVeh, false, false)
|
|
SetEntityVisible(freecamVeh, false)
|
|
FreezeEntityPosition(freecamVeh, true)
|
|
if not isVehAHorse then
|
|
SetVehicleCanBreak(freecamVeh, false)
|
|
SetVehicleWheelsCanBreak(freecamVeh, false)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function enableNoClip()
|
|
lastTpCoords = GetEntityCoords(ped)
|
|
|
|
SetFreecamActive(true)
|
|
StartFreecamThread()
|
|
|
|
Citizen.CreateThread(function()
|
|
while IsFreecamActive() do
|
|
setLocallyInvisibleFunc(ped, true)
|
|
if freecamVeh > 0 then
|
|
if DoesEntityExist(freecamVeh) then
|
|
setLocallyInvisibleFunc(freecamVeh, true) -- only works for players in RedM, but to prevent errors.
|
|
else
|
|
freecamVeh = 0
|
|
end
|
|
end
|
|
Wait(0)
|
|
end
|
|
|
|
if freecamVeh > 0 and DoesEntityExist(freecamVeh) then
|
|
local coords = GetEntityCoords(ped)
|
|
NetworkSetEntityInvisibleToNetwork(freecamVeh, false)
|
|
SetEntityCoords(freecamVeh, coords[1], coords[2], coords[3], false, false, false, false)
|
|
SetVehicleOnGroundProperly(freecamVeh)
|
|
SetEntityCollision(freecamVeh, true, true)
|
|
SetEntityVisible(freecamVeh, true)
|
|
FreezeEntityPosition(freecamVeh, false)
|
|
|
|
if isVehAHorse then
|
|
Citizen.InvokeNative(0x028F76B6E78246EB, ped, freecamVeh, -1) --SetPedOntoMount
|
|
else
|
|
SetEntityAlpha(freecamVeh, 125)
|
|
SetPedIntoVehicle(ped, freecamVeh, -1)
|
|
local persistVeh = freecamVeh --since freecamVeh is erased down below
|
|
CreateThread(function()
|
|
Wait(2000)
|
|
ResetEntityAlpha(persistVeh)
|
|
SetVehicleCanBreak(persistVeh, true)
|
|
SetVehicleWheelsCanBreak(persistVeh, true)
|
|
end)
|
|
end
|
|
end
|
|
freecamVeh = 0
|
|
end)
|
|
end
|
|
|
|
local function disableNoClip()
|
|
SetFreecamActive(false)
|
|
if IS_FIVEM then
|
|
SetGameplayCamRelativeHeading(0)
|
|
else
|
|
Citizen.InvokeNative(0x14F3947318CA8AD2, 0.0, 0.0) -- SetThirdPersonCamRelativeHeadingLimitsThisUpdate
|
|
end
|
|
if freecamVeh == 0 then
|
|
disableRagdollingWhileFall()
|
|
end
|
|
end
|
|
|
|
if not IsFreecamActive() and enabled then
|
|
sendPersistentAlert('noClipEnabled', 'info', 'nui_menu.page_main.player_mode.noclip.success', true)
|
|
enableNoClip()
|
|
end
|
|
|
|
if IsFreecamActive() and not enabled then
|
|
clearPersistentAlert('noClipEnabled')
|
|
disableNoClip()
|
|
end
|
|
end
|
|
|
|
|
|
-- Ask server for playermode change and sends nearby playerlist
|
|
local function askChangePlayerMode(mode)
|
|
debugPrint("Requesting player mode change to " .. mode)
|
|
|
|
-- get nearby players to receive the ptfx sync event
|
|
local players = GetActivePlayers()
|
|
local nearbyPlayers = {}
|
|
for _, player in ipairs(players) do
|
|
nearbyPlayers[#nearbyPlayers + 1] = GetPlayerServerId(player)
|
|
end
|
|
|
|
TriggerServerEvent('txsv:req:changePlayerMode', mode, nearbyPlayers)
|
|
end
|
|
|
|
-- NoClip toggle keybind
|
|
RegisterCommand('txAdmin:menu:noClipToggle', function()
|
|
if not menuIsAccessible then return end
|
|
if not DoesPlayerHavePerm(menuPermissions, 'players.playermode') then
|
|
return sendSnackbarMessage('error', 'nui_menu.misc.no_perms', true)
|
|
end
|
|
askChangePlayerMode(noClipEnabled and 'none' or 'noclip')
|
|
end)
|
|
|
|
-- Menu callback to change the player mode
|
|
RegisterSecureNuiCallback('playerModeChanged', function(mode, cb)
|
|
askChangePlayerMode(mode)
|
|
cb({})
|
|
end)
|
|
|
|
-- [[ Player mode changed cb event ]]
|
|
RegisterNetEvent('txcl:setPlayerMode', function(mode, ptfx)
|
|
if ptfx then
|
|
CreatePlayerModePtfxLoop(PlayerPedId(), true)
|
|
end
|
|
|
|
--FIXME: Refactor this file:
|
|
-- * keep state of the current game mode in a var
|
|
-- * when changing mode, disable the current mode
|
|
-- * and only then enable the new mode
|
|
-- * separate the noclip enable and disable functions
|
|
-- * rename "freecam" to "noclip" in the code
|
|
|
|
--NOTE: always do the toggleX(true) at the bottom to prevent
|
|
--conflict with some other native like toggleGodMode(false) after toggleFreecam(true)
|
|
--which disables the SetEntityInvincible(true)
|
|
|
|
--FIXME: for now, always disable godmode before disabling noclip
|
|
if mode == 'godmode' then
|
|
toggleFreecam(false)
|
|
toggleSuperJump(false)
|
|
toggleGodMode(true)
|
|
elseif mode == 'noclip' then
|
|
toggleGodMode(false)
|
|
toggleSuperJump(false)
|
|
toggleFreecam(true)
|
|
elseif mode == 'superjump' then
|
|
toggleGodMode(false)
|
|
toggleFreecam(false)
|
|
toggleSuperJump(true)
|
|
elseif mode == 'none' then
|
|
toggleGodMode(false)
|
|
toggleFreecam(false)
|
|
toggleSuperJump(false)
|
|
end
|
|
end)
|