Stopping a target getting hit twice

So im not sure how to explain this very well. In my tower defence type game i have a slow firing, high damage tower, however, if there are 2 towers they will shoot in unison; effectively meaning damage will be wasted if the enemy has a lower health then the damage dealt (which is normal). Please note im not looking for a solution which makes the tower wait an extra random miloseconds to not fire in unison.

while wait() do
	
	nearestTarget = nil

	nearestTarget= module.GetTarget(range, script.Parent.HumanoidRootPart,ghost)

	if script.Parent:FindFirstChild("Stun") then else

		if(nearestTarget ~= nil) then
			spawn(function()
				module.OriginalLookAt(script.Parent,nearestTarget:FindFirstChild("HumanoidRootPart"),script.offset.Value)
				fPos = flare.Position - Vector3.new(0,0.4,0)
				module.Flash(flare,"a",0.1,stat.Color.Value)
				Fire()
				wait()
				module.Animation(fireanim,script.Parent:WaitForChild("Humanoid"),0,1)
			end)
			
			wait(fr)
		end
	end
end

module.GetTarget = function(range,root,ghost)
	
	local nearestTarget
	local v = 0

	for i, target in ipairs(game.Workspace.Mobs:GetChildren()) do

		if(target) then
			
			if target.Name ~= "Arrow" then
				if((target.HumanoidRootPart.Position - root.Position).Magnitude <= range) then
					
					if target:FindFirstChild("Ghost").Value == false or ghost == true then

						if(target.DistanceOnTrack.Value > v) then

							nearestTarget = target

							v = target.DistanceOnTrack.Value	

						end
					end

				end
			end

		end

	end

	return nearestTarget

end
1 Like

You can use a Module Script or custom attributes to store a cooldown in the enemy, which will be read before they take damage to avoid getting hit more than once. Remember to update the cooldown, though!

1 Like

I assume there’s a little bit of time between the section where the tower locks on and does its animation, and the section where the damage is actually dealt.
In that scenario, you would need a way to “mark” what enemies are already being targeted. In other words, if an enemy is already on death’s row, then you want to have towers update a variable corresponding to that enemy. And, when that variable is updated, then it basically removes that enemy from the targeting algorithm of other towers.

To do this, the best way is probably to have a NumberValue that’s parented to the enemies that are in workspace.Mobs. Let’s say it’s called something like ImminentDeath.
The “module.GetTarget” function should then look at the value of target.ImminentDeath. If it has the value of 0 (aka death is NOT imminent), then that enemy will be considered in the targeting process. Otherwise, we don’t care about the enemy.
Then, if we see that the target is going to take fatal damage, then we set nearestTarget.ImminentDeath to 1 so that other towers understand that the enemy is basically already dead and won’t attack it.
(The only caveat of this system is that if you have an enemy that revives, then you have to keep ImminentDeath in mind and update it accordingly. I’m pretty sure any other system that has you try to dissuade other towers from targeting an already-going-to-die enemy will have the same issue, though.)

Another approach is that, instead of making a whole new NumberValue, you can just have a dictionary and put enemies that are going to die like this:

local takingFatalDamage = {}
-- Dictionary of enemies that have been targeted by an attack that kills them
-- [enemy (an Instance)] = true

-- later in the script

-- check if this is true
if takingFatalDamage[target] then

-- update it to this if the enemy's going to die
takingFatalDamage[target] = true

-- When you want to clear all the enemies in the dictionary, you can do this:
takingFatalDamage = {}
-- to not have a table of all the enemies that are already gone. Also, remove elements like this:
takingFatalDamage[target] = nil -- or false but i like nil 

Having to clear the table manually makes me prefer the NumberValue more, but to each their own.

Also this is unrelated but you should use task.wait([stuff]) instead of wait([stuff]), and coroutine.wrap(function() [stuff] end)() instead of spawn(function() [stuff] end). Basically, those versions you use are prone to small delays, which have the potential to become really big delays if the game lags. There’s a little more to it than that but other forum posts go into more detail about it.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.