Issues with grappling server script

You can write your topic however you want, but you need to answer these questions:

  1. What do I want to achieve?
    I am making a grapple hook system. When you press Q or E, you will grapple towards where ever your mouse is pointing.
  2. What is the issue? Include screenshots / videos if possible!
    Whenever I “double-hook”, or press Q and E at the same time, the script breaks entirely and the player won’t be able to grapple.
    As seen here:
    https://gyazo.com/13ac5828518ca9a6d959d7c5d1aef82a
Client Script
--//Services
local InputService = game:GetService("UserInputService")
local RunService   = game:GetService("RunService")

--//Locals
local player     = game.Players.LocalPlayer
local camera     = workspace.CurrentCamera
local mouse      = player:GetMouse()
local character  = player.Character

local CameraShaker = require(game.ReplicatedStorage.CameraShaker)
local RightHook = false
local LeftHook = false

--.. gear stuff
local gearFolder      = script.Parent.Parent
local valuesFolder    = gearFolder:WaitForChild("Values")
local remotesFolder   = gearFolder:WaitForChild("Remotes")
local animationFolder = gearFolder:WaitForChild("Animations")

local burstCooldown = 2

local Multiplier = 1


local gasBoost = false
local Blades = false

--..body movers
local bodyGyro     = character.HumanoidRootPart:WaitForChild("BodyGyro")
local bodyVelocity = character.HumanoidRootPart:WaitForChild("BodyVelocity")

--..debounce
local debounce  = false
local cooldown  = (1/4)

--..camera stuff
local tiltAngle = 0

local canReload = true

local BladesAmount = valuesFolder.BladeAmount.Value
local GasAmount = valuesFolder.GasAmount.Value

local ODMGui = gearFolder.ODMGui:Clone()
ODMGui.Parent = player.PlayerGui

local gasBar = ODMGui.GasFrame.count

local function UpdateBar()
	gasBar:TweenSize(UDim2.new(0.296, 0,GasAmount/-1526.71756, 0))
end

UpdateBar()

local Grapple = animationFolder["Grapple"]
local BladeChange = animationFolder["BladeChange"]
local RightOrbit = animationFolder["RightOrbit"]
local LeftOrbit = animationFolder["LeftOrbit"]
local VerticalSpin = animationFolder["VerticalSpin"]
local HorizontalSpin = animationFolder["HorizontalSpin"]



--..animation stuff
--local leftCycle  = character.Humanoid:LoadAnimation(animationFolder["LeftCycle"])
--local rightCycle = character.Humanoid:LoadAnimation(animationFolder["RightCycle"])

--//Functions
local function lerpUnit(a ,b ,c)
	return a + (b - a) * c
end

local function checkHookPlayerDistance()
	return math.abs((character.HumanoidRootPart.CFrame.Position - mouse.Hit.Position).Magnitude)
end

local function returnHookDirection(side)
	if valuesFolder[side.."Position"].Value == Vector3.new() then 
		return Vector3.new() 
	end
	return (valuesFolder[side.."Position"].Value - character.HumanoidRootPart.CFrame.Position).Unit
end

local function onInputBegan(key, event)
	if event then return end
	if debounce then return end
	--..scripting the right hook ejection key
	if key.KeyCode == Enum.KeyCode.E and not debounce then
		if checkHookPlayerDistance() <= valuesFolder["MaxHookDistance"].Value then
			if mouse.Target ~= nil then
				--.. we can eject hook and do other cool stuff
				remotesFolder["Grapple"]:FireServer({"Eject", "Right", mouse.Hit.Position, mouse.Target})
				debounce = true
				RightHook = true
				wait(cooldown)
				debounce = false
				
				GasAmount = GasAmount - 1
				
				local track = character:WaitForChild("Humanoid"):LoadAnimation(Grapple)
				track:Play()
				
				for i,v in pairs (character.Humanoid:GetPlayingAnimationTracks())do
					v:Stop()
				end
				
				track.Priority = Enum.AnimationPriority.Action

				print(GasAmount)
				UpdateBar()
			end
		end
		
	elseif key.KeyCode == Enum.KeyCode.Q and not debounce then
		if checkHookPlayerDistance() <= valuesFolder["MaxHookDistance"].Value then
			if mouse.Target ~= nil then
				--.. we can eject hook and do other cool stuff
				remotesFolder["Grapple"]:FireServer({"Eject", "Left", mouse.Hit.Position, mouse.Target})
				debounce = true
				LeftHook = true
				wait(cooldown)
				debounce = false
				
				GasAmount = GasAmount - 1
				local track = character:WaitForChild("Humanoid"):LoadAnimation(Grapple)
				track:Play()
				
				for i,v in pairs (character.Humanoid:GetPlayingAnimationTracks())do
					v:Stop()
				end
				
				track.Priority = Enum.AnimationPriority.Action
				
				print(GasAmount)
				UpdateBar()

			end
		end
	end
