local UIS = game:GetService("UserInputService") local Players = game:GetService("Players") local RunService = game:GetService("RunService") local Workspace = game:GetService("Workspace") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoidRootPart = character:WaitForChild("HumanoidRootPart") local camera = Workspace.CurrentCamera --------------------------------------------------- -- CONFIGS --------------------------------------------------- local maxDistance = 5000 local pullStrength = 1 -- rope tension local boostForce = 70 -- CTRL boost local idlePull = 20 -- gentle pull toward anchor local maxSwingSpeed = 200 -- maximum swing speed local airDrag = 1 -- speed decay local cableThickness = 0.45 local aimRadius = 25 -- radius for aim assist in studs -- FOV boost settings local defaultFOV = camera and camera.FieldOfView or 70 local fovMax = 95 -- maximum FOV at top speed local fovSpeedStart = 60 -- speed (studs/s) where FOV starts increasing local fovSpeedMax = 200 -- speed that yields full fovMax local fovLerpSpeed = 6 -- smoothing factor (higher = faster) -- Grapple distance multiplier settings local grappleDistanceStart = 30 -- distance (studs) where extra pull begins local grappleDistanceMax = maxDistance local grappleMaxMultiplier = 2.0 -- max multiplier at farthest distance local grappleMinMultiplier = 1.0 -- multiplier at or below start distance --------------------------------------------------- local left = { active = false } local right = { active = false } local boosting = false local function createAnchor(position, hitPart) local anchor = Instance.new("Part") anchor.Size = Vector3.new(0.4, 0.4, 0.4) anchor.Anchored = true anchor.CanCollide = false anchor.Transparency = 1 anchor.Position = position anchor.Parent = Workspace if hitPart then local weld = Instance.new("WeldConstraint") weld.Part0 = anchor weld.Part1 = hitPart weld.Parent = anchor end return anchor end local function createCable(color) local attach0 = Instance.new("Attachment") attach0.Position = Vector3.new(0, 0.5, 0) attach0.Parent = humanoidRootPart local beam = Instance.new("Beam") beam.Attachment0 = attach0 beam.Width0 = cableThickness beam.Width1 = cableThickness beam.FaceCamera = true beam.LightEmission = 0.7 beam.Color = ColorSequence.new(color) beam.Parent = humanoidRootPart return beam, attach0 end local function getMouseHit() local mouse = player:GetMouse() local ray = camera:ScreenPointToRay(mouse.X, mouse.Y) local params = RaycastParams.new() params.FilterDescendantsInstances = { character } params.FilterType = Enum.RaycastFilterType.Blacklist local result = Workspace:Raycast(ray.Origin, ray.Direction * maxDistance, params) if result and result.Instance and result.Instance.CanCollide then return result.Position, result.Instance end local closestPart local closestPoint local closestDist = math.huge local rayEnd = ray.Origin + ray.Direction * maxDistance for _, part in ipairs(Workspace:GetDescendants()) do if part:IsA("BasePart") and part.CanCollide then local point = part.Position local dist = (point - rayEnd).Magnitude if dist < aimRadius and dist < closestDist then closestDist = dist closestPart = part closestPoint = point end end end if closestPart then return closestPoint, closestPart end return nil, nil end local function fire(side) local data = (side == "left") and left or right if data.active then return end local hitPos, hitPart = getMouseHit() if not hitPos or not hitPart then return end data.anchor = createAnchor(hitPos, hitPart) data.attach1 = Instance.new("Attachment", data.anchor) data.beam, data.attach0 = createCable( side == "left" and Color3.fromRGB(255, 70, 70) or Color3.fromRGB(80, 180, 255) ) data.beam.Attachment1 = data.attach1 data.length = (humanoidRootPart.Position - hitPos).Magnitude -- compute per-grapple pull multiplier based on distance local dist = data.length local t = 0 if dist > grappleDistanceStart then t = math.clamp((dist - grappleDistanceStart) / math.max(1, (grappleDistanceMax - grappleDistanceStart)), 0, 1) end data.pullMultiplier = grappleMinMultiplier + (grappleMaxMultiplier - grappleMinMultiplier) * t data.active = true end local function release(side) local data = (side == "left") and left or right if not data.active then return end data.beam:Destroy() data.attach0:Destroy() data.attach1:Destroy() data.anchor:Destroy() data.active = false end RunService.Heartbeat:Connect(function(dt) for _, data in ipairs({ left, right }) do if data.active then local hrp = humanoidRootPart local toAnchor = data.anchor.Position - hrp.Position local distance = toAnchor.Magnitude local dir = toAnchor.Unit if distance > data.length then hrp:ApplyImpulse(dir * (distance - data.length) * pullStrength * (data.pullMultiplier or 1)) end hrp:ApplyImpulse(dir * idlePull * (data.pullMultiplier or 1)) if boosting then hrp:ApplyImpulse(dir * boostForce * (data.pullMultiplier or 1)) end local vel = hrp.AssemblyLinearVelocity if vel.Magnitude > maxSwingSpeed then hrp.AssemblyLinearVelocity = vel.Unit * maxSwingSpeed end hrp.AssemblyLinearVelocity *= airDrag end end -- FOV boost based on speed local speed = 0 if humanoidRootPart and humanoidRootPart.AssemblyLinearVelocity then speed = humanoidRootPart.AssemblyLinearVelocity.Magnitude end local t = 0 if speed > fovSpeedStart then t = math.clamp((speed - fovSpeedStart) / math.max(1, (fovSpeedMax - fovSpeedStart)), 0, 1) end local targetFOV = defaultFOV + (fovMax - defaultFOV) * t if camera then camera.FieldOfView = camera.FieldOfView + (targetFOV - camera.FieldOfView) * math.clamp(fovLerpSpeed * dt, 0, 1) end end) UIS.InputBegan:Connect(function(input, gp) if gp then return end if input.KeyCode == Enum.KeyCode.Q then fire("left") elseif input.KeyCode == Enum.KeyCode.E then fire("right") elseif input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.RightControl then boosting = true end end) UIS.InputEnded:Connect(function(input) if input.KeyCode == Enum.KeyCode.Q then release("left") elseif input.KeyCode == Enum.KeyCode.E then release("right") elseif input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.RightControl then boosting = false end end) player.CharacterAdded:Connect(function(char) character = char humanoidRootPart = char:WaitForChild("HumanoidRootPart") boosting = false release("left") release("right") end) --------------------------------------------------- print("ODM GRAPPLE LOADED") print("Hold Q/E = Grapple | Hold CTRL = Boost | Aim near objects to auto-snap")