I have created a melee system based on one ModuleScript placed in ReplicatedStorage.
The problem is requiring the ModuleScript in multiple scripts for tools, they all act as one.
It’s like this:
Player A joins and swings knife
Player B joins and suddenly player A’s knife stops working
Player B equips knife, player A swings, but it causes player B to swing, and player B swings player B’s knife.
It seems to follow the newest created tool.
I’ve tried:
- adding
module.__index = module
→ no result - switching to OOP with
.new
and:function
’s → calling self:function() doesn’t work, calling module:function() also doesn’t
I would like to keep the code in a ModuleScript so it’s easier to update tools.
Script that’s requiring the ModuleScript
local config = {
["Blade"] = script.Parent.Handle,
["Tool"] = script.Parent,
["Range"] = 3,
["Damage"] = {5, 15},
["Animations"] = {
["Swing"] = 14438327862,
["Idle"] = 0
},
["Debounce"] = 1,
["Pollingrate"] = 0.01
}
local meleemod = require(game.ReplicatedStorage.ObjectScripts.MeleeSystem)
meleemod.loadConfig(config)
meleemod.start()
ModuleScript
local module = {}
local config = {
["Blade"] = nil,
["Tool"] = nil,
["Range"] = 1,
["Damage"] = {15, 25},
["Animations"] = {
["Swing"] = 0,
["Idle"] = 0
},
["Debounce"] = 1,
["Pollingrate"] = 0.005
}
local db = false
local hits = {}
-- Raycast off front of blade
function module.raycastFromBlade()
local origin = config.Blade.CFrame.Position
local direction = ((config.Blade.CFrame.LookVector)*config.Range)
local result = game.Workspace:Raycast(origin, direction)
return result
end
function module.damage(target)
target.Parent.Humanoid:TakeDamage(math.random(config.Damage[1], config.Damage[2]))
end
function module.isDamagable(target)
if target.Parent:FindFirstChild("Humanoid") then
return true
end
return false
end
function module.loadConfig(cfg)
config = cfg
end
function playAnimation(id, priority)
if id == nil or id == 0 then
return
end
local hum = config.Tool.Parent:FindFirstChild("Humanoid")
if hum then
local animator = hum:WaitForChild("Animator")
if animator then
local anim = Instance.new("Animation")
anim.AnimationId = "rbxassetid://"..tostring(id)
local track = animator:LoadAnimation(anim)
track.Priority = priority
track:Play()
return track
end
end
end
function module.start()
local idleTrack
config.Tool.Equipped:Connect(function()
idleTrack = playAnimation(config.Animations.Idle, Enum.AnimationPriority.Action)
for i, v in config.Tool:GetChildren() do
if v:IsA("Part") or v:IsA("UnionOperation") or v:IsA("MeshPart") then
v.CanCollide = false
end
end
end)
config.Tool.Unequipped:Connect(function()
if idleTrack then
idleTrack:Stop()
end
for i, v in config.Tool:GetChildren() do
if v:IsA("Part") or v:IsA("UnionOperation") or v:IsA("MeshPart") then
v.CanColli1de = true
end
end
end)
config.Tool.Activated:Connect(function()
if not db then
db = true
pcall(function()
playAnimation(config.Animations.Swing, Enum.AnimationPriority.Action2)
end)
local s = config.Debounce / config.Pollingrate
local c = 0
while c < s do
task.wait(config.Pollingrate)
c += 1
local result = module.raycastFromBlade()
if result ~= nil then
if table.find(hits, result.Instance.Parent, 1) then
continue
else
table.insert(hits, result.Instance.Parent)
if module.isDamagable(result.Instance) then
module.damage(result.Instance)
end
end
end
end
db = false
hits = {}
end
end)
end
return module
OOP attempt
local module = {}
module.__index = module
function module.new(o)
o = o or {
["Config"] = {
["Blade"] = nil,
["Tool"] = nil,
["Range"] = 1,
["Damage"] = {15, 25},
["Animations"] = {
["Swing"] = 0,
["Idle"] = 0
},
["Debounce"] = 1,
["Pollingrate"] = 0.005
},
["Debounce"] = false,
["Hits"] = {}
}
setmetatable(o, module)
return o
end
function module:loadConfig(cfg)
self.Config = cfg
end
function module:raycastOffBlade()
local origin = self.Config.Blade.CFrame.Position
local direction = ((self.Config.Blade.CFrame.LookVector)*self.Config.Range)
local result = game.Workspace:Raycast(origin, direction)
return result
end
function module:damage(target)
target.Parent.Humanoid:TakeDamage(math.random(self.Config.Damage[1], self.Config.Damage[2]))
end
function module:isDamagable(target)
if target.Parent:FindFirstChild("Humanoid") then
return true
end
return false
end
function playAnimation(config, id, priority)
if id == nil or id == 0 then
return
end
local hum = config.Tool.Parent:FindFirstChild("Humanoid")
if hum then
local animator = hum:WaitForChild("Animator")
if animator then
local anim = Instance.new("Animation")
anim.AnimationId = "rbxassetid://"..tostring(id)
local track = animator:LoadAnimation(anim)
track.Priority = priority
track:Play()
return track
end
end
end
function module:start()
local idleTrack
self.Config.Tool.Equipped:Connect(function()
idleTrack = playAnimation(self.Config, self.Config.Animations.Idle, Enum.AnimationPriority.Action)
for i, v in self.Config.Tool:GetChildren() do
--[[if v:IsA("Part") or v:IsA("UnionOperation") or v:IsA("MeshPart") then
v.CanCollide = false
end]]
end
end)
self.Config.Tool.Unequipped:Connect(function()
if idleTrack then
idleTrack:Stop()
end
for i, v in self.Config.Tool:GetChildren() do
--[[if v:IsA("Part") or v:IsA("UnionOperation") or v:IsA("MeshPart") then
v.CanColli1de = true
end]]
end
end)
self.Config.Tool.Activated:Connect(function()
if not self.Debounce then
self.Debounce = true
pcall(function()
playAnimation(self.Config, self.Config.Animations.Swing, Enum.AnimationPriority.Action2)
end)
local s = self.Config.Debounce / self.Config.Pollingrate
local c = 0
while c < s do
task.wait(self.Config.Pollingrate)
c += 1
local result = self:raycastFromBlade()
if result ~= nil then
if table.find(self.Hits, result.Instance.Parent, 1) then
continue
else
table.insert(self.Hits, result.Instance.Parent)
if self:isDamagable(result.Instance) then
self:damage(result.Instance)
end
end
end
end
self.Debounce = false
self.Hits = {}
end
end)
end
return module
I don’t know if my OOP is wrong but help is appreciated.