arminhello

Grapple and Climb TRY 2

Jul 22nd, 2025
457
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.05 KB | Source Code | 0 0
  1. -- grab core services
  2. local Players = game:GetService("Players")
  3. local RunService = game:GetService("RunService")
  4. local UserInputService = game:GetService("UserInputService")
  5. local TweenService = game:GetService("TweenService")
  6. local StarterGui = game:GetService("StarterGui")
  7. local ContextActionService = game:GetService("ContextActionService")
  8.  
  9. -- get basic refs
  10. local player = Players.LocalPlayer
  11. local camera = workspace.CurrentCamera
  12.  
  13. -- wait till we got a char
  14. local character = player.Character or player.CharacterAdded:Wait()
  15. local humanoid = character:WaitForChild("Humanoid")
  16. local rootPart = character:WaitForChild("HumanoidRootPart")
  17.  
  18. -- simple state thing to track what we're doing
  19. local state = {}
  20. state.__index = state
  21. state.current = "Idle"
  22.  
  23. function state:set(newState)
  24.     self.current = newState
  25. end
  26.  
  27. function state:is(val)
  28.     return self.current == val
  29. end
  30.  
  31. -- basic config stuff
  32. local GRAPPLE_SPEED = 120
  33. local CLIMB_SPEED = 1
  34. local CLIMB_DURATION = 1.5
  35. local WALL_CHECK_DISTANCE = 5
  36. local COOLDOWN_TIME = 1.2
  37.  
  38. -- visuals and state flags
  39. local grapplePoint = nil
  40. local isGrappling = false
  41. local isClimbing = false
  42. local beam = nil
  43. local attachments = {}
  44.  
  45. -- ui setup for cooldown bar
  46. local ui = Instance.new("ScreenGui")
  47. ui.Name = "GrappleUI"
  48. ui.ResetOnSpawn = false
  49. ui.Parent = player:WaitForChild("PlayerGui")
  50.  
  51. local cooldownBar = Instance.new("Frame")
  52. cooldownBar.Size = UDim2.new(0, 100, 0, 6)
  53. cooldownBar.Position = UDim2.new(0.5, -50, 0.95, 0)
  54. cooldownBar.BackgroundColor3 = Color3.fromRGB(60, 180, 255)
  55. cooldownBar.BorderSizePixel = 0
  56. cooldownBar.Visible = false
  57. cooldownBar.Parent = ui
  58.  
  59. local cooldownFill = Instance.new("Frame")
  60. cooldownFill.Size = UDim2.new(1, 0, 1, 0)
  61. cooldownFill.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
  62. cooldownFill.BorderSizePixel = 0
  63. cooldownFill.Parent = cooldownBar
  64.  
  65. -- buttons for mobile users
  66. if UserInputService.TouchEnabled then
  67.     local grappleButton = Instance.new("TextButton")
  68.     grappleButton.Size = UDim2.new(0, 100, 0, 40)
  69.     grappleButton.Position = UDim2.new(1, -110, 1, -100)
  70.     grappleButton.Text = "Grapple"
  71.     grappleButton.BackgroundColor3 = Color3.fromRGB(70, 70, 255)
  72.     grappleButton.TextColor3 = Color3.new(1, 1, 1)
  73.     grappleButton.Font = Enum.Font.SourceSansBold
  74.     grappleButton.TextSize = 20
  75.     grappleButton.Parent = ui
  76.  
  77.     local climbButton = Instance.new("TextButton")
  78.     climbButton.Size = UDim2.new(0, 100, 0, 40)
  79.     climbButton.Position = UDim2.new(1, -110, 1, -150)
  80.     climbButton.Text = "Climb"
  81.     climbButton.BackgroundColor3 = Color3.fromRGB(70, 255, 70)
  82.     climbButton.TextColor3 = Color3.new(1, 1, 1)
  83.     climbButton.Font = Enum.Font.SourceSansBold
  84.     climbButton.TextSize = 20
  85.     climbButton.Parent = ui
  86.  
  87.     -- hook up buttons to actions
  88.     grappleButton.MouseButton1Click:Connect(function()
  89.         fireGrapple()
  90.     end)
  91.  
  92.     climbButton.MouseButton1Click:Connect(function()
  93.         startClimbing()
  94.     end)
  95. end
  96.  
  97. -- does the cooldown bar anim
  98. local function showCooldown(duration)
  99.     cooldownBar.Visible = true
  100.     cooldownFill.Size = UDim2.new(1, 0, 1, 0)
  101.  
  102.     local tween = TweenService:Create(cooldownFill, TweenInfo.new(duration), {
  103.         Size = UDim2.new(0, 0, 1, 0)
  104.     })
  105.     tween:Play()
  106.     tween.Completed:Wait()
  107.  
  108.     cooldownBar.Visible = false
  109. end
  110.  
  111. -- draws the beam from player to point
  112. local function setupGrappleVisual()
  113.     local a1 = Instance.new("Attachment")
  114.     a1.Name = "GrappleStart"
  115.     a1.Position = Vector3.zero
  116.     a1.Parent = rootPart
  117.  
  118.     local a2 = Instance.new("Attachment")
  119.     a2.Name = "GrappleEnd"
  120.     a2.Parent = workspace.Terrain
  121.  
  122.     local b = Instance.new("Beam")
  123.     b.Attachment0 = a1
  124.     b.Attachment1 = a2
  125.     b.Width0 = 0.1
  126.     b.Width1 = 0.1
  127.     b.Color = ColorSequence.new(Color3.fromRGB(200, 200, 255))
  128.     b.LightEmission = 1
  129.     b.FaceCamera = true
  130.     b.Parent = a1
  131.  
  132.     attachments.start = a1
  133.     attachments.end_ = a2
  134.     beam = b
  135. end
  136.  
  137. -- kills the visual
  138. local function removeGrappleVisual()
  139.     if beam then beam:Destroy() end
  140.     if attachments.start then attachments.start:Destroy() end
  141.     if attachments.end_ then attachments.end_:Destroy() end
  142.     attachments = {}
  143.     beam = nil
  144. end
  145.  
  146. -- checks if there's a wall in front
  147. local function isNearWall()
  148.     local rayOrigin = rootPart.Position
  149.     local rayDirection = rootPart.CFrame.LookVector * WALL_CHECK_DISTANCE
  150.  
  151.     local raycastParams = RaycastParams.new()
  152.     raycastParams.FilterDescendantsInstances = {character}
  153.     raycastParams.FilterType = Enum.RaycastFilterType.Exclude
  154.  
  155.     local result = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
  156.     if result and result.Instance and not result.Instance:IsDescendantOf(character) then
  157.         return true, result.Position
  158.     end
  159.     return false
  160. end
  161.  
  162. -- handles grapple move step
  163. local function moveTo(position)
  164.     local direction = (position - rootPart.Position)
  165.     local distance = direction.Magnitude
  166.     local duration = distance / GRAPPLE_SPEED
  167.     local t0 = tick()
  168.  
  169.     state:set("Grappling")
  170.     isGrappling = true
  171.  
  172.     RunService:BindToRenderStep("GrappleStep", Enum.RenderPriority.Character.Value + 1, function()
  173.         local dt = tick() - t0
  174.         if dt >= duration or not grapplePoint then
  175.             RunService:UnbindFromRenderStep("GrappleStep")
  176.             isGrappling = false
  177.             state:set("Cooldown")
  178.             task.delay(COOLDOWN_TIME, function()
  179.                 if not isClimbing then
  180.                     state:set("Idle")
  181.                 end
  182.             end)
  183.             showCooldown(COOLDOWN_TIME)
  184.             removeGrappleVisual()
  185.             return
  186.         end
  187.  
  188.         local step = rootPart.CFrame.Position:Lerp(grapplePoint, math.clamp(dt / duration, 0, 1))
  189.         local look = (grapplePoint - rootPart.Position).Unit
  190.         rootPart.CFrame = CFrame.new(step, step + look)
  191.  
  192.         if attachments.end_ then
  193.             attachments.end_.WorldPosition = grapplePoint
  194.         end
  195.     end)
  196. end
  197.  
  198. -- shoot ray from mouse, see what we hit
  199. function fireGrapple()
  200.     if isGrappling or isClimbing or state.current ~= "Idle" then return end
  201.  
  202.     local mousePos = UserInputService:GetMouseLocation()
  203.     local unitRay = camera:ViewportPointToRay(mousePos.X, mousePos.Y)
  204.     local rayOrigin = unitRay.Origin
  205.     local rayDirection = unitRay.Direction * 1000
  206.  
  207.     local raycastParams = RaycastParams.new()
  208.     raycastParams.FilterDescendantsInstances = {character}
  209.     raycastParams.FilterType = Enum.RaycastFilterType.Exclude
  210.  
  211.     local result = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
  212.     if not result or not result.Instance then return end
  213.  
  214.     grapplePoint = result.Position
  215.     setupGrappleVisual()
  216.     moveTo(result.Position)
  217. end
  218.  
  219. -- starts climbing if near wall
  220. function startClimbing()
  221.     if isClimbing or isGrappling or state.current ~= "Idle" then return end
  222.     local nearWall, wallPos = isNearWall()
  223.     if not nearWall then return end
  224.  
  225.     state:set("Climbing")
  226.     isClimbing = true
  227.     local climbStart = tick()
  228.  
  229.     humanoid.PlatformStand = true
  230.     rootPart.Velocity = Vector3.new(0, 0, 0)
  231.  
  232.     RunService:BindToRenderStep("ClimbStep", Enum.RenderPriority.Character.Value + 2, function()
  233.         local dt = tick() - climbStart
  234.         if dt > CLIMB_DURATION then
  235.             RunService:UnbindFromRenderStep("ClimbStep")
  236.             humanoid.PlatformStand = false
  237.             isClimbing = false
  238.             state:set("Cooldown")
  239.             task.delay(COOLDOWN_TIME, function()
  240.                 if not isGrappling then
  241.                     state:set("Idle")
  242.                 end
  243.             end)
  244.             showCooldown(COOLDOWN_TIME)
  245.             return
  246.         end
  247.  
  248.         local climbSpeed = CLIMB_SPEED * dt
  249.         local newPos = rootPart.Position + Vector3.new(0, climbSpeed, 0)
  250.         rootPart.CFrame = CFrame.new(newPos, newPos + rootPart.CFrame.LookVector)
  251.     end)
  252. end
  253.  
  254. -- right click = grapple, left = climb
  255. UserInputService.InputBegan:Connect(function(input, processed)
  256.     if processed then return end
  257.     if input.UserInputType == Enum.UserInputType.MouseButton2 then
  258.         fireGrapple()
  259.     elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
  260.         startClimbing()
  261.     end
  262. end)
  263.  
  264. -- reset stuff on death
  265. humanoid.Died:Connect(function()
  266.     state:set("Idle")
  267.     isClimbing = false
  268.     isGrappling = false
  269.     removeGrappleVisual()
  270. end)
  271.  
  272. -- rebind stuff when respawn
  273. player.CharacterAdded:Connect(function(char)
  274.     character = char
  275.     humanoid = char:WaitForChild("Humanoid")
  276.     rootPart = char:WaitForChild("HumanoidRootPart")
  277.     state:set("Idle")
  278. end)
  279.  
  280. -- keep backpack gui on
  281. StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, true)
Advertisement
Add Comment
Please, Sign In to add comment