Hey everyone!
I’ve been working on an NPC attacking system similar to those in simulator games. You will automatically walk toward them and start attacking them by clicking your mouse on any NPC. I’ve found a bug that lets the player multiply their damage by interrupting the MoveTo() and clicking the NPC again. For example, if the player walks out of the MoveTo() and clicks the NPC and repeats that 3 times, they will do 10 x 3 damage which is 30.
Here is a clip of the bug happening:
The script that calls the function:
local Attacking = Instance.new("ObjectValue")
Attacking.Name = "Attacking"
Attacking.Parent = Player.Character
local Mouse = Player:GetMouse()
UserInputService.InputBegan:Connect(function(Input, GameProcessed)
if GameProcessed then return end
if Input.UserInputType == Enum.UserInputType.MouseButton1 then
if Mouse.Target and Mouse.Target.Parent.Parent == workspace.NPCs and Attacking.Value ~= Mouse.Target.Parent then
AttackNPC(Mouse.Target.Parent, Player)
end
end
end)
The function:
function AttackNPC(NPC, Player)
local Character = Player.Character
local Humanoid = Character.Humanoid
Humanoid:MoveTo(NPC.PrimaryPart.Position + NPC.PrimaryPart.CFrame.LookVector * 3)
Character.Attacking.Value = NPC
local Connection
Connection = Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
Character.Attacking.Value = nil
Connection:Disconnect()
return
end)
Humanoid.MoveToFinished:Wait()
while Character.Attacking.Value == NPC and NPC.Humanoid.Health > 0 do
local AttackAnimations = {
"rbxassetid://9972570264",
"rbxassetid://9972635563",
}
local RandomAnimation = AttackAnimations[math.random(1, table.getn(AttackAnimations))]
local Animation = Instance.new("Animation")
Animation.AnimationId = RandomAnimation
local Loaded = Humanoid.Animator:LoadAnimation(Animation)
Loaded.Priority = Enum.AnimationPriority.Action
Loaded:Play()
task.wait(.3)
NPC.Humanoid:TakeDamage(10)
task.wait(.3)
end
end
If anybody could help, that would be greatly appreciated. Thanks!
I’d start by checking whether or not a user is already attacking an NPC before letting AttackNPC run, whenever AttackNPC is called, set a boolean to true and don’t set it to false until the connection disconnects.
As an example:
local attackingNPC = false
function AttackNPC(NPC, Player)
if not attackingNPC then
attackingNPC = true
connection = Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
attackingNPC = false
end)
end
end
Hm. Wouldn’t that not allow the player to attack another NPC while they are already attacking one? What my system should do is let the player attack another NPC while they are already attacking one and cancel their attack if they want.
I’ve went ahead and written up an un-tested example so you can hopefully understand what I mean.
local connections = {}
local currentNPC = nil
local isAttacking = false
local function cleanupConnections()
for _, connection in ipairs (connections) do
if connection.Connected then
connection:Disconnect()
end
end
end
local function attackNPC(npc, player)
--If they're not attacking or the NPC they want to attack is different than the one they're attacking
if not isAttacking or npc ~= currentNPC then
--Set attacking to true so we don't run this more than once
isAttacking = true
--Cleanup our old connections in case there's some lingering
cleanupConnections()
local character = player.Character
local humanoid = character.Humanoid
humanoid:MoveTo(npc.PrimaryPart.Position + npc.PrimaryPart.CFrame.LookVector * 3)
character.Attacking.Value = npc
--Insert our move direction connection into our connection table
table.insert(connections, humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
character.Attacking.Value = nil
isAttacking = false
--Cleanup the connections in case the user exits this way
cleanupConnections()
return
end))
humanoid.MoveToFinished:Wait()
while character.Attacking.Value == npc and npc.Humanoid.Health > 0 do
local AttackAnimations = {
"rbxassetid://9972570264",
"rbxassetid://9972635563",
}
local RandomAnimation = AttackAnimations[math.random(1, table.getn(AttackAnimations))]
local Animation = Instance.new("Animation")
Animation.AnimationId = RandomAnimation
local Loaded = humanoid.Animator:LoadAnimation(Animation)
Loaded.Priority = Enum.AnimationPriority.Action
Loaded:Play()
task.wait(.3)
npc.Humanoid:TakeDamage(10)
task.wait(.3)
end
end
end
local function onPress(inputObject, gameProcessed)
if inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
local npc = mouse.Target.Parent
if npc then
attackNPC(npc, player)
end
end
end