[open source] sekiro grapple hook

You can set a debounce on clicking! Sorry by this late response… But i hope that helps you!

Will this be mobile compatible as well? Also epic resource for new devs! :+1:

3 Likes

This was my literal dream as a little kid on roblox. I always wanted to make one of these!!! THANK YOU :star_struck:

Make a batman game? :thinking:
“Thats amazing.”

1 Like

Since grapple_client do most of the work, is this expensive for the server to handle?

YOOOO This is actually REALLY dopeee!!! Thank you for sharing this with the community, Will def. have to check this out ;D

Really enjoyed this this mechanic, really hope to see more of your work. :ok_hand:

1 Like

What method is used to move the player towards its target?

It’s official… this is the COOLEST thing I’ve ever seen on this platform.

I’d Prefer an easier way to select grappling points, but besides that, this is amazing!

Why do I get sent to 0, -340282346638528859811704183484516925440, 0 when I use it twice in a short period of time?

I’m having the same problem… how do I add a debounce to it?

Really cool source! What you do for the community is really good.

Keep up the great work! :slightly_smiling_face:

Actually, my theory was wrong, there’s already a debounce line, besides that it is not related with the bug, it would be an issue related with the distance, i tried to fix it from checking the distance between the player and the place you wanna grapple, and if the distance is not long enough to grapple, then just return nothing, and it worked.

This is the script related to the bug:
image

And here’s the code with the bug fixed:

-- setup
local MinDistanceRequired = 2 --If you still have the bug, try to lightly increment this value
local import = require(game.ReplicatedStorage:WaitForChild("import"))

-- constants

local Services = import("shared/services")
local Raycast = import("shared/raycast")
local Grapple = import("client/grapple")
local GrappleEffect = import("effects/grapple")
local GrappleRemote = import("remotes/grapple_hook")

local Player = Services.Players.LocalPlayer
local Camera = Services.Workspace.CurrentCamera

local GrapplePoints = Services.Workspace:WaitForChild("GrapplePoints")

-- variables

local character, humanoid, root
local animations = {}

local lastUpdate = 0
local grappleCooldown = 0

local grappling = false

-- functions

local function HandleCharacter(newCharacter) -- set up variables for new characters
	character, humanoid, root = nil, nil, nil
	animations = {}
	
	if not newCharacter then
		return
	end
	
	-- load animations
	humanoid = newCharacter:WaitForChild("Humanoid")
	local animFolder = newCharacter:WaitForChild("GrappleAnimations")
	
	animations = {
		Cast = humanoid:LoadAnimation(animFolder:WaitForChild("Cast"))
	}
	
	-- wait for the root
	root = newCharacter:WaitForChild("HumanoidRootPart")
	character = newCharacter
end

