local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local RunService = game:GetService("RunService") local ContentProvider = game:GetService("ContentProvider") local TweenService = game:GetService("TweenService") local player = Players.LocalPlayer if player:GetAttribute("CrouchSlideLoaded") then return end player:SetAttribute("CrouchSlideLoaded", true) local Slide_At = 20 local Crouch_Hip, Slide_Hip, Crouch_Speed = 1.85, 1.3, 0.55 local hitbox_Y, hitbox_Size = 0.5, Vector3.new(2, 1, 1) local crouch_top = hitbox_Y + hitbox_Size.Y * 0.5 local scan_radius = 8 local FLAT_DECEL, UPHILL_DECEL, DOWNHILL_ACCEL = 12, 52, 46 local CROUCH_IMG = "rbxassetid://109660173469064" local CROUCH_DOWN = "rbxassetid://105987157375274" local SLIDE_IMG = "rbxassetid://110954069432063" local SLIDE_DOWN = "rbxassetid://134994664343117" local ANIMS = { idle = "rbxassetid://109602961761132", walk = "rbxassetid://134301743319119", run = "rbxassetid://77522194791234", } local SLIDE_ANIM = "rbxassetid://125545085077300" local SLIDE_SOUND = "rbxassetid://18952286141" local State = { Stand = "Stand", Crouch = "Crouch", Slide = "Slide" } local character, humanoid, hrp, animator local originalHip, savedSpeed = 2, 16 local collider, colliderWeld local origCollide, origQuery = {}, {} local state, crouchOn, slowedSpeed = State.Stand, false, nil local hipLock = false local tracks, crouchName, slideTrack, slideSound = {}, nil, nil, nil local slideDir, slideVel, slideTime, fading = Vector3.zAxis, 0, 0, false local velocity, attachment, lastToggle = nil, nil, 0 local button, gui local disabledParts, origPartCollide = {}, {} local onToggle local function flatSpeed() if not hrp then return 0 end local v = hrp.AssemblyLinearVelocity return Vector3.new(v.X, 0, v.Z).Magnitude end local function readSpeed() if not humanoid or not hrp then return 0 end local vel = flatSpeed() return humanoid.MoveDirection.Magnitude > 0.05 and math.max(vel, humanoid.WalkSpeed) or vel end local function grounded() if not humanoid then return false end local s = humanoid:GetState() return s ~= Enum.HumanoidStateType.Freefall and s ~= Enum.HumanoidStateType.Jumping and humanoid.FloorMaterial ~= Enum.Material.Air end local function usable() if not humanoid or not hrp or humanoid.Health <= 0 then return false end local s = humanoid:GetState() return s ~= Enum.HumanoidStateType.Dead and s ~= Enum.HumanoidStateType.Seated and s ~= Enum.HumanoidStateType.Swimming and s ~= Enum.HumanoidStateType.Climbing end local function rayParams() local p = RaycastParams.new() p.FilterType = Enum.RaycastFilterType.Exclude p.FilterDescendantsInstances = { character } p.RespectCanCollide = true return p end local function setAttributes() if character then character:SetAttribute("Crouching", state == State.Crouch) character:SetAttribute("Sliding", state == State.Slide) end end local function syncCollider() if colliderWeld and humanoid then colliderWeld.C0 = CFrame.new(0, hitbox_Y - humanoid.HipHeight, 0) end end local function setPose(hip) if not humanoid or not humanoid.Parent then return end local target = hip or originalHip if math.abs(humanoid.HipHeight - target) > 0.001 then hipLock = true humanoid.HipHeight = target hipLock = false end syncCollider() end local function restoreOverhang() for p in pairs(disabledParts) do if p.Parent then p.CanCollide = origPartCollide[p] ~= false end end disabledParts, origPartCollide = {}, {} end local function disableOverhang() if not hrp or not humanoid then return end local lowerBound = hrp.Position.Y - humanoid.HipHeight + crouch_top + 0.03 local upperBound = hrp.Position.Y + 3 for p in pairs(disabledParts) do if not p.Parent or (p.Position - hrp.Position).Magnitude > scan_radius * 3 then if p.Parent then p.CanCollide = origPartCollide[p] ~= false end disabledParts[p], origPartCollide[p] = nil, nil end end if upperBound <= lowerBound then return end local params = OverlapParams.new() params.FilterType = Enum.RaycastFilterType.Exclude params.FilterDescendantsInstances = { character, workspace.CurrentCamera } params.MaxParts = 200 local ok, parts = pcall(function() return workspace:GetPartBoundsInBox( CFrame.new(hrp.Position.X, (lowerBound + upperBound) * 0.5, hrp.Position.Z), Vector3.new(scan_radius * 2, upperBound - lowerBound, scan_radius * 2), params ) end) if not ok or not parts then return end for _, p in ipairs(parts) do if p.Parent and not disabledParts[p] and p.CanCollide and not p:IsDescendantOf(character) then local cf, sz = p.CFrame, p.Size local lowestY = cf.Position.Y - sz.Y * 0.5 * math.abs(cf.UpVector.Y) - sz.Z * 0.5 * math.abs(cf.LookVector.Y) - sz.X * 0.5 * math.abs(cf.RightVector.Y) if lowestY >= lowerBound and lowestY <= upperBound then disabledParts[p], origPartCollide[p], p.CanCollide = true, p.CanCollide, false end end end end local function disableBody() for part in pairs(origCollide) do if part.Parent then part.CanCollide, part.CanQuery = false, false end end if collider then collider.CanCollide, collider.CanQuery = true, true end end local function enableBody() for part, orig in pairs(origCollide) do if part.Parent then part.CanCollide = orig part.CanQuery = origQuery[part] ~= false end end if collider then collider.CanCollide, collider.CanQuery = false, false end end local function stopCrouch(fade) for _, track in pairs(tracks) do if track.IsPlaying then track:Stop(fade or 0.15) end end crouchName = nil end local function playCrouch(name) local track = tracks[name] if not track then return end if crouchName ~= name then for n, other in pairs(tracks) do if n ~= name and other.IsPlaying then other:Stop(0.15) end end track:Play(0.15, 1, 1) crouchName = name end local speed = flatSpeed() track:AdjustSpeed(name == "walk" and math.clamp(speed / 8, 0.65, 1.5) or name == "run" and math.clamp(speed / 18, 0.75, 1.45) or 1) end local function updateCrouchAnim() if state ~= State.Crouch then return end local speed = flatSpeed() playCrouch(speed < 0.75 and "idle" or speed < 16 and "walk" or "run") end local function loadTracks() tracks, crouchName, slideTrack = {}, nil, nil if not animator then return end for name, id in pairs(ANIMS) do local anim = Instance.new("Animation") anim.AnimationId = id local ok, track = pcall(function() return animator:LoadAnimation(anim) end) if ok and track then track.Priority, track.Looped, tracks[name] = Enum.AnimationPriority.Movement, true, track end end local anim = Instance.new("Animation") anim.AnimationId = SLIDE_ANIM local ok, track = pcall(function() return animator:LoadAnimation(anim) end) if ok and track then track.Priority, track.Looped, slideTrack = Enum.AnimationPriority.Action4, true, track end end local function freezeSlide() if not slideTrack or not slideTrack.IsPlaying or slideTrack.Length <= 0.05 then return end local at = math.min(0.6, math.max(slideTrack.Length - 0.04, 0)) if slideTrack.TimePosition >= at then slideTrack.TimePosition = at if slideTrack.Speed ~= 0 then slideTrack:AdjustSpeed(0) end end end local function startSound() if not hrp then return end if slideSound then slideSound:Destroy() end slideSound = Instance.new("Sound") slideSound.Name, slideSound.SoundId, slideSound.Looped, slideSound.Volume = "SlideLoop", SLIDE_SOUND, true, 0.6 slideSound.RollOffMode, slideSound.RollOffMaxDistance = Enum.RollOffMode.Linear, 70 slideSound.Parent = hrp slideSound:Play() end local function stopSound(fade) local s = slideSound slideSound = nil if not s then return end if fade and s.Parent then local tw = TweenService:Create(s, TweenInfo.new(0.22), { Volume = 0 }) tw:Play() tw.Completed:Connect(function() s:Stop(); s:Destroy() end) else s:Stop(); s:Destroy() end end local function clearMover() if velocity then velocity:Destroy(); velocity = nil end if attachment then attachment:Destroy(); attachment = nil end end local function findJump() local pg = player:FindFirstChild("PlayerGui") local touch = pg and pg:FindFirstChild("TouchGui") local frame = touch and touch:FindFirstChild("TouchControlFrame") return frame and frame:FindFirstChild("JumpButton") end local function refreshButton() if not button then return end local pressed = state == State.Crouch or (state == State.Slide and crouchOn) local sliding = state == State.Slide or (state ~= State.Crouch and readSpeed() >= Slide_At) button.Image = sliding and (pressed and SLIDE_DOWN or SLIDE_IMG) or (pressed and CROUCH_DOWN or CROUCH_IMG) end local function layoutButton() if not button or not gui then return end local jump = findJump() button.Visible = UserInputService.TouchEnabled and jump ~= nil if not button.Visible then return end local pos, size = jump.AbsolutePosition, jump.AbsoluteSize if size.X < 2 then return end button.Size = UDim2.fromOffset(size.X, size.Y) button.Position = UDim2.fromOffset(pos.X - gui.AbsolutePosition.X - math.max(8, size.X * 0.18), pos.Y - gui.AbsolutePosition.Y + size.Y * 0.5) end local function ensureGui() if gui and gui.Parent then return end gui = Instance.new("ScreenGui") gui.Name, gui.ResetOnSpawn, gui.IgnoreGuiInset, gui.DisplayOrder = "CrouchSlideGui", false, true, 50 gui.Parent = player:WaitForChild("PlayerGui") button = Instance.new("ImageButton") button.Name = "CrouchButton" button.AnchorPoint = Vector2.new(1, 0.5) button.BackgroundTransparency, button.AutoButtonColor = 1, false button.ScaleType, button.Image = Enum.ScaleType.Fit, CROUCH_IMG button.Size, button.Visible, button.ZIndex = UDim2.fromOffset(70, 70), false, 10 button.Parent = gui button.Activated:Connect(function() onToggle() end) end local function facing() if not humanoid or not hrp then return Vector3.zAxis end local flat = Vector3.new(humanoid.MoveDirection.X, 0, humanoid.MoveDirection.Z) if flat.Magnitude > 0.1 then return flat.Unit end local vel = hrp.AssemblyLinearVelocity flat = Vector3.new(vel.X, 0, vel.Z) if flat.Magnitude > 2 then return flat.Unit end flat = Vector3.new(hrp.CFrame.LookVector.X, 0, hrp.CFrame.LookVector.Z) return flat.Magnitude > 0.05 and flat.Unit or Vector3.zAxis end local function slope() if not hrp then return 0 end local hit = workspace:Raycast(hrp.Position + Vector3.new(0, 2.5, 0), Vector3.new(0, -12, 0), rayParams()) return hit and math.clamp(hit.Normal:Dot(slideDir), -1, 1) or 0 end local function steerSlide(dt) local wish = humanoid and humanoid.MoveDirection or Vector3.zero local flat = Vector3.new(wish.X, 0, wish.Z) if flat.Magnitude < 0.12 then local keys = (UserInputService:IsKeyDown(Enum.KeyCode.A) or UserInputService:IsKeyDown(Enum.KeyCode.Left)) and -1 or 0 if UserInputService:IsKeyDown(Enum.KeyCode.D) or UserInputService:IsKeyDown(Enum.KeyCode.Right) then keys += 1 end if keys == 0 then return end local right = workspace.CurrentCamera and workspace.CurrentCamera.CFrame.RightVector or Vector3.xAxis flat = Vector3.new(right.X, 0, right.Z) if flat.Magnitude < 0.01 then return end flat = flat.Unit * keys end if flat.Magnitude < 0.01 then return end local blended = slideDir:Lerp(flat.Unit, 1 - math.exp(-5.5 * math.clamp(flat.Magnitude, 0, 1) * dt)) if blended.Magnitude > 0.01 then slideDir = blended.Unit end end local function hitWall() if not hrp then return false end local dist = math.clamp(1.6 + slideVel * 0.05, 1.8, 4.5) for _, y in ipairs({ 0.2, 1.3, 2.2 }) do local hit = workspace:Raycast(hrp.Position + Vector3.new(0, y, 0), slideDir * dist, rayParams()) if hit and hit.Instance and hit.Instance.CanCollide and hit.Normal.Y < 0.45 then return true end end return false end local function beginFade() if fading then return end fading = true if crouchOn then setPose(Crouch_Hip) if slideTrack and slideTrack.IsPlaying then slideTrack:Stop(0.22) end playCrouch(flatSpeed() >= 16 and "run" or flatSpeed() >= 0.75 and "walk" or "idle") else setPose(nil) if slideTrack and slideTrack.IsPlaying then slideTrack:Stop(0.22) end stopCrouch(0.18) end stopSound(true) end local function finishSlide(wall) if state ~= State.Slide then return end local keep = crouchOn and humanoid and humanoid.Health > 0 if not fading then beginFade() end clearMover() if humanoid and humanoid.Parent then humanoid.AutoRotate = true humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) if wall and hrp then local v = hrp.AssemblyLinearVelocity hrp.AssemblyLinearVelocity = Vector3.new(0, v.Y, 0) end if keep then state = State.Crouch setPose(Crouch_Hip) humanoid.WalkSpeed = math.max(6, savedSpeed * Crouch_Speed) slowedSpeed = humanoid.WalkSpeed disableBody() else state, crouchOn = State.Stand, false humanoid.WalkSpeed, slowedSpeed = savedSpeed, nil setPose(nil) stopCrouch(0.16) enableBody() restoreOverhang() end else state, crouchOn = State.Stand, false enableBody() restoreOverhang() end fading = false setAttributes() refreshButton() end local function startSlide(speed) savedSpeed, slideDir = humanoid.WalkSpeed, facing() slideVel = math.clamp(math.max(speed, flatSpeed(), Slide_At) * 1.5, 24, 100) slideTime, fading, state, crouchOn = 0, false, State.Slide, true stopCrouch(0.06) setPose(Slide_Hip) disableBody() humanoid.AutoRotate = false humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false) humanoid.WalkSpeed = 0 local y = hrp.AssemblyLinearVelocity.Y hrp.AssemblyLinearVelocity = Vector3.new(slideDir.X * slideVel, y, slideDir.Z * slideVel) hrp.CFrame = CFrame.lookAt(hrp.Position, hrp.Position + slideDir) clearMover() attachment = Instance.new("Attachment") attachment.Parent = hrp velocity = Instance.new("LinearVelocity") velocity.Attachment0 = attachment velocity.RelativeTo = Enum.ActuatorRelativeTo.World velocity.ForceLimitMode = Enum.ForceLimitMode.PerAxis velocity.MaxAxesForce = Vector3.new(1e5, 0, 1e5) velocity.VectorVelocity = Vector3.new(slideDir.X * slideVel, 0, slideDir.Z * slideVel) velocity.Parent = hrp if slideTrack then slideTrack:Play(0.08, 1, 1); slideTrack:AdjustSpeed(1) end startSound() setAttributes() refreshButton() end local function enterCrouch() if state == State.Stand then savedSpeed = humanoid.WalkSpeed end state, crouchOn = State.Crouch, true setPose(Crouch_Hip) disableBody() humanoid.WalkSpeed = math.max(6, savedSpeed * Crouch_Speed) slowedSpeed = humanoid.WalkSpeed setAttributes() refreshButton() updateCrouchAnim() end local function exitCrouch() state, crouchOn = State.Stand, false stopCrouch(0.15) setPose(nil) enableBody() restoreOverhang() if humanoid and humanoid.Parent then if slowedSpeed and math.abs(humanoid.WalkSpeed - slowedSpeed) < 0.4 then humanoid.WalkSpeed = savedSpeed end humanoid.AutoRotate = true humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) end slowedSpeed = nil setAttributes() refreshButton() end onToggle = function() local now = os.clock() if now - lastToggle < 0.1 then return end lastToggle = now if not usable() then return end if state == State.Slide then crouchOn = false finishSlide(true) elseif state == State.Crouch then exitCrouch() elseif grounded() then local speed = readSpeed() if speed >= Slide_At then startSlide(speed) else enterCrouch() end end end local function updateSlide(dt) if state ~= State.Slide or not humanoid or not hrp or humanoid.Health <= 0 then if state == State.Slide then crouchOn = false; finishSlide(false) end return end if slideTime > 0.18 and flatSpeed() < 4 and slideVel > 12 then finishSlide(true); return end slideTime += dt steerSlide(dt) local grade = slope() slideVel = math.clamp(slideVel + ((grade > 0.03 and grade * DOWNHILL_ACCEL or 0) - (FLAT_DECEL + (grade < -0.03 and -grade * UPHILL_DECEL or 0))) * dt, 0, 110) humanoid.WalkSpeed, humanoid.AutoRotate = 0, false if velocity then velocity.VectorVelocity = Vector3.new(slideDir.X * slideVel, 0, slideDir.Z * slideVel) end local y = hrp.AssemblyLinearVelocity.Y hrp.CFrame = CFrame.lookAt(hrp.Position, hrp.Position + slideDir) hrp.AssemblyLinearVelocity = Vector3.new(slideDir.X * slideVel, y, slideDir.Z * slideVel) if hitWall() then finishSlide(true); return end if not fading and slideVel < 11 then beginFade() end if (slideTime > 0.35 and slideVel <= 5) or slideTime >= 7 then finishSlide(false) end end local function destroyCollider() if collider then collider:Destroy(); collider = nil end colliderWeld, origCollide, origQuery = nil, {}, {} end local function createCollider(char, root) collider = Instance.new("Part") collider.Name, collider.Size, collider.Transparency = "LowHitbox", hitbox_Size, 1 collider.CanCollide, collider.CanQuery, collider.CanTouch, collider.Massless = false, false, false, true collider.Parent = char colliderWeld = Instance.new("Weld") colliderWeld.Part0, colliderWeld.Part1 = root, collider colliderWeld.C0 = CFrame.new(0, hitbox_Y - (humanoid and humanoid.HipHeight or originalHip), 0) colliderWeld.Parent = collider origCollide, origQuery = {}, {} for _, d in ipairs(char:GetDescendants()) do if d:IsA("BasePart") and d ~= root and d ~= collider then origCollide[d], origQuery[d] = d.CanCollide, d.CanQuery end end end local function teardown() clearMover() stopSound(false) stopCrouch(0) if slideTrack and slideTrack.IsPlaying then slideTrack:Stop(0) end enableBody() restoreOverhang() destroyCollider() if humanoid and humanoid.Parent then pcall(function() hipLock = true humanoid.HipHeight = originalHip hipLock = false humanoid.AutoRotate = true humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) if slowedSpeed and math.abs(humanoid.WalkSpeed - slowedSpeed) < 0.4 then humanoid.WalkSpeed = savedSpeed end end) end state, crouchOn, fading, slowedSpeed, tracks, slideTrack = State.Stand, false, false, nil, {}, nil end local function setup(char) local hum = char:WaitForChild("Humanoid", 10) local root = char:WaitForChild("HumanoidRootPart", 10) if not hum or not root or player.Character ~= char then return end char:WaitForChild("Animate", 5) task.wait(0.15) if player.Character ~= char or not hum.Parent then return end teardown() character, humanoid, hrp = char, hum, root animator = hum:FindFirstChildOfClass("Animator") or hum:WaitForChild("Animator", 5) originalHip = hum.HipHeight > 0.05 and hum.HipHeight or 2 savedSpeed, state, crouchOn = hum.WalkSpeed, State.Stand, false loadTracks() createCollider(char, root) syncCollider() setAttributes() refreshButton() char.DescendantAdded:Connect(function(d) if d:IsA("BasePart") and d ~= collider and d ~= root and origCollide[d] == nil then origCollide[d], origQuery[d] = d.CanCollide, d.CanQuery if state ~= State.Stand then d.CanCollide, d.CanQuery = false, false end end end) hum:GetPropertyChangedSignal("HipHeight"):Connect(function() if not hipLock and humanoid then syncCollider() end end) hum.Died:Connect(function() if character ~= char then return end crouchOn = false if state == State.Slide then finishSlide(false) end teardown() end) hum.Jumping:Connect(function(active) if character == char and active and state == State.Crouch then exitCrouch() end end) hum.Seated:Connect(function(active) if character ~= char or not active or state == State.Stand then return end crouchOn = false if state == State.Slide then finishSlide(false) end if state == State.Crouch then exitCrouch() end end) end task.spawn(function() local assets = {} for _, id in pairs(ANIMS) do local a = Instance.new("Animation") a.AnimationId = id table.insert(assets, a) end local a, s = Instance.new("Animation"), Instance.new("Sound") a.AnimationId, s.SoundId = SLIDE_ANIM, SLIDE_SOUND table.insert(assets, a) table.insert(assets, s) pcall(function() ContentProvider:PreloadAsync(assets) end) end) ensureGui() UserInputService.InputBegan:Connect(function(input, processed) if processed or UserInputService:GetFocusedTextBox() then return end if input.KeyCode == Enum.KeyCode.C then onToggle() end end) pcall(function() RunService:UnbindFromRenderStep("CrouchSlidePose") end) RunService.PreSimulation:Connect(function(dt) syncCollider() if state == State.Crouch or state == State.Slide then disableBody() disableOverhang() end if state == State.Slide then updateSlide(dt) end end) RunService:BindToRenderStep("CrouchSlidePose", Enum.RenderPriority.Last.Value, function() layoutButton() refreshButton() if state == State.Slide then freezeSlide() elseif state == State.Crouch then updateCrouchAnim() end end) player.CharacterAdded:Connect(function(char) task.spawn(setup, char) end) if player.Character then task.spawn(setup, player.Character) end