arminhello

Grapple and Climb TRY 3 (if ts ain't enough comments u js torturing me atp 😭)

Jul 25th, 2025
784
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.43 KB | Source Code | 0 0
  1. -- grab core services
  2. local Players = game:GetService("Players") -- need this to get the player
  3. local RunService = game:GetService("RunService") -- need this for smooth movement every frame
  4. local UserInputService = game:GetService("UserInputService") -- need this to detect mouse clicks
  5. local TweenService = game:GetService("TweenService") -- need this for smooth cooldown animation
  6. local StarterGui = game:GetService("StarterGui") -- need this to control core gui
  7. local ContextActionService = game:GetService("ContextActionService") -- got this but not using it
  8.  
  9. -- get basic refs
  10. local player = Players.LocalPlayer -- the person playing the game
  11. local camera = workspace.CurrentCamera -- need camera to shoot rays from mouse position
  12.  
  13. -- wait till we got a char
  14. local character = player.Character or player.CharacterAdded:Wait() -- might not have spawned yet so wait
  15. local humanoid = character:WaitForChild("Humanoid") -- controls player movement and health
  16. local rootPart = character:WaitForChild("HumanoidRootPart") -- the main part we move around
  17.  
  18. -- simple state thing to track what we're doing
  19. local state = {} -- using a table to track if player can do actions
  20. state.__index = state -- makes the table work like an object
  21. state.current = "Idle" -- start in idle so player can do stuff
  22.  
  23. function state:set(newState) -- changes what the player is doing
  24.     self.current = newState -- updates the state
  25. end -- done with function
  26.  
  27. function state:is(val) -- checks if we're in a certain state
  28.     return self.current == val -- returns true or false
  29. end -- done with function
  30.  
  31. -- basic config stuff
  32. local GRAPPLE_SPEED = 120 -- studs per second for grapple movement
  33. local CLIMB_SPEED = 1 -- studs per frame for climbing up walls
  34. local CLIMB_DURATION = 1.5 -- seconds to climb before stopping
  35. local WALL_CHECK_DISTANCE = 5 -- studs to check if wall is close enough
  36. local COOLDOWN_TIME = 1.2 -- seconds before player can use abilities again
  37.  
  38. -- visuals and state flags
  39. local grapplePoint = nil -- where the player is grappling to
  40. local isGrappling = false -- prevents multiple grapples at once
  41. local isClimbing = false -- prevents multiple climbs at once
  42. local beam = nil -- the rope visual effect
  43. local attachments = {} -- holds the beam connection points
  44.  
  45. -- ui setup for cooldown bar
  46. local ui = Instance.new("ScreenGui") -- container for all ui elements
  47. ui.Name = "GrappleUI" -- name it so we can find it later
  48. ui.ResetOnSpawn = false -- keep ui when player dies
  49. ui.Parent = player:WaitForChild("PlayerGui") -- put it where player can see it
  50.  
  51. local cooldownBar = Instance.new("Frame") -- background of cooldown timer
  52. cooldownBar.Size = UDim2.new(0, 100, 0, 6) -- small horizontal bar
  53. cooldownBar.Position = UDim2.new(0.5, -50, 0.95, 0) -- bottom center of screen
  54. cooldownBar.BackgroundColor3 = Color3.fromRGB(60, 180, 255) -- blue color
  55. cooldownBar.BorderSizePixel = 0 -- no ugly border
  56. cooldownBar.Visible = false -- hide until cooldown starts
  57. cooldownBar.Parent = ui -- add to screen
  58.  
  59. local cooldownFill = Instance.new("Frame") -- white part that shrinks
  60. cooldownFill.Size = UDim2.new(1, 0, 1, 0) -- fills whole bar at start
  61. cooldownFill.BackgroundColor3 = Color3.fromRGB(255, 255, 255) -- white color
  62. cooldownFill.BorderSizePixel = 0 -- no border
  63. cooldownFill.Parent = cooldownBar -- put inside cooldown bar
  64.  
  65. -- buttons for mobile users
  66. if UserInputService.TouchEnabled then -- only make buttons on phones/tablets
  67.     local grappleButton = Instance.new("TextButton") -- button to grapple
  68.     grappleButton.Size = UDim2.new(0, 100, 0, 40) -- decent size for fingers
  69.     grappleButton.Position = UDim2.new(1, -110, 1, -100) -- bottom right corner
  70.     grappleButton.Text = "Grapple" -- what it says
  71.     grappleButton.BackgroundColor3 = Color3.fromRGB(70, 70, 255) -- blue like grapple
  72.     grappleButton.TextColor3 = Color3.new(1, 1, 1) -- white text
  73.     grappleButton.Font = Enum.Font.SourceSansBold -- readable font
  74.     grappleButton.TextSize = 20 -- big enough to read
  75.     grappleButton.Parent = ui -- add to screen
  76.  
  77.     local climbButton = Instance.new("TextButton") -- button to climb
  78.     climbButton.Size = UDim2.new(0, 100, 0, 40) -- same size as grapple
  79.     climbButton.Position = UDim2.new(1, -110, 1, -150) -- above grapple button
  80.     climbButton.Text = "Climb" -- what it says
  81.     climbButton.BackgroundColor3 = Color3.fromRGB(70, 255, 70) -- green for climb
  82.     climbButton.TextColor3 = Color3.new(1, 1, 1) -- white text
  83.     climbButton.Font = Enum.Font.SourceSansBold -- same font
  84.     climbButton.TextSize = 20 -- same size
  85.     climbButton.Parent = ui -- add to screen
  86.  
  87.     -- hook up buttons to actions
  88.     grappleButton.MouseButton1Click:Connect(function() -- when button pressed
  89.         fireGrapple() -- do the grapple
  90.     end) -- done connecting
  91.  
  92.     climbButton.MouseButton1Click:Connect(function() -- when button pressed
  93.         startClimbing() -- do the climb
  94.     end) -- done connecting
  95. end -- done with mobile buttons
  96.  
  97. -- does the cooldown bar anim
  98. local function showCooldown(duration) -- shows timer after ability use
  99.     cooldownBar.Visible = true -- show the bar
  100.     cooldownFill.Size = UDim2.new(1, 0, 1, 0) -- reset to full size
  101.  
  102.     local tween = TweenService:Create(cooldownFill, TweenInfo.new(duration), { -- animate shrinking
  103.         Size = UDim2.new(0, 0, 1, 0) -- shrink to nothing
  104.     }) -- tween setup done
  105.     tween:Play() -- start animation
  106.     tween.Completed:Wait() -- wait until done
  107.  
  108.     cooldownBar.Visible = false -- hide when cooldown over
  109. end -- done with function
  110.  
  111. -- draws the beam from player to point
  112. local function setupGrappleVisual() -- creates the rope effect
  113.     local a1 = Instance.new("Attachment") -- point on player
  114.     a1.Name = "GrappleStart" -- name for debugging
  115.     a1.Position = Vector3.zero -- center of player part
  116.     a1.Parent = rootPart -- attach to player
  117.  
  118.     local a2 = Instance.new("Attachment") -- point at target
  119.     a2.Name = "GrappleEnd" -- name for debugging
  120.     a2.Parent = workspace.Terrain -- put in world
  121.  
  122.     local b = Instance.new("Beam") -- the rope visual
  123.     b.Attachment0 = a1 -- connect from player
  124.     b.Attachment1 = a2 -- connect to target
  125.     b.Width0 = 0.1 -- thin rope
  126.     b.Width1 = 0.1 -- same width whole way
  127.     b.Color = ColorSequence.new(Color3.fromRGB(200, 200, 255)) -- light blue rope
  128.     b.LightEmission = 1 -- make it glow
  129.     b.FaceCamera = true -- always face player view
  130.     b.Parent = a1 -- attach to start point
  131.  
  132.     attachments.start = a1 -- save for later
  133.     attachments.end_ = a2 -- save for later
  134.     beam = b -- save beam reference
  135. end -- done with function
  136.  
  137. -- kills the visual
  138. local function removeGrappleVisual() -- cleans up rope when done
  139.     if beam then beam:Destroy() end -- remove rope if exists
  140.     if attachments.start then attachments.start:Destroy() end -- remove start point if exists
  141.     if attachments.end_ then attachments.end_:Destroy() end -- remove end point if exists
  142.     attachments = {} -- clear saved references
  143.     beam = nil -- clear beam reference
  144. end -- done with function
  145.  
  146. -- checks if there's a wall in front
  147. local function isNearWall() -- sees if player can climb
  148.     local rayOrigin = rootPart.Position -- start from player center
  149.     local rayDirection = rootPart.CFrame.LookVector * WALL_CHECK_DISTANCE -- check forward direction
  150.  
  151.     local raycastParams = RaycastParams.new() -- setup ray settings
  152.     raycastParams.FilterDescendantsInstances = {character} -- dont hit yourself
  153.     raycastParams.FilterType = Enum.RaycastFilterType.Exclude -- ignore listed stuff
  154.  
  155.     local result = workspace:Raycast(rayOrigin, rayDirection, raycastParams) -- shoot the ray
  156.     if result and result.Instance and not result.Instance:IsDescendantOf(character) then -- hit something that isnt player
  157.         return true, result.Position -- yes theres a wall and heres where
  158.     end -- done checking
  159.     return false -- no wall found
  160. end -- done with function
  161.  
  162. -- handles grapple move step
  163. local function moveTo(position) -- moves player to grapple point
  164.     local direction = (position - rootPart.Position) -- vector from player to target
  165.     local distance = direction.Magnitude -- how far to travel
  166.     local duration = distance / GRAPPLE_SPEED -- time it should take
  167.     local t0 = tick() -- remember start time
  168.  
  169.     state:set("Grappling") -- player is busy grappling
  170.     isGrappling = true -- flag to prevent other actions
  171.  
  172.     RunService:BindToRenderStep("GrappleStep", Enum.RenderPriority.Character.Value + 1, function() -- run every frame
  173.         local dt = tick() - t0 -- how long since start
  174.         if dt >= duration or not grapplePoint then -- reached target or cancelled
  175.             RunService:UnbindFromRenderStep("GrappleStep") -- stop moving
  176.             isGrappling = false -- not grappling anymore
  177.             state:set("Cooldown") -- start cooldown period
  178.             task.delay(COOLDOWN_TIME, function() -- after cooldown time
  179.                 if not isClimbing then -- unless climbing started
  180.                     state:set("Idle") -- go back to idle
  181.                 end -- done checking
  182.             end) -- done with delay
  183.             showCooldown(COOLDOWN_TIME) -- show cooldown visual
  184.             removeGrappleVisual() -- remove rope
  185.             return -- exit this frame
  186.         end -- done checking if finished
  187.  
  188.         local step = rootPart.CFrame.Position:Lerp(grapplePoint, math.clamp(dt / duration, 0, 1)) -- smooth position between start and end
  189.         local look = (grapplePoint - rootPart.Position).Unit -- direction to look
  190.         rootPart.CFrame = CFrame.new(step, step + look) -- move and rotate player
  191.  
  192.         if attachments.end_ then -- if rope end exists
  193.             attachments.end_.WorldPosition = grapplePoint -- keep rope connected to target
  194.         end -- done updating rope
  195.     end) -- done with frame function
  196. end -- done with function
  197.  
  198. -- shoot ray from mouse, see what we hit
  199. function fireGrapple() -- main grapple function
  200.     if isGrappling or isClimbing or state.current ~= "Idle" then return end -- cant grapple if busy
  201.  
  202.     local mousePos = UserInputService:GetMouseLocation() -- where mouse is on screen
  203.     local unitRay = camera:ViewportPointToRay(mousePos.X, mousePos.Y) -- ray from camera through mouse
  204.     local rayOrigin = unitRay.Origin -- where ray starts
  205.     local rayDirection = unitRay.Direction * 1000 -- ray direction times big number for range
  206.  
  207.     local raycastParams = RaycastParams.new() -- setup ray settings
  208.     raycastParams.FilterDescendantsInstances = {character} -- dont hit yourself
  209.     raycastParams.FilterType = Enum.RaycastFilterType.Exclude -- ignore listed stuff
  210.  
  211.     local result = workspace:Raycast(rayOrigin, rayDirection, raycastParams) -- shoot ray into world
  212.     if not result or not result.Instance then return end -- didnt hit anything so stop
  213.  
  214.     grapplePoint = result.Position -- save where we hit
  215.     setupGrappleVisual() -- make the rope
  216.     moveTo(result.Position) -- start moving there
  217. end -- done with function
  218.  
  219. -- starts climbing if near wall
  220. function startClimbing() -- main climb function
  221.     if isClimbing or isGrappling or state.current ~= "Idle" then return end -- cant climb if busy
  222.     local nearWall, wallPos = isNearWall() -- check if wall nearby
  223.     if not nearWall then return end -- no wall so stop
  224.  
  225.     state:set("Climbing") -- player is busy climbing
  226.     isClimbing = true -- flag to prevent other actions
  227.     local climbStart = tick() -- remember when started
  228.  
  229.     humanoid.PlatformStand = true -- stops normal physics so we control movement
  230.     rootPart.Velocity = Vector3.new(0, 0, 0) -- stop any existing movement
  231.  
  232.     RunService:BindToRenderStep("ClimbStep", Enum.RenderPriority.Character.Value + 2, function() -- run every frame
  233.         local dt = tick() - climbStart -- how long climbing
  234.         if dt > CLIMB_DURATION then -- climbed long enough
  235.             RunService:UnbindFromRenderStep("ClimbStep") -- stop climbing movement
  236.             humanoid.PlatformStand = false -- return to normal physics
  237.             isClimbing = false -- not climbing anymore
  238.             state:set("Cooldown") -- start cooldown period
  239.             task.delay(COOLDOWN_TIME, function() -- after cooldown time
  240.                 if not isGrappling then -- unless grappling started
  241.                     state:set("Idle") -- go back to idle
  242.                 end -- done checking
  243.             end) -- done with delay
  244.             showCooldown(COOLDOWN_TIME) -- show cooldown visual
  245.             return -- exit this frame
  246.         end -- done checking if finished
  247.  
  248.         local climbSpeed = CLIMB_SPEED * dt -- how much to move up based on time
  249.         local newPos = rootPart.Position + Vector3.new(0, climbSpeed, 0) -- add upward movement
  250.         rootPart.CFrame = CFrame.new(newPos, newPos + rootPart.CFrame.LookVector) -- move up and keep facing same way
  251.     end) -- done with frame function
  252. end -- done with function
  253.  
  254. -- right click = grapple, left = climb
  255. UserInputService.InputBegan:Connect(function(input, processed) -- when any input happens
  256.     if processed then return end -- game already handled it so ignore
  257.     if input.UserInputType == Enum.UserInputType.MouseButton2 then -- right mouse button
  258.         fireGrapple() -- do grapple
  259.     elseif input.UserInputType == Enum.UserInputType.MouseButton1 then -- left mouse button
  260.         startClimbing() -- do climb
  261.     end -- done checking input
  262. end) -- done connecting input
  263.  
  264. -- reset stuff on death
  265. humanoid.Died:Connect(function() -- when player dies
  266.     state:set("Idle") -- reset state so respawn works right
  267.     isClimbing = false -- clear climb flag
  268.     isGrappling = false -- clear grapple flag
  269.     removeGrappleVisual() -- clean up any rope
  270. end) -- done connecting death
  271.  
  272. -- rebind stuff when respawn
  273. player.CharacterAdded:Connect(function(char) -- when player respawns
  274.     character = char -- update character reference
  275.     humanoid = char:WaitForChild("Humanoid") -- get new humanoid
  276.     rootPart = char:WaitForChild("HumanoidRootPart") -- get new root part
  277.     state:set("Idle") -- start in idle state
  278. end) -- done connecting respawn
  279.  
  280. -- keep backpack gui on
  281. StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, true) -- makes sure inventory stays visible
Advertisement
Add Comment
Please, Sign In to add comment