end

local function onInputEnded(key, event)
	if event then return end

	if key.KeyCode == Enum.KeyCode.E then
		if valuesFolder["RightHooked"].Value or valuesFolder["RightEjected"].Value then
			remotesFolder["Grapple"]:FireServer({"Retract", "Right"})
			RightHook = false
		end

	elseif key.KeyCode == Enum.KeyCode.Q  then
		if valuesFolder["LeftHooked"].Value or valuesFolder["LeftEjected"].Value then
			remotesFolder["Grapple"]:FireServer({"Retract", "Left"})
			LeftHook = false
		end
	end
end


--//Events
InputService.InputBegan:Connect(onInputBegan)

InputService.InputEnded:Connect(onInputEnded)

InputService.InputBegan:connect(function(input, isTyping)
	if isTyping then return end
	if input.KeyCode == Enum.KeyCode.R and canReload and BladesAmount > 0 and not Blades then
		remotesFolder:WaitForChild("Reload"):FireServer()
		canReload = false
		Blades = true
		
		for i,v in pairs (character.Humanoid:GetPlayingAnimationTracks())do
			v:Stop()
		end
		
		local track = character:WaitForChild("Humanoid"):LoadAnimation(BladeChange)
		track:Play()
		
		
		track.Priority = Enum.AnimationPriority.Action
		wait(1)
		canReload = true
	end
end)



InputService.InputBegan:connect(function(input, isTyping)
	if isTyping then return end
	if input.KeyCode == Enum.KeyCode.Space and GasAmount > 0 and LeftHook or RightHook then
		gasBoost = true
		Multiplier = 1.5
		print("Gas! Gas! Gas!")
		remotesFolder["Boost"]:FireServer(gasBoost)
		GasAmount = GasAmount - 1

		print(GasAmount)
		UpdateBar()
	end 
end)



InputService.InputEnded:connect(function(input, isTyping)
	if isTyping then return end
	if input.KeyCode == Enum.KeyCode.Space then
		gasBoost = false
		Multiplier = 1
		print("Stopped the gas function.")
		remotesFolder["Boost"]:FireServer(gasBoost)
	end 
end)

InputService.InputBegan:connect(function(input, isTyping)
	if isTyping then return end
	if input.KeyCode == Enum.KeyCode.R and canReload and Blades then
		remotesFolder:WaitForChild("ReturnBlade"):FireServer()
		canReload = false
		Blades = false
		
		for i,v in pairs (character:WaitForChild("Humanoid"):GetPlayingAnimationTracks())do
			v:Stop()
		end
		
		local track = character:WaitForChild("Humanoid"):LoadAnimation(BladeChange)
		track:Play()
		
		
		track.Priority = Enum.AnimationPriority.Action
		wait(1)
		canReload = true
	end
end)

InputService.InputBegan:connect(function(input, isTyping)
	local chance = math.random(1,2)
	if isTyping then return end
	if input.UserInputType == Enum.UserInputType.MouseButton1 and Blades then
		if chance == 1 then
				
			for i,v in pairs (character:WaitForChild("Humanoid"):GetPlayingAnimationTracks())do
				v:Stop()
			end
			
			local track = character:WaitForChild("Humanoid"):LoadAnimation(VerticalSpin)
			track:Play()
		
			
			track.Priority = Enum.AnimationPriority.Action
			remotesFolder["Attack"]:FireServer()
			
		elseif chance == 2 then
			
			for i,v in pairs (character:WaitForChild("Humanoid"):GetPlayingAnimationTracks())do
				v:Stop()
			end
			
			local track = character:WaitForChild("Humanoid"):LoadAnimation(HorizontalSpin)
			track:Play()
			
			
			track.Priority = Enum.AnimationPriority.Action
			remotesFolder["Attack"]:FireServer()

		end
	end
end)

