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") 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]) 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 CreateExport('IsPlayerCrouched', IsPlayerCrouched) ---Returns if the player is prone (both when laying still and when moving) ---@return boolean local function IsPlayerProne() return IsProne end CreateExport('IsPlayerProne', IsPlayerProne) ---Returns if the player is crawling (only when moving forward/backward) ---@return boolean local function IsPlayerCrawling() return isCrawling end CreateExport('IsPlayerCrawling', IsPlayerCrawling) ---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 CreateExport('GetPlayerProneType', GetPlayerProneType) -- Useful to call if the player gets handcuffed etc. CreateExport('StopPlayerProne', stopPlayerProne)