I want my npcs to be able to attack players asynchronously. To solve this without dealing with closing coroutines when the function finishes, I call the attack functions with task.spawn. Problem is, the function runs once then the script errors out.
Error in text form:
02:09:49.593 ServerScriptService.AI.Main.Zombie:91: invalid argument #1 to 'spawn' (function or thread expected) - Server - Zombie:91
02:09:49.593 Stack Begin - Studio
02:09:49.593 Script 'ServerScriptService.AI.Main.Zombie', Line 91 - function attack - Studio - Zombie:91
02:09:49.593 Script 'ServerScriptService.AI.Main', Line 180 - function tick - Studio - Main:180
02:09:49.593 Script 'ServerScriptService.EntityHandler', Line 24 - Studio - EntityHandler:24
02:09:49.593 Stack End - Studio
The ModuleScript:
local moveset = {}
moveset.__index = moveset
function moveset.new(entity)
local self = {
debounces = {
["Attack"] = false,
["Punch"] = false,
["Bite"] = false,
["Lunge"] = false,
},
cooldowns = {
["Attack"] = .3,
["Punch"] = .1,
["Bite"] = 1,
["Lunge"] = 2,
},
attacks = {},
loadedAnimations = false,
entity = entity,
animationTracks = {},
}
setmetatable(self, moveset)
self:loadAnimations()
return self
end
function moveset:punch()
if self.debounces.Punch == false then
self.debounces.Punch = true
self.animationTracks.Punch:Play()
self.animationTracks.Punch.Ended:Wait()
self.debounces.Punch = false
if (self.entity.humanoidRootPart.Position - self.entity.targets[1].HumanoidRootPart.Position).Magnitude <= 3 then
end
task.wait(self.cooldowns.Attack)
self.debounces.Attack = false
end
end
function moveset:bite()
if self.debounces.Bite == false then
self.debounces.Bite = true
self.animationTracks.Bite:Play()
self.animationTracks.Bite.Ended:Wait()
self.debounces.Bite = false
if (self.entity.humanoidRootPart.Position - self.entity.targets[1].HumanoidRootPart.Position).Magnitude <= 3 then
self.entity.humanoid.Health += 10
end
task.wait(self.cooldowns.Attack)
self.debounces.Attack = false
end
end
function moveset:lunge()
if self.debounces.Lunge == false then
self.debounces.Lunge = true
self.animationTracks.Lunge:Play()
self.animationTracks.Lunge.Ended:Wait()
self.debounces.Lunge = false
if (self.entity.humanoidRootPart.Position - self.entity.targets[1].HumanoidRootPart.Position).Magnitude <= 3 then
end
task.wait(self.cooldowns.Attack)
self.debounces.Attack = false
end
end
function moveset:attack()
--print(self.debounces)
if self.debounces.Attack == false then
--print("attack "..self.entity.targets[1].Name)
if self.entity.humanoidRootPart and self.entity.humanoid.Health > 0 and self.entity.targets[1] ~= nil then
local targetDistance = (self.entity.humanoidRootPart.Position - self.entity.targets[1].HumanoidRootPart.Position).Magnitude
if targetDistance <= 3 then
self.debounces.Attack = true
task.spawn(self:bite())
elseif targetDistance <= 7 then
self.debounces.Attack = true
task.spawn(self:punch())
elseif targetDistance <= 16 then
self.debounces.Attack = true
task.spawn(self:lunge())
end
end
end
end
function moveset:loadAnimations()
if self.loadedAnimations then
return
end
self.loadedAnimations = true
local animationsFolder = self.entity.character.Animations
local animator = self.entity.humanoid.Animator
local animationTracks = {}
for _, animation in animationsFolder:GetChildren() do
local animationTrack = animator:LoadAnimation(animation)
animationTracks[animation.Name] = animationTrack
end
self.animationTracks = animationTracks
end
return moveset