InputService.InputBegan:connect(function(input, isTyping)
	local DoneOnce = false
	local briefCooldown = 0.3
	
	if isTyping then return end
	if input.KeyCode == Enum.KeyCode.W then
		if not DoneOnce then
			DoneOnce = true
			wait(briefCooldown)
			DoneOnce = false
			return
		else 
		if DoneOnce == true then
			print("Burst!")
			DoneOnce = false
			end
		end
	end
end)
--//Loops
while true do
	RunService.Heartbeat:Wait()
	if valuesFolder["RightHooked"].Value or valuesFolder["LeftHooked"].Value then
		character.Humanoid.AutoRotate = false
		-- body gyro
		bodyGyro.MaxTorque = lerpUnit(bodyGyro.MaxTorque, Vector3.new(150000 * Multiplier, 150000 * Multiplier, 150000 * Multiplier), 0.3)
		bodyGyro.CFrame = CFrame.new(character.HumanoidRootPart.Position, valuesFolder["RightPosition"].Value + valuesFolder["LeftPosition"].Value)

		-- body velocity
		bodyVelocity.MaxForce = lerpUnit(bodyVelocity.MaxForce, Vector3.new(200000 * Multiplier, 200000 * Multiplier, 200000 * Multiplier), 0.1)

		if InputService:IsKeyDown(Enum.KeyCode.A) then
			for i,v in pairs (character.Humanoid:GetPlayingAnimationTracks())do
				v:Stop()
			end
			
			local track = character:WaitForChild("Humanoid"):LoadAnimation(RightOrbit)
			track:Play()
			
			
			track.Priority = Enum.AnimationPriority.Movement
			bodyVelocity.Velocity = lerpUnit(bodyVelocity.Velocity, (returnHookDirection("Right") + returnHookDirection("Left") + (-character.HumanoidRootPart.CFrame.RightVector)) * (valuesFolder["Magnitude"].Value - 30) * Multiplier, 0.08)
			--bodyGyro.CFrame = CFrame.new(character.HumanoidRootPart.Position, valuesFolder["RightPosition"].Value + valuesFolder["LeftPosition"].Value) * CFrame.Angles(0, 0, math.rad(25))
		elseif InputService:IsKeyDown(Enum.KeyCode.D) then
			for i,v in pairs (character.Humanoid:GetPlayingAnimationTracks())do
				v:Stop()
			end

			local track = character:WaitForChild("Humanoid"):LoadAnimation(LeftOrbit)
			track:Play()
			
			track.Priority = Enum.AnimationPriority.Movement
			bodyVelocity.Velocity = lerpUnit(bodyVelocity.Velocity, (returnHookDirection("Right") + returnHookDirection("Left") + (character.HumanoidRootPart.CFrame.RightVector)) * (valuesFolder["Magnitude"].Value - 30) * Multiplier, 0.08)
			--bodyGyro.CFrame = CFrame.new(character.HumanoidRootPart.Position, valuesFolder["RightPosition"].Value + valuesFolder["LeftPosition"].Value) * CFrame.Angles(0, 0, math.rad(-25))
		else
			bodyVelocity.Velocity = lerpUnit(bodyVelocity.Velocity, (returnHookDirection("Right") + returnHookDirection("Left")) * (valuesFolder["Magnitude"].Value * Multiplier), 0.2)
			--bodyGyro.CFrame = CFrame.new(character.HumanoidRootPart.Position, valuesFolder["RightPosition"].Value + valuesFolder["LeftPosition"].Value) * CFrame.Angles(0, 0, math.rad(0))
		end
		camera.FieldOfView = lerpUnit(camera.FieldOfView, 85, 0.05)
			

		else
			-- reset values when we arent hooked 
			character.Humanoid.AutoRotate = true
			bodyVelocity.MaxForce = lerpUnit(bodyVelocity.MaxForce, Vector3.new(), 0.2)
			bodyGyro.MaxTorque = lerpUnit(bodyGyro.MaxTorque, Vector3.new(), 0.3)
			camera.FieldOfView = lerpUnit(camera.FieldOfView, 70, 0.05)
	end
