Hey everyone, I have a tool called batons in my game which are supposed to be used to simply knock players back and players are supposed to fight with it to push each other in the water it was made with a module script due to how many batons there are in the game (Its a cosemetic)
In studio it works great and players get pushed back a few studs but in game it works horribly and as soon as a player is hit they are just sent flying ive tried many methods to fix it but nothing works and if it does it just breaks the batons
This is the module
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Baton = {}
Baton.__index = Baton
local function GetAnimation(id: number): Animation
local Animation = Instance.new("Animation")
Animation.AnimationId = "rbxassetid://" .. id
return Animation
end
function Baton.New(name: string, tool: Tool, idleID: number, force: number, swing1ID: number, swing2ID: number): {any}
local self = setmetatable({}, Baton)
self.Name = name
self.Cooldown = 1
self.Tool = tool
self.Equipped = false
self.Wielder = nil
self.Swinging = false
self.Force = force
self.Idle = GetAnimation(idleID)
self.Swing1 = GetAnimation(swing1ID)
self.Swing2 = GetAnimation(swing2ID)
self.Run = GetAnimation(113913581766221)
self._Connections = {}
self._Animations = {}
return self
end
function Baton:HitPlayer(targetCharacter: Model): nil
if not self.Wielder then return end
local targetHRP = targetCharacter:FindFirstChild("HumanoidRootPart")
if not targetHRP then return end
local attackerHRP = self.Tool.Parent:FindFirstChild("HumanoidRootPart")
if not attackerHRP then return end
-- Clean up old attachment if it exists
local oldAttach = targetHRP:FindFirstChild("KnockbackAttachment")
if oldAttach then
local oldLV = oldAttach:FindFirstChildOfClass("LinearVelocity")
if oldLV then oldLV:Destroy() end
oldAttach:Destroy()
end
-- Compute strictly horizontal direction
local direction = (targetHRP.Position - attackerHRP.Position)
direction = Vector3.new(direction.X, 0, direction.Z)
if direction.Magnitude < 0.1 then return end
direction = direction.Unit
-- Attach to HRP center
local attach = Instance.new("Attachment")
attach.Name = "KnockbackAttachment"
attach.Position = Vector3.zero -- ensure centered
attach.Parent = targetHRP
local linearVelocity = Instance.new("LinearVelocity")
linearVelocity.Attachment0 = attach
linearVelocity.RelativeTo = Enum.ActuatorRelativeTo.World
linearVelocity.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
linearVelocity.VectorVelocity = direction * math.clamp(self.Force, 0, 20000)
linearVelocity.MaxForce = math.huge
linearVelocity.Parent = targetHRP
task.delay(0.2, function()
if linearVelocity then linearVelocity:Destroy() end
if attach then attach:Destroy() end
end)
end
function Baton:StartDetection(): nil
local debounce = {}
local function BatonTouched(part)
local character = part:FindFirstAncestorWhichIsA("Model")
if not character or not self.Swinging then return end
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if not humanoid then return end
for _, check in pairs(debounce) do
if check == character then return end
end
table.insert(debounce, character)
self:HitPlayer(character)
task.wait(1)
table.remove(debounce, table.find(debounce, character))
end
for index, object in pairs(self.Tool:GetDescendants()) do
if object:IsA("BasePart") then
self._Connections[index .. "touchevent"] = object.Touched:Connect(BatonTouched)
end
end
end
function Baton:Setup(): nil
local function EquipTool()
self.Equipped = true
local character = self.Tool.Parent
local humanoid = character:FindFirstChild("Humanoid")
if not humanoid then return end
local player = players:GetPlayerFromCharacter(character)
self.Wielder = player
self._Animations.Idle = humanoid:LoadAnimation(self.Idle)
self._Animations.Swing1 = humanoid:LoadAnimation(self.Swing1)
self._Animations.Swing2 = humanoid:LoadAnimation(self.Swing2)
self._Animations.Run = humanoid:LoadAnimation(self.Run)
local idleAlr = false
local runAlr = false
self._Animations.Idle:Play()
task.spawn(function()
RunService.Heartbeat:Connect(function()
if self.Equipped then
if humanoid.MoveDirection.Magnitude > 0 then
if not runAlr then
runAlr = true
idleAlr = false
self._Animations.Idle:Stop()
self._Animations.Run:Play()
end
else
if not idleAlr then
idleAlr = true
runAlr = false
self._Animations.Idle:Play()
self._Animations.Run:Stop()
end
end
else
self._Animations.Idle:Stop()
self._Animations.Run:Stop()
end
end)
end)
local activationSequence = 0
local cooldown = false
local function Swing()
if not humanoid or cooldown then return end
cooldown = true
self.Swinging = true
activationSequence += 1
if activationSequence >= 2 then
activationSequence = 0
self._Animations.Swing2:Play()
else
self._Animations.Swing1:Play()
end
task.wait(self.Cooldown)
self.Swinging = false
cooldown = false
end
self._Connections["Swing"] = self.Tool.Activated:Connect(Swing)
end
self._Connections["Equip"] = self.Tool.Equipped:Connect(EquipTool)
local function DequipTool()
if not self.Equipped then return end
self.Equipped = false
self.Wielder = nil
if self._Animations.Idle then self._Animations.Idle:Stop() end
if self._Connections["Swing"] then self._Connections["Swing"]:Disconnect() end
end
self._Connections["Dequip"] = self.Tool.Unequipped:Connect(DequipTool)
local function RemoveConnections()
for _, connection in pairs(self._Connections) do
connection:Disconnect()
end
for _, anim in pairs(self._Animations) do
anim:Stop()
end
table.clear(self._Connections)
table.clear(self._Animations)
end
self.Tool.Destroying:Connect(RemoveConnections)
end
function Baton:ReplicateSword(char)
local GetPlayer = players:GetPlayerFromCharacter(char)
if not GetPlayer or not GetPlayer:FindFirstChild("BatonSelected") then return end
local CurrentBaton = GetPlayer.BatonSelected
local Sword = ReplicatedStorage.BatonModels:FindFirstChild(CurrentBaton.Value .. " Replica")
if Sword then
local SwordReplica = Sword:Clone()
local Handle = SwordReplica.Handler
local RootPart = char:FindFirstChild("Torso")
if RootPart then
local weld = Instance.new("Weld")
weld.Part0 = RootPart
weld.Part1 = Handle
weld.C1 = CFrame.new(0, .45, -.3) * CFrame.Angles(math.rad(90), 0, math.rad(-40))
weld.Parent = Handle
SwordReplica.Parent = char
else
warn("HumanoidRootPart not found in character.")
end
else
warn("Sword model not found in BatonModels.")
end
end
function Baton:RemoveSword(char: Model, baton)
for _, v: Model in pairs(char:GetChildren()) do
if v.Name == baton.Name .. " Replica" then
v:Destroy()
end
end
end
return Baton
and this is the script that goes in the actual tool
local replicatedStorage = game:GetService("ReplicatedStorage")
local classes = replicatedStorage:WaitForChild("Classes")
local tool = script.Parent
local Baton = require(classes.Baton)
local NewBaton = Baton.New(tool.Name, tool, 134226851422062, 30, 130210959577138, 81841947512068)
NewBaton:Setup()
NewBaton:StartDetection()
tool.Equipped:Connect(function()
Baton:RemoveSword(tool.Parent, tool)
end)
tool.Unequipped:Connect(function()
Baton:ReplicateSword(tool.Parent.Parent.Character, tool)
end)
Any help would be greatly appreciated launch is in a few hours