2025-06-07 08:51:21 +02:00
IsProne = false
local isCrouched = false
local isCrawling = false
local inAction = false
local proneType = ' onfront '
local lastKeyPress = 0
local forceEndProne = false
-- Crouching --
local function ResetCrouch ( )
local playerPed = PlayerPedId ( )
ResetPedStrafeClipset ( playerPed )
ResetPedWeaponMovementClipset ( playerPed )
SetPedMaxMoveBlendRatio ( playerPed , 1.0 )
SetPedCanPlayAmbientAnims ( playerPed , true )
local walkstyle = GetResourceKvpString ( " walkstyle " )
2025-06-12 03:36:12 +02:00
if walkstyle then
local toApply = RP [ walkstyle ]
if not toApply or type ( toApply ) ~= " table " or toApply.category ~= " Walks " then
ResetPedMovementClipset ( playerPed , 0.5 )
DeleteResourceKvp ( " walkstyle " )
DebugPrint ( ' Invalid walkstyle found in KVP, resetting to default. ' )
return
end
RequestWalking ( toApply [ 1 ] )
SetPedMovementClipset ( PlayerPedId ( ) , toApply [ 1 ] , 0.5 )
RemoveClipSet ( toApply [ 1 ] )
2025-06-07 08:51:21 +02:00
else
ResetPedMovementClipset ( playerPed , 0.5 )
end
RemoveAnimSet ( ' move_ped_crouched ' )
end
local function CrouchLoop ( )
local playerId = PlayerId ( )
while isCrouched do
local playerPed = PlayerPedId ( )
if not CanPlayerCrouchCrawl ( playerPed ) then
isCrouched = false
break
end
if IsPlayerAiming ( playerId ) then
SetPedMaxMoveBlendRatio ( playerPed , 0.15 )
end
SetPedCanPlayAmbientAnims ( playerPed , false )
DisableControlAction ( 0 , 36 , true )
if IsPedUsingActionMode ( playerPed ) == 1 then
SetPedUsingActionMode ( playerPed , false , - 1 , ' DEFAULT_ACTION ' )
end
DisableFirstPersonCamThisFrame ( )
if Config.FpsMode == true then
DisableControlAction ( 2 , 25 , true ) -- disables the aim control action entirely while crouched
end
Wait ( 0 )
end
TriggerEvent ( ' crouch_crawl:onCrouch ' , false )
ResetCrouch ( )
end
local function StartCrouch ( )
isCrouched = true
RequestWalking ( ' move_ped_crouched ' )
local playerPed = PlayerPedId ( )
if GetPedStealthMovement ( playerPed ) ~= 0 then
SetPedStealthMovement ( playerPed , false , ' DEFAULT_ACTION ' )
Wait ( 100 )
end
if GetFollowPedCamViewMode ( ) == 4 then
SetFollowPedCamViewMode ( 0 ) -- THIRD_PERSON_NEAR
end
SetPedMovementClipset ( playerPed , ' move_ped_crouched ' , 0.6 )
SetPedStrafeClipset ( playerPed , ' move_ped_crouched_strafing ' )
-- For other scripts to use
TriggerEvent ( ' crouch_crawl:onCrouch ' , true )
CreateThread ( CrouchLoop )
end
---@param playerPed number
---@return boolean success
local function AttemptCrouch ( playerPed )
if CanPlayerCrouchCrawl ( playerPed ) and IsPedHuman ( playerPed ) then
StartCrouch ( )
return true
else
return false
end
end
---Disables a control until it's key has been released
---@param padIndex integer
---@param control integer
local function DisableControlUntilReleased ( padIndex , control )
CreateThread ( function ( )
while IsDisabledControlPressed ( padIndex , control ) do
DisableControlAction ( padIndex , control , true )
Wait ( 0 )
end
end )
end
local function CrouchKeyPressed ( )
if not LocalPlayer.state . canEmote then return end
if inAction then
return
end
if IsPauseMenuActive ( ) or IsNuiFocused ( ) then
return
end
if isCrouched then
isCrouched = false
local crouchKey = GetControlInstructionalButton ( 0 , ` + crouch ` | 0x80000000 , false )
local lookBehindKey = GetControlInstructionalButton ( 0 , 26 , false ) -- INPUT_LOOK_BEHIND
if crouchKey == lookBehindKey then
DisableControlUntilReleased ( 0 , 26 ) -- INPUT_LOOK_BEHIND
end
return
end
local playerPed = PlayerPedId ( )
if not CanPlayerCrouchCrawl ( playerPed ) or not IsPedHuman ( playerPed ) then
return
end
local crouchKey = GetControlInstructionalButton ( 0 , ` + crouch ` | 0x80000000 , false )
local lookBehindKey = GetControlInstructionalButton ( 0 , 26 , false ) -- INPUT_LOOK_BEHIND
local duckKey = GetControlInstructionalButton ( 0 , 36 , false ) -- INPUT_DUCK
if crouchKey == lookBehindKey then
DisableControlUntilReleased ( 0 , 26 ) -- INPUT_LOOK_BEHIND
end
if crouchKey == duckKey then
if Config.CrouchOverrideStealthMode then
DisableControlAction ( 0 , 36 , true ) -- Disable INPUT_DUCK this frame
elseif not IsProne then
local timer = GetGameTimer ( )
if GetPedStealthMovement ( playerPed ) ~= 0 and timer - lastKeyPress < 1000 then
DisableControlAction ( 0 , 36 , true ) -- Disable INPUT_DUCK this frame
lastKeyPress = 0
else
lastKeyPress = timer
return
end
end
end
StartCrouch ( )
if 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 --
---@param playerPed number
---@return boolean
local function ShouldPlayerDiveToCrawl ( playerPed )
if IsPedRunning ( playerPed ) or IsPedSprinting ( playerPed ) then
return true
end
return false
end
---Stops the player from being prone
---@param force boolean If forced then no exit anim is played
local function stopPlayerProne ( force )
IsProne = false
forceEndProne = force
end
---@param playerPed number
---@param heading number|nil
---@param blendInSpeed number|nil
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
---@param forceEnd boolean
local function PlayExitCrawlAnims ( forceEnd )
if not forceEnd then
inAction = true
local playerPed = PlayerPedId ( )
local animDict , animName , waitTime
if proneType == ' onfront ' then
animDict , animName , waitTime = ' get_up@directional@transition@prone_to_knees@crawl ' , ' front ' , 780
else
animDict , animName , waitTime = ' get_up@directional@transition@prone_to_seated@crawl ' , ' back ' , 950
end
PlayAnimOnce ( playerPed , animDict , animName , nil , nil , waitTime )
if not isCrouched then
Wait ( waitTime )
PlayAnimOnce ( playerPed , ' get_up@directional@movement@from_ ' .. ( proneType == ' onfront ' and ' knees ' or ' seated ' ) .. ' @standard ' , ' getup_l_0 ' , nil , nil , 1300 )
end
end
end
-- Crawls one "step" forward/backward
---@param playerPed number
---@param type string
---@param direction string
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
-- Flips the player when crawling
---@param playerPed number
local function CrawlFlip ( playerPed )
inAction = true
local heading = GetEntityHeading ( playerPed )
proneType = proneType == ' onfront ' and ' onback ' or ' onfront '
if proneType == ' onback ' then
PlayAnimOnce ( playerPed , ' get_up@directional_sweep@combat@pistol@front ' , ' front_to_prone ' , 2.0 )
ChangeHeadingSmooth ( playerPed , - 18.0 , 3600 )
else
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 CrawlLoop ( )
Wait ( 400 )
while IsProne do
local playerPed = PlayerPedId ( )
if not CanPlayerCrouchCrawl ( playerPed ) or IsEntityInWater ( playerPed ) then
ClearPedTasks ( playerPed )
stopPlayerProne ( true )
break
end
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
if not isCrawling then
if IsControlPressed ( 0 , 22 ) then -- INPUT_JUMP
CrawlFlip ( playerPed )
end
end
Wait ( 0 )
end
TriggerEvent ( ' crouch_crawl:onCrawl ' , false )
PlayExitCrawlAnims ( forceEndProne )
isCrawling = false
inAction = false
forceEndProne = false
proneType = ' onfront '
SetPedConfigFlag ( PlayerPedId ( ) , 48 , false ) -- CPED_CONFIG_FLAG_BlockWeaponSwitching
RemoveAnimDict ( ' move_crawl ' )
RemoveAnimDict ( ' move_crawlprone2crawlfront ' )
end
local function CrawlKeyPressed ( )
if not LocalPlayer.state . canEmote then return end
if inAction then
return
end
if IsPauseMenuActive ( ) or IsNuiFocused ( ) then
return
end
if IsProne then
IsProne = false
return
end
if IsInAnimation then
EmoteCancel ( )
end
local wasCrouched = false
if isCrouched then
isCrouched = false
wasCrouched = true
end
local playerPed = PlayerPedId ( )
if not CanPlayerCrouchCrawl ( playerPed ) or IsEntityInWater ( playerPed ) or not IsPedHuman ( playerPed ) then
return
end
inAction = true
if Pointing then
Pointing = false
end
if InHandsup then
return
end
if IsInActionWithErrorMessage ( { [ ' IsProne ' ] = true } ) then
return
end
IsProne = true
SetPedConfigFlag ( playerPed , 48 , true ) -- CPED_CONFIG_FLAG_BlockWeaponSwitching
if GetPedStealthMovement ( playerPed ) ~= 0 then
SetPedStealthMovement ( playerPed , false , ' DEFAULT_ACTION ' )
Wait ( 100 )
end
LoadAnim ( ' move_crawl ' )
LoadAnim ( ' 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
if CanPlayerCrouchCrawl ( playerPed ) and not IsEntityInWater ( playerPed ) then
PlayIdleCrawlAnim ( playerPed , nil , 3.0 )
end
TriggerEvent ( ' crouch_crawl:onCrawl ' , true )
inAction = false
CreateThread ( CrawlLoop )
end
-- Commands & KeyMapping --
if Config.CrouchEnabled then
if Config.CrouchKeybindEnabled then
RegisterKeyMapping ( ' +crouch ' , Translate ( ' register_crouch ' ) , ' keyboard ' , Config.CrouchKeybind )
RegisterCommand ( ' +crouch ' , function ( ) CrouchKeyPressed ( ) end , false )
RegisterCommand ( ' -crouch ' , function ( ) end , false ) -- This needs to be here to prevent warnings in chat
end
RegisterCommand ( ' crouch ' , function ( )
if not LocalPlayer.state . canEmote then return end
if isCrouched then
isCrouched = false
return
end
AttemptCrouch ( PlayerPedId ( ) )
end , false )
TriggerEvent ( ' chat:addSuggestion ' , ' /crouch ' , Translate ( ' crouch ' ) )
end
if Config.CrawlEnabled then
if Config.CrawlKeybindEnabled then
RegisterKeyMapping ( ' +crawl ' , Translate ( ' register_crawl ' ) , ' keyboard ' , Config.CrawlKeybind )
RegisterCommand ( ' +crawl ' , function ( ) CrawlKeyPressed ( ) end , false )
RegisterCommand ( ' -crawl ' , function ( ) end , false ) -- This needs to be here to prevent warnings in chat
end
RegisterCommand ( ' crawl ' , function ( ) CrawlKeyPressed ( ) end , false )
TriggerEvent ( ' chat:addSuggestion ' , ' /crawl ' , Translate ( ' crawl ' ) )
end
---Returns if the player is crouched
---@return boolean
local function IsPlayerCrouched ( )
return isCrouched
end
2025-06-12 03:36:12 +02:00
CreateExport ( ' IsPlayerCrouched ' , IsPlayerCrouched )
2025-06-07 08:51:21 +02:00
---Returns if the player is prone (both when laying still and when moving)
---@return boolean
local function IsPlayerProne ( )
return IsProne
end
2025-06-12 03:36:12 +02:00
CreateExport ( ' IsPlayerProne ' , IsPlayerProne )
2025-06-07 08:51:21 +02:00
---Returns if the player is crawling (only when moving forward/backward)
---@return boolean
local function IsPlayerCrawling ( )
return isCrawling
end
2025-06-12 03:36:12 +02:00
CreateExport ( ' IsPlayerCrawling ' , IsPlayerCrawling )
2025-06-07 08:51:21 +02:00
---Returns either "onfront" or "onback", this can be used to check if the player is on his back or on his stomach. NOTE: This will still return a string even if the player is not prone. Use IsPlayerProne() to check if the player is prone.
---@return string
local function GetPlayerProneType ( )
return proneType
end
2025-06-12 03:36:12 +02:00
CreateExport ( ' GetPlayerProneType ' , GetPlayerProneType )
2025-06-07 08:51:21 +02:00
-- Useful to call if the player gets handcuffed etc.
2025-06-12 03:36:12 +02:00
CreateExport ( ' StopPlayerProne ' , stopPlayerProne )