I am making a grappling tool. Everything works fine but I want to make it move towards the target gradually instead of it teleporting to make it look smooth. But whenever I try adding either a second part thats the visual rope or a align position, this weird bug happens where the client physics basically freeze. The character gets deleted, and the lighting changes.
When I tween just the one slinger part the player doesnt move on the server.
Grapple Module:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local TweenService = game:GetService("TweenService")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local matchRotation = true
local currentTilt = 0
local currentRotation = 0
local oldMouseHit = Vector3.zero
local module = {}
local grappleTemplate = ReplicatedStorage:WaitForChild("GrapplePart")
local placeCount = 0
local grappleTween = nil
local grapple = nil
local function roundToGrid(n, gridSize)
gridSize = gridSize or 1
return math.floor(n / gridSize + 0.5) * gridSize
end
local function getPlacementCFrame()
local origin = camera.CFrame.Position
local direction = (mouse.Hit.Position - origin).Unit * 1000
oldMouseHit = mouse.Hit.Position
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {player.Character}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local result = workspace:Raycast(origin, direction, rayParams)
if not result then return nil end
local hitPos = result.Position
local normal = result.Normal
local target = result.Instance
if CollectionService:HasTag(target.Parent, "IgnorePlacement") then
return nil
end
local size = grappleTemplate.Size
local up = normal.Unit
local targetLook = target.CFrame.LookVector
local targetRight = target.CFrame.RightVector
local forward = (targetLook - up * targetLook:Dot(up))
if forward.Magnitude < 0.001 then
forward = (targetRight - up * targetRight:Dot(up))
end
forward = forward.Unit
local right = forward:Cross(up).Unit
local baseCFrame = CFrame.fromMatrix(Vector3.zero, right, up)
local rotationCFrame = CFrame.Angles(currentTilt, currentRotation, 0)
local partOrientationCFrame = matchRotation and (baseCFrame * rotationCFrame) or rotationCFrame
local function getOffset(size, normal, cframe)
local halfSize = size / 2
local axisX = cframe.RightVector * halfSize.X
local axisY = cframe.UpVector * halfSize.Y
local axisZ = cframe.LookVector * halfSize.Z
return math.abs(normal:Dot(axisX)) + math.abs(normal:Dot(axisY)) + math.abs(normal:Dot(axisZ))
end
local offset = getOffset(size, normal, partOrientationCFrame)
local exactPos = hitPos + normal * offset
local targetCFrame = target.CFrame
local localPos = targetCFrame:PointToObjectSpace(exactPos)
local localNormal = targetCFrame:VectorToObjectSpace(normal)
local snapX = math.abs(localNormal.X) < 0.9 and roundToGrid(localPos.X) or localPos.X
local snapY = math.abs(localNormal.Y) < 0.9 and roundToGrid(localPos.Y) or localPos.Y
local snapZ = math.abs(localNormal.Z) < 0.9 and roundToGrid(localPos.Z) or localPos.Z
local snappedLocalPos = Vector3.new(snapX, snapY, snapZ)
local snappedWorldPos = targetCFrame:PointToWorldSpace(snappedLocalPos)
local finalBaseCFrame = CFrame.fromMatrix(snappedWorldPos, right, up)
return finalBaseCFrame * rotationCFrame
end
function DeleteSlinger()
if grapple then
grapple:Destroy()
grapple = nil
end
if grappleTween then
grappleTween:Cancel()
grappleTween = nil
end
end
function module:RegisterToolInput(tool, grappleAttachment)
assert(tool and tool:IsA("Tool"), "Expected a Tool instance")
local char = player.Character or player.CharacterAdded:Wait()
local root = char:WaitForChild("HumanoidRootPart")
local rootAttach = root:WaitForChild("RootAttachment")
local handle = tool:WaitForChild("Handle")
local function playSound(sound)
if not sound then return end
local soundClone = sound:Clone()
soundClone.Parent = handle
soundClone:Play()
soundClone.Ended:Connect(function()
soundClone:Destroy()
end)
end
local equipSound = handle:FindFirstChild("Connect")
local fireSound = handle:FindFirstChild("Fire")
local hitSound = handle:FindFirstChild("Hit")
if not equipSound or not fireSound or not hitSound then return end
player.CharacterAdded:Connect(function(character)
DeleteSlinger()
end)
tool.Unequipped:Connect(function()
DeleteSlinger()
end)
tool.Equipped:Connect(function()
playSound(equipSound)
end)
tool.Activated:Connect(function()
local placementCFrame = getPlacementCFrame()
if not placementCFrame then return end
DeleteSlinger()
grapple = grappleTemplate:Clone()
grapple.Name = "Slinger"
grapple.CFrame = grappleAttachment.WorldCFrame
grapple.AlignPosition.Position = getPlacementCFrame().Position
grapple.Parent = workspace
if grappleAttachment then
grapple.RopeConstraint.Attachment0 = grappleAttachment
else
grapple.RopeConstraint.Attachment0 = rootAttach
end
placeCount += 1
local currentCount = placeCount
if grappleTween then
grappleTween:Cancel()
grappleTween = nil
end
playSound(fireSound)
end)
end
return module
I never came across this issue before and it seems really weird why this would even happen.