local function GrappleToPosition(position)
	-- make sure the character exists
	if not character then
		return
	end
	
	-- dont allow grappling while already grappling
	if grappling then
		return
	end
	
	local direction = (position - root.Position)
	local travelCFrame = CFrame.new(position + Vector3.new(0, 4, 0), position + Vector3.new(direction.X, 4, direction.Z))
	
	if (root.Position - travelCFrame.Position).Magnitude < MinDistanceRequired then --//Here we check if the distance between our character's root and the grapple position is lower than the minimum distance required.
		return
	end
	
	-- fire remote so the server can replicate the effect
	GrappleRemote:FireServer(position)
	
	grappling = true
	
	-- play the grapple effect on the client
	GrappleEffect:Play(character, position, Grapple.CastTime, Grapple.TravelTime)
	
	-- ez camera tween for more impact
	local cameraInfo = TweenInfo.new((Grapple.CastTime + Grapple.TravelTime) / 2, Enum.EasingStyle.Quad, Enum.EasingDirection.In, 0, true)
	local cameraTween = Services.TweenService:Create(Camera, cameraInfo, {FieldOfView = Camera.FieldOfView + 30})
	cameraTween:Play()
	-- cast
	-- play the cast animation
	animations.Cast:Play(0.1, 1, 1/(Grapple.CastTime * 2))
	
	-- tween the character into position, keeping them floating in the air
	local castInfo = TweenInfo.new(Grapple.CastTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
	local castTween = Services.TweenService:Create(root, castInfo, {CFrame = CFrame.new(root.Position, position)})
	
	castTween:Play()
	castTween.Completed:Wait()
	-- travel
	-- calculate the landing position and tween to it
	
	local travelInfo = TweenInfo.new(Grapple.TravelTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
	local travelTween = Services.TweenService:Create(root, travelInfo, {CFrame = travelCFrame})
	
	travelTween:Play()
	travelTween.Completed:Wait()
	-- land
	-- switch to getting up in case we tripped somehow
	humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
	grappling = false
end

-- events

GrappleRemote.OnClientEvent:Connect(function(...) -- play the grapple effect when the server says to
	GrappleEffect:Play(...)
end)

Services.UserInputService.InputBegan:Connect(function(inputObject, processed) -- basic key input
	if processed then
		return
	end
	
	if inputObject.KeyCode == Grapple.Hotkey then
		if not Grapple.Target then
			return
		end
		
		GrappleToPosition(Grapple.Target.Position)
	end
end)

Player.CharacterAdded:Connect(HandleCharacter)

-- main

Services.RunService.Stepped:Connect(function() -- main targeting logic
	if not character then
		return
	end
	
	-- if we're grappling, set the root velocity to 0,0,0 to avoid weird physics glitches
	if grappling then
		root.Velocity = Vector3.new()
	end
	
	-- only update the target if enough time has elapsed
	if tick() - lastUpdate < Grapple.TargetUpdateTime then
		return
	end
	
	lastUpdate = tick()
	
	local targets = {}
	
	for _, point in pairs(GrapplePoints:GetChildren()) do
		local offset = point.Position - root.Position
		local distance = offset.Magnitude
		local dotted = Camera.CFrame.LookVector:Dot(offset)
		
		-- make sure the point is in front of us and within double range
		if dotted > 0 and distance < Grapple.MaxRange * 2 then
			-- check that the grapple point is visible, ignoring characters
			local ignore = {}
			
			for _, player in pairs(Services.Players:GetPlayers()) do
				if player.Character and player.Character.Parent then
					table.insert(ignore, player.Character)
				end
			end
			
			local hit = Raycast:CastIgnore(root.Position, offset, ignore)
			
			if hit and hit == point then
				-- add the target to our dictionary, with info about angle from center and distance
				local angle = math.acos(dotted / offset.Magnitude)
				
				targets[point] = {Angle = angle, Distance = distance}
			end
		end
	end
	
	-- select the target closest to the middle of the screen
	local newTarget, angle = nil, 10000
	
	for target, info in pairs(targets) do
		if info.Angle < angle then
			newTarget = target
			angle = info.Angle
		end
	end
	
	-- set closest target
	Grapple.ClosestTarget = newTarget
	
	-- if the closest target is out of range, don't set the primary target
	if newTarget then
		local info = targets[newTarget]
		
		Grapple.ClosestRange = info.Distance
		
		if info.Distance > Grapple.MaxRange then
			newTarget = nil
		end
	end
	
	Grapple.Target = newTarget
end)

-- init

HandleCharacter(Player.Character)

Remember you can tell me if there’s any issue with it or something. Thanks for reading.

3 Likes

Where Would I put the Animation?

it might be just me but I don’t think the stream link works anymore

Awesome!! This is actually one of the best grapple hooks I’ve ever seen!

It’s not I tested the link and it does not work for me either :stuck_out_tongue:

Would it be possible to butcher this in a way?

you want to change it into R6 but you don’t know how? follow my lead
first, you gotta change it into R6 and then go to ReplicatedStorage.effects.grapple
and open the module then go to line 20

 local hand = character:FindFirstChild("RightHand")

change the “RightHand” into “Right Arm”
this should look like this:

local hand = character:FindFirstChild("Right Arm")

and we’re done!
don’t forget to change the animation into R6

it’s R6 compatible but it just doesn’t show the chain and effect from the grapple

1 Like