end


--valuesFolder["Magnitude"].Value
Server Script
--//Services
local TweenService = game:GetService("TweenService")
--//Locals
--..tweens tuff
local tweenInfo  = TweenInfo.new(0.2, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
local tweenTable = {}

local leftgrapple = false
local rightgrapple = false

local Blades = false
--.. gear stuff
local gearFolder      = script.Parent.Parent
local valuesFolder    = gearFolder:WaitForChild("Values")
local remotesFolder   = gearFolder:WaitForChild("Remotes")
local Animations	  = gearFolder:WaitForChild("Animations")

--//Functions
local function createAttachment(_ancestor)
	local attachment = Instance.new("Attachment")
	attachment.Parent = _ancestor
	attachment.Name = "RopeAttachment"
	return attachment
end

local function createPseudoHook(_ancestor)
	local hook = Instance.new("Part")
	hook.Anchored = true
	hook.Size = Vector3.new(0.1,0.1,0.1)
	hook.CFrame = _ancestor.CFrame
	hook.Parent = _ancestor
	hook.Name = "PseudoHook"
	return hook
end


--.. really and truly its a spring but it has the cool effect we want
local function createRope(_ancestor, attahcment0, attachment1)
	local rope = Instance.new("SpringConstraint")
	rope.Name = "Rope"
	rope.Thickness = 0.1
	rope.Coils = 6
	rope.Radius = 2
	rope.Color = BrickColor.Black()
	rope.Visible = true
	rope.Attachment0 = attahcment0
	rope.Attachment1 = attachment1
	rope.Parent = _ancestor
	return rope
end

local function createWeld(part0, part1)
	local weld = Instance.new("WeldConstraint")
	weld.Part0 = part0
	weld.Part1 = part1
	weld.Parent = part1
	weld.Name = "HookWeld"
end




local function onServerEvent(client, array)
	local character = client.Character
	if array[1] == "Eject" then
		if array[2] == "Right" then
			if valuesFolder["RightEjected"].Value then return end
			if valuesFolder["RightHooked"].Value then return end

			valuesFolder["RightEjected"].Value = true
			character["Torso"].rightgrapple.Eject.Enabled = true
			local hook = createPseudoHook(character["Torso"].rightgrapple)
			local attachment = createAttachment(hook)
			local rope = createRope(hook, hook.Parent["RopeAttachment"], attachment)

			tweenTable.tweenHookCFrameRight = TweenService:Create(hook, tweenInfo, {CFrame = CFrame.new(array[3])})
			tweenTable.tweenRopeRadiusRight = TweenService:Create(rope, tweenInfo, {Radius = 0})
			tweenTable.tweenHookCFrameRight:Play()
			tweenTable.tweenRopeRadiusRight:Play()
			character["Torso"].rightgrapple.Grapple:Play()

			tweenTable.tweenHookCFrameRight.Completed:Connect(function(state)
				if state == Enum.PlaybackState.Completed then
					valuesFolder["RightHooked"].Value = true
					valuesFolder["RightEjected"].Value = false
					valuesFolder["RightPosition"].Value = hook.Position
					createWeld(array[4], hook)	
					character["Torso"].Ejector.Gas.Enabled = true
				end
				tweenTable.tweenHookCFrameRight = nil
				tweenTable.tweenRopeRadiusRight = nil
			end)
			character["Torso"].rightgrapple.Eject.Enabled = false
		elseif array[2] == "Left" then
			if valuesFolder["LeftEjected"].Value then return end
			if valuesFolder["LeftHooked"].Value then return end

			valuesFolder["LeftEjected"].Value = true
			character["Torso"].leftgrapple.Eject.Enabled = true
			local hook = createPseudoHook(character["Torso"].leftgrapple)
			local attachment = createAttachment(hook)
			local rope = createRope(hook, hook.Parent["RopeAttachment"], attachment)

			tweenTable.tweenHookCFrameLeft = TweenService:Create(hook, tweenInfo, {CFrame = CFrame.new(array[3])})
			tweenTable.tweenRopeRadiusLeft = TweenService:Create(rope, tweenInfo, {Radius = 0})
			tweenTable.tweenHookCFrameLeft:Play()
			tweenTable.tweenRopeRadiusLeft:Play()
			character["Torso"].leftgrapple.Grapple:Play()

			tweenTable.tweenHookCFrameLeft.Completed:Connect(function(state)
				if state == Enum.PlaybackState.Completed then
					valuesFolder["LeftHooked"].Value = true
					valuesFolder["LeftEjected"].Value = false
					valuesFolder["LeftPosition"].Value = hook.Position
					createWeld(array[4], hook)
					character["Torso"].Ejector.Gas.Enabled = true					
				end
				tweenTable.tweenHookCFrameLeft = nil
				tweenTable.tweenRopeRadiusLeft = nil
			end)
		end
		character["Torso"].leftgrapple.Eject.Enabled = false
	elseif array[1] == "Retract" then
		if array[2] == "Right" then
			if valuesFolder["RightEjected"].Value then
				tweenTable.tweenHookCFrameRight:Cancel()
				local hook = character["Torso"].rightgrapple["PseudoHook"]
				local ropeTween = TweenService:Create(hook["Rope"], tweenInfo, {Thickness = 0})
				ropeTween:Play()
				character["Torso"].rightgrapple.Reel:Play()

				if character["Torso"].Ejector.Gas.Enabled then
					if valuesFolder["LeftHooked"].Value then 
						return 
					else
						character["Torso"].Ejector.Gas.Enabled = false
					end
				end

				ropeTween.Completed:Connect(function()
					valuesFolder["RightEjected"].Value = false
					valuesFolder["RightPosition"].Value = Vector3.new()
					wait(.1)
					hook:Destroy()
				end)
			elseif valuesFolder["RightHooked"].Value then
				local hook = character["Torso"].rightgrapple["PseudoHook"]
				local ropeTween1 = TweenService:Create(hook["Rope"], tweenInfo, {Thickness = 0})
				local ropeTween2 = TweenService:Create(hook["Rope"], tweenInfo, {Radius = 0.2})
				wait(.1)
				hook["HookWeld"]:Destroy()
				ropeTween1:Play()
				ropeTween2:Play()
				character["Torso"].rightgrapple.Reel:Play()

				if character["Torso"].Ejector.Gas.Enabled then
					if valuesFolder["LeftHooked"].Value then 
						return 
					else
						character["Torso"].Ejector.Gas.Enabled = false
					end
				end

				ropeTween2.Completed:Connect(function()
					valuesFolder["RightHooked"].Value = false
					valuesFolder["RightPosition"].Value = Vector3.new()
					wait(.1)
					hook:Destroy()
				end)
			end
		elseif array[2] == "Left" then
			if valuesFolder["LeftEjected"].Value then
				tweenTable.tweenHookCFrameLeft:Cancel()
				local hook = character["Torso"].leftgrapple["PseudoHook"]
				local ropeTween = TweenService:Create(hook["Rope"], tweenInfo, {Thickness = 0})
				ropeTween:Play()
				character["Torso"].leftgrapple.Reel:Play()

				if character["Torso"].Ejector.Gas.Enabled then
					if valuesFolder["RightHooked"].Value then 
						return 
					else
						character["Torso"].Ejector.Gas.Enabled = false
					end
				end

				ropeTween.Completed:Connect(function()
					valuesFolder["LeftEjected"].Value = false
					valuesFolder["LeftPosition"].Value = Vector3.new()
					wait(.1)
					hook:Destroy()
				end)
			elseif valuesFolder["LeftHooked"].Value then
				local hook = character["Torso"].leftgrapple["PseudoHook"]
				local ropeTween1 = TweenService:Create(hook["Rope"], tweenInfo, {Thickness = 0})
				local ropeTween2 = TweenService:Create(hook["Rope"], tweenInfo, {Radius = 0.2})
				wait(.1)
				hook["HookWeld"]:Destroy()
				ropeTween1:Play()
				ropeTween2:Play()
				character["Torso"].leftgrapple.Reel:Play()

				if character["Torso"].Ejector.Gas.Enabled then
					if valuesFolder["RightHooked"].Value then 
						return 
					else
						character["Torso"].Ejector.Gas.Enabled = false
					end
				end

				ropeTween2.Completed:Connect(function()
					valuesFolder["LeftHooked"].Value = false
					valuesFolder["LeftPosition"].Value = Vector3.new()
					wait(.1)
					hook:Destroy()
				end)
			end
		end
	end
end

--//Events
remotesFolder["Grapple"].OnServerEvent:Connect(onServerEvent)


remotesFolder["Attack"].OnServerEvent:Connect(function(player)
	if Blades then
		local LeftBlade = player.character["Left Arm"]:WaitForChild("Blade")
		local RightBlade = player.character["Right Arm"]:WaitForChild("Blade")

		local leftTrail = LeftBlade.Trail
		local rightTrail = RightBlade.Trail

		leftTrail.Enabled = true
		rightTrail.Enabled = true

		local a = Instance.new("Sound")
		a.Parent = player.Character.Head
		a.SoundId = "rbxassetid://4958430453"
		a.Volume = 1
		a:Play()

		game.Debris:AddItem(a,1)

		LeftBlade.Material = Enum.Material.Neon
		RightBlade.Material = Enum.Material.Neon

		local hitBox = LeftBlade or RightBlade

		hitBox.Touched:connect(function(hit)
			if hit.Parent:findFirstChild("Humanoid") and hit.Parent.Name ~= player.Name then
				if not hit.Parent:findFirstChild("Nape") and not hit.Parent:findFirstChild("Ragdoller") then
					hit.Parent.Humanoid:TakeDamage(25)
				end

				if hit.Name == "Nape" then
					local hitv = Instance.new("ObjectValue")
					hitv.Name = "HitRequest"
					hitv.Value = player
					hit.Parent:findFirstChildOfClass("Humanoid"):TakeDamage(2000)
				end

			
			end
		end)
		
		wait(1)
		leftTrail.Enabled = false
		rightTrail.Enabled = false
		LeftBlade.Material = Enum.Material.Metal
		RightBlade.Material = Enum.Material.Metal
	end
end)

remotesFolder["GasBurst"].OnServerEvent:Connect(function(player, Side)



end)

remotesFolder["Boost"].OnServerEvent:Connect(function(player, Boost)
	if Boost then
		
		local char = player.Character
		local sound = char.Torso.Body.Gas
		
		sound:Play()
	else
		local char = player.Character
		local sound = char.Torso.Body.Gas
		
		sound:Stop()
	end
end)

remotesFolder["Reload"].OnServerEvent:connect(function(player)
	local character = player.Character

	local LeftBlade = player.character["Left Arm"]:WaitForChild("Blade")
	local RightBlade = player.character["Right Arm"]:WaitForChild("Blade")

	valuesFolder.BladeAmount.Value = valuesFolder.BladeAmount.Value - 2

	LeftBlade.Transparency = 0
	RightBlade.Transparency = 0

	local a = Instance.new("Sound")
	a.SoundId = "http://www.roblox.com/asset/?id=130785405"
	a.Volume = 1
	a.Pitch = 2
	a.MaxDistance = 50
	a.Parent = player.Character.HumanoidRootPart
	a:play()
	game.Debris:AddItem(a,0.5)

	Blades = true

end)

remotesFolder["ReturnBlade"].OnServerEvent:connect(function(player)

	local character = player.Character
	
	local LeftBlade = player.character["Left Arm"]:WaitForChild("Blade")
	local RightBlade = player.character["Right Arm"]:WaitForChild("Blade")


	valuesFolder.BladeAmount.Value = valuesFolder.BladeAmount.Value + 2

	LeftBlade.Transparency = 1
	RightBlade.Transparency = 1


	local a = Instance.new("Sound")
	a.SoundId = "http://www.roblox.com/asset/?id=211134014"
	a.Volume = 1
	a.Pitch = 2
	a.MaxDistance = 50
	a.Parent = player.Character.HumanoidRootPart
	a:play()
	game.Debris:AddItem(a,0.5)

	Blades = false
end)
1 Like

In this if statement, if the right hook is reeled while the left is hooked, the function returns. This is an issue because ropetween2.completed:Connect() never runs so you never set valuesFolder["LeftEjected"].Value = false. Because of this, you can never eject left in the future
Gas will also still be enabled, so when you try to reel left afterwards too, ropetween2.completed:Connect() will not run for the left reel.

4 Likes

Alright I removed the “return” statement and it actually works like expected now! Thank you.

1 Like