487 lines
15 KiB
Lua
487 lines
15 KiB
Lua
IsProne = false
|
|
local isCrouched = false
|
|
local isCrawling = false
|
|
local inAction = false
|
|
local proneType = "onfront"
|
|
local lastKeyPress = 0
|
|
|
|
|
|
-- Utils --
|
|
local function LoadAnimDict(dict)
|
|
RequestAnimDict(dict)
|
|
while not HasAnimDictLoaded(dict) do
|
|
Citizen.Wait(0)
|
|
end
|
|
end
|
|
|
|
local function LoadAnimSet(set)
|
|
RequestAnimSet(set)
|
|
while not HasAnimSetLoaded(set) do
|
|
Citizen.Wait(0)
|
|
end
|
|
end
|
|
|
|
local function IsPlayerAiming(player)
|
|
return IsPlayerFreeAiming(player) or IsAimCamActive() or IsAimCamThirdPersonActive()
|
|
end
|
|
|
|
local function CanPlayerCrouchCrawl(playerPed)
|
|
if not IsPedOnFoot(playerPed) or IsPedJumping(playerPed) or IsPedFalling(playerPed) or IsPedInjured(playerPed) or IsPedInMeleeCombat(playerPed) or IsPedRagdoll(playerPed) then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function PlayAnimOnce(playerPed, animDict, animName, blendInSpeed, blendOutSpeed, duration, startTime)
|
|
LoadAnimDict(animDict)
|
|
TaskPlayAnim(playerPed, animDict, animName, blendInSpeed or 2.0, blendOutSpeed or 2.0, duration or -1, 0, startTime or 0.0, false, false, false)
|
|
RemoveAnimDict(animDict)
|
|
end
|
|
|
|
local function ChangeHeadingSmooth(playerPed, amount, time)
|
|
local times = math.abs(amount)
|
|
local test = amount / times
|
|
local wait = time / times
|
|
|
|
for _i = 1, times do
|
|
Wait(wait)
|
|
SetEntityHeading(playerPed, GetEntityHeading(playerPed) + test)
|
|
end
|
|
end
|
|
|
|
|
|
-- Crouching --
|
|
local function ResetCrouch()
|
|
local playerPed = PlayerPedId()
|
|
|
|
ResetPedStrafeClipset(playerPed)
|
|
ResetPedWeaponMovementClipset(playerPed)
|
|
SetPedMaxMoveBlendRatio(playerPed, 1.0)
|
|
SetPedCanPlayAmbientAnims(playerPed, true)
|
|
|
|
-- Applies the previous walk style (or resets to default if non had been set)
|
|
local walkStyle = GetResourceKvpString("walkstyle")
|
|
if walkStyle ~= nil then
|
|
LoadAnimSet(walkStyle)
|
|
SetPedMovementClipset(playerPed, walkStyle, 0.6)
|
|
RemoveAnimSet(walkStyle)
|
|
else
|
|
ResetPedMovementClipset(playerPed, 0.5)
|
|
end
|
|
|
|
RemoveAnimSet("move_ped_crouched")
|
|
end
|
|
|
|
local function CrouchThread()
|
|
CreateThread(function()
|
|
local playerId = PlayerId()
|
|
|
|
while isCrouched do
|
|
local playerPed = PlayerPedId()
|
|
|
|
-- Checks if the player is falling, in vehicle, dead etc.
|
|
if not CanPlayerCrouchCrawl(playerPed) then
|
|
isCrouched = false
|
|
break
|
|
end
|
|
|
|
-- Limit the speed that the player can walk when aiming
|
|
if IsPlayerAiming(playerId) then
|
|
SetPedMaxMoveBlendRatio(playerPed, 0.15)
|
|
end
|
|
|
|
-- This blocks the ped from standing up and playing idle anims (this needs to be looped)
|
|
SetPedCanPlayAmbientAnims(playerPed, false)
|
|
|
|
-- Disables "INPUT_DUCK" and blocks action mode
|
|
DisableControlAction(0, 36, true)
|
|
if IsPedUsingActionMode(playerPed) == 1 then
|
|
SetPedUsingActionMode(playerPed, false, -1, "DEFAULT_ACTION")
|
|
end
|
|
|
|
-- Disable first person
|
|
DisableFirstPersonCamThisFrame()
|
|
|
|
Wait(0)
|
|
end
|
|
|
|
-- Reset walk style and ped variables
|
|
ResetCrouch()
|
|
end)
|
|
end
|
|
|
|
local function StartCrouch()
|
|
isCrouched = true
|
|
LoadAnimSet("move_ped_crouched")
|
|
local playerPed = PlayerPedId()
|
|
|
|
-- Force leave stealth mode
|
|
if GetPedStealthMovement(playerPed) == 1 then
|
|
SetPedStealthMovement(playerPed, false, "DEFAULT_ACTION")
|
|
Wait(100)
|
|
end
|
|
|
|
-- Force leave first person view
|
|
if GetFollowPedCamViewMode() == 4 then
|
|
SetFollowPedCamViewMode(0) -- THIRD_PERSON_NEAR
|
|
end
|
|
|
|
SetPedMovementClipset(playerPed, "move_ped_crouched", 0.6)
|
|
SetPedStrafeClipset(playerPed, "move_ped_crouched_strafing")
|
|
|
|
CrouchThread()
|
|
end
|
|
|
|
local function AttemptCrouch(playerPed)
|
|
if CanPlayerCrouchCrawl(playerPed) then
|
|
StartCrouch()
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function CrouchKeyPressed()
|
|
-- If we already are doing something, then don't continue
|
|
if inAction then
|
|
return
|
|
end
|
|
|
|
-- If crouched then stop crouching
|
|
if isCrouched then
|
|
isCrouched = false
|
|
return
|
|
end
|
|
|
|
-- Get the player ped
|
|
local playerPed = PlayerPedId()
|
|
|
|
if Config.CrouchOverride then
|
|
DisableControlAction(0, 36, true) -- Disable INPUT_DUCK this frame
|
|
else
|
|
-- Get +crouch and INPUT_DUCK keys
|
|
local crouchKey = GetControlInstructionalButton(0, 0xD2D0BEBA, false)
|
|
local duckKey = GetControlInstructionalButton(0, 36, false)
|
|
|
|
-- If they are the same and we aren't prone, then check if we are in stealth mode and how long ago the last button press was.
|
|
if crouchKey == duckKey and not IsProne then
|
|
local timer = GetGameTimer()
|
|
|
|
-- If we are in stealth mode and we have already pressed the button in the last second
|
|
if GetPedStealthMovement(playerPed) == 1 and timer - lastKeyPress < 1000 then
|
|
DisableControlAction(0, 36, true) -- Disable INPUT_DUCK this frame
|
|
lastKeyPress = 0
|
|
AttemptCrouch(playerPed)
|
|
return
|
|
end
|
|
lastKeyPress = timer
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Attempt to crouch, if we were successful, then also check if we are prone, if so then play an animaiton
|
|
if AttemptCrouch(playerPed) and IsProne then
|
|
inAction = true
|
|
IsProne = false
|
|
PlayAnimOnce(playerPed, "get_up@directional@transition@prone_to_knees@crawl", "front", nil, nil, 780)
|
|
Wait(780)
|
|
inAction = false
|
|
end
|
|
end
|
|
|
|
|
|
-- Crawling --
|
|
local function ShouldPlayerDiveToCrawl(playerPed)
|
|
if IsPedRunning(playerPed) or IsPedSprinting(playerPed) then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
local function PlayIdleCrawlAnim(playerPed, heading, blendInSpeed)
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
TaskPlayAnimAdvanced(playerPed, "move_crawl", proneType.."_fwd", playerCoords.x, playerCoords.y, playerCoords.z, 0.0, 0.0, heading or GetEntityHeading(playerPed), blendInSpeed or 2.0, 2.0, -1, 2, 1.0, false, false)
|
|
end
|
|
|
|
local function PlayExitCrawlAnims(forceEnd)
|
|
if not forceEnd then
|
|
inAction = true
|
|
local playerPed = PlayerPedId()
|
|
|
|
if proneType == "onfront" then
|
|
PlayAnimOnce(playerPed, "get_up@directional@transition@prone_to_knees@crawl", "front", nil, nil, 780)
|
|
|
|
-- Only stand fully up if we are not crouching
|
|
if not isCrouched then
|
|
Wait(780)
|
|
PlayAnimOnce(playerPed, "get_up@directional@movement@from_knees@standard", "getup_l_0", nil, nil, 1300)
|
|
end
|
|
else
|
|
PlayAnimOnce(playerPed, "get_up@directional@transition@prone_to_seated@crawl", "back", 16.0, nil, 950)
|
|
|
|
-- Only stand fully up if we are not crouching
|
|
if not isCrouched then
|
|
Wait(950)
|
|
PlayAnimOnce(playerPed, "get_up@directional@movement@from_seated@standard", "get_up_l_0", nil, nil, 1300)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function Crawl(playerPed, type, direction)
|
|
isCrawling = true
|
|
|
|
TaskPlayAnim(playerPed, "move_crawl", type.."_"..direction, 8.0, -8.0, -1, 2, 0.0, false, false, false)
|
|
|
|
local time = {
|
|
["onfront"] = {
|
|
["fwd"] = 820,
|
|
["bwd"] = 990
|
|
},
|
|
["onback"] = {
|
|
["fwd"] = 1200,
|
|
["bwd"] = 1200
|
|
}
|
|
}
|
|
|
|
SetTimeout(time[type][direction], function()
|
|
isCrawling = false
|
|
end)
|
|
end
|
|
|
|
local function CrawlFlip(playerPed)
|
|
inAction = true
|
|
local heading = GetEntityHeading(playerPed)
|
|
|
|
if proneType == "onfront" then
|
|
proneType = "onback"
|
|
|
|
PlayAnimOnce(playerPed, "get_up@directional_sweep@combat@pistol@front", "front_to_prone", 2.0)
|
|
ChangeHeadingSmooth(playerPed, -18.0, 3600)
|
|
else
|
|
proneType = "onfront"
|
|
|
|
PlayAnimOnce(playerPed, "move_crawlprone2crawlfront", "back", 2.0, nil, -1)
|
|
ChangeHeadingSmooth(playerPed, 12.0, 1700)
|
|
end
|
|
|
|
PlayIdleCrawlAnim(playerPed, heading + 180.0)
|
|
Wait(400)
|
|
inAction = false
|
|
end
|
|
|
|
local function CrawlThread()
|
|
CreateThread(function()
|
|
Wait(400)
|
|
|
|
local forceEnd = false
|
|
|
|
while IsProne do
|
|
local playerPed = PlayerPedId()
|
|
|
|
-- Checks if the player is falling, in vehicle, dead etc.
|
|
if not CanPlayerCrouchCrawl(playerPed) or IsEntityInWater(playerPed) then
|
|
ClearPedTasks(playerPed)
|
|
IsProne = false
|
|
forceEnd = true
|
|
break
|
|
end
|
|
|
|
-- Handles forwad/backward movement
|
|
local forward, backwards = IsControlPressed(0, 32), IsControlPressed(0, 33) -- INPUT_MOVE_UP_ONLY, INPUT_MOVE_DOWN_ONLY
|
|
if not isCrawling then
|
|
if forward then -- Forward
|
|
Crawl(playerPed, proneType, "fwd")
|
|
elseif backwards then -- Back
|
|
Crawl(playerPed, proneType, "bwd")
|
|
end
|
|
end
|
|
|
|
-- Moving left/right
|
|
if IsControlPressed(0, 34) then -- INPUT_MOVE_LEFT_ONLY
|
|
if isCrawling then
|
|
local headingDiff = forward and 1.0 or -1.0
|
|
SetEntityHeading(playerPed, GetEntityHeading(playerPed) + headingDiff)
|
|
else
|
|
inAction = true
|
|
if proneType == "onfront" then
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
TaskPlayAnimAdvanced(playerPed, "move_crawlprone2crawlfront", "left", playerCoords.x, playerCoords.y, playerCoords.z, 0.0, 0.0, GetEntityHeading(playerPed), 2.0, 2.0, -1, 2, 0.1, false, false)
|
|
ChangeHeadingSmooth(playerPed, -10.0, 300)
|
|
Wait(700)
|
|
else
|
|
PlayAnimOnce(playerPed, "get_up@directional_sweep@combat@pistol@left", "left_to_prone")
|
|
ChangeHeadingSmooth(playerPed, 25.0, 400)
|
|
PlayIdleCrawlAnim(playerPed)
|
|
Wait(600)
|
|
end
|
|
inAction = false
|
|
end
|
|
elseif IsControlPressed(0, 35) then -- INPUT_MOVE_RIGHT_ONLY
|
|
if isCrawling then
|
|
local headingDiff = backwards and 1.0 or -1.0
|
|
SetEntityHeading(playerPed, GetEntityHeading(playerPed) + headingDiff)
|
|
else
|
|
inAction = true
|
|
if proneType == "onfront" then
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
TaskPlayAnimAdvanced(playerPed, "move_crawlprone2crawlfront", "right", playerCoords.x, playerCoords.y, playerCoords.z, 0.0, 0.0, GetEntityHeading(playerPed), 2.0, 2.0, -1, 2, 0.1, false, false)
|
|
ChangeHeadingSmooth(playerPed, 10.0, 300)
|
|
Wait(700)
|
|
else
|
|
PlayAnimOnce(playerPed, "get_up@directional_sweep@combat@pistol@right", "right_to_prone")
|
|
ChangeHeadingSmooth(playerPed, -25.0, 400)
|
|
PlayIdleCrawlAnim(playerPed)
|
|
Wait(600)
|
|
end
|
|
inAction = false
|
|
end
|
|
end
|
|
|
|
-- Flipping around
|
|
if not isCrawling then
|
|
if IsControlPressed(0, 22) then -- INPUT_JUMP
|
|
CrawlFlip(playerPed)
|
|
end
|
|
end
|
|
|
|
Wait(0)
|
|
end
|
|
|
|
-- If the crawling wasn't forcefully ended, then play the get up animations
|
|
PlayExitCrawlAnims(forceEnd)
|
|
|
|
-- Reset variabels
|
|
isCrawling = false
|
|
inAction = false
|
|
proneType = "onfront"
|
|
SetPedConfigFlag(PlayerPedId(), 48, false) -- CPED_CONFIG_FLAG_BlockWeaponSwitching
|
|
|
|
-- Unload animation dictionaries
|
|
RemoveAnimDict("move_crawl")
|
|
RemoveAnimDict("move_crawlprone2crawlfront")
|
|
end)
|
|
end
|
|
|
|
local function CrawlKeyPressed()
|
|
-- If we already are doing something, then don't continue
|
|
if inAction then
|
|
return
|
|
end
|
|
|
|
-- Don't start/stop to crawl if we are in the pause menu
|
|
if IsPauseMenuActive() then
|
|
return
|
|
end
|
|
|
|
-- If already prone, then stop
|
|
if IsProne then
|
|
IsProne = false
|
|
return
|
|
end
|
|
|
|
-- If we are playing an animation, then cancel it first
|
|
if IsInAnimation then
|
|
EmoteCancel()
|
|
end
|
|
|
|
-- If we are crouching we should stop that first
|
|
local wasCrouched = false
|
|
if isCrouched then
|
|
isCrouched = false
|
|
wasCrouched = true
|
|
end
|
|
|
|
local playerPed = PlayerPedId()
|
|
if not CanPlayerCrouchCrawl(playerPed) or IsEntityInWater(playerPed) then
|
|
return
|
|
end
|
|
inAction = true
|
|
|
|
-- If we are pointing then stop pointing
|
|
if Pointing then
|
|
Pointing = false
|
|
end
|
|
|
|
IsProne = true
|
|
SetPedConfigFlag(playerPed, 48, true) -- CPED_CONFIG_FLAG_BlockWeaponSwitching
|
|
|
|
-- Force leave stealth mode
|
|
if GetPedStealthMovement(playerPed) == 1 then
|
|
SetPedStealthMovement(playerPed, false, "DEFAULT_ACTION")
|
|
Wait(100)
|
|
end
|
|
|
|
-- Load animations that the crawling is going to use
|
|
LoadAnimDict("move_crawl")
|
|
LoadAnimDict("move_crawlprone2crawlfront")
|
|
|
|
if ShouldPlayerDiveToCrawl(playerPed) then
|
|
PlayAnimOnce(playerPed, "explosions", "react_blown_forwards", nil, 3.0)
|
|
Wait(1100)
|
|
elseif wasCrouched then
|
|
PlayAnimOnce(playerPed, "amb@world_human_sunbathe@male@front@enter", "enter", nil, nil, -1, 0.3)
|
|
Wait(1500)
|
|
else
|
|
PlayAnimOnce(playerPed, "amb@world_human_sunbathe@male@front@enter", "enter")
|
|
Wait(3000)
|
|
end
|
|
|
|
-- Set the player into the idle position (but only if we can still crawl)
|
|
if CanPlayerCrouchCrawl(playerPed) and not IsEntityInWater(playerPed) then
|
|
PlayIdleCrawlAnim(playerPed, nil, 3.0)
|
|
end
|
|
inAction = false
|
|
|
|
CrawlThread()
|
|
end
|
|
|
|
|
|
-- Commands & KeyMapping --
|
|
if Config.CrouchEnabled then
|
|
if Config.CrouchKeybindEnabled then
|
|
RegisterKeyMapping('+crouch', "Crouch", "keyboard", Config.CrouchKeybind)
|
|
RegisterCommand('+crouch', function() CrouchKeyPressed() end, false)
|
|
RegisterCommand('-crouch', function() end, false) -- This needs to be here to prevent errors/warnings
|
|
end
|
|
RegisterCommand('crouch', function()
|
|
if isCrouched then
|
|
isCrouched = false
|
|
return
|
|
end
|
|
|
|
AttemptCrouch(PlayerPedId())
|
|
end, false)
|
|
TriggerEvent('chat:addSuggestion', '/crouch', 'Crouch')
|
|
end
|
|
|
|
if Config.CrawlEnabled then
|
|
if Config.CrawlKeybindEnabled then
|
|
RegisterKeyMapping('crawl', "Crawl", "keyboard", Config.CrawlKeybind)
|
|
end
|
|
RegisterCommand('crawl', function() CrawlKeyPressed() end, false)
|
|
end
|
|
|
|
|
|
-- Exports --
|
|
-- Returns weather or not the player is crouched
|
|
local function IsPlayerCrouched()
|
|
return isCrouched
|
|
end
|
|
|
|
-- Returns if the player is prone (both when laying still and when moving)
|
|
local function IsPlayerProne()
|
|
return IsProne
|
|
end
|
|
|
|
-- Returns if the player is crawling (only when moving forward/backwards)
|
|
local function IsPlayerCrawling()
|
|
return isCrawling
|
|
end
|
|
|
|
exports('IsPlayerCrouched', IsPlayerCrouched)
|
|
exports('IsPlayerProne', IsPlayerProne)
|
|
exports('IsPlayerCrawling', IsPlayerCrawling)
|