AI only targets one player (SOLVED)

You should create a loop so it would chase the closest player and if there is someone new that is close to the bot, you can stop the pathfinding and create a new path

(a loop that constantly looks for the closest player)

1 Like

here, this worked for me.

local SearchDistance = math.huge

local ZombieDamage = math.huge


local canWander = false
local WanderX, WanderZ = 30, 30



function getHumanoid(model)
	for _, v in pairs(model:GetChildren()) do
		if v:IsA'Humanoid' then
			return v
		end
	end
end


local zombie = script.Parent
local human = getHumanoid(zombie)
local hroot = zombie.HumanoidRootPart
local zspeed = hroot.Velocity.magnitude


local pfs = game:GetService("PathfindingService")

function GetPlayerNames()
	local players = game:GetService('Players'):GetChildren()
	local name = nil
	for _, v in pairs(players) do
		if v:IsA'Player' then
			name = tostring(v.Name)
		end
	end
	return name
end

function GetPlayersBodyParts(t)
	--local torso = t
	--if torso then
	--	local figure = torso.Parent
	--	for _, v in pairs(figure:GetChildren()) do
	--		if v:IsA'Part' then
	--			return v.Name
	--		end
	--	end
	--else
	--	return "HumanoidRootPart"
	--end
end


function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torsos = {}
	local torso = nil
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				table.insert(torsos, charRoot)
			end
		end
	end
	table.sort(torsos, function (a, b) return a.Position > b.Position end)
	torso = torsos[1]
	return torso
end

for _, zambieparts in pairs(zombie:GetChildren()) do
	if zambieparts:IsA'Part' then
		zambieparts.Touched:connect(function(p)
			if p.Parent.Name == GetPlayerNames() and p.Parent.Name ~= zombie.Name then -- damage
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				enemyhuman:TakeDamage(ZombieDamage)
			end
		end)
	end
end

local path
local waypoint
local oldpoints
local isWandering = 0

if canWander then
	spawn(function()
		while isWandering == 0 do
			isWandering = 1
			local desgx, desgz = hroot.Position.x + math.random(-WanderX, WanderX), hroot.Position.z + math.random(-WanderZ, WanderZ)
			human:MoveTo( Vector3.new(desgx, 0, desgz) )
			wait(math.random(4, 6))
			isWandering = 0
		end
	end)
end

while wait() do
	local enemytorso = GetTorso(hroot.Position)
	if enemytorso ~= nil then
		isWandering = 1
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat
					ci = ci + 1
					wait()
				until t[ci] ~= nil
				return Vector3.new(1, 0, 0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end

		path = pfs:FindPathAsync(hroot.Position, enemytorso.Position)
		waypoint = path:GetWaypoints()
		oldpoints = waypoint
		local connection;

		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)

		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end

			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
				human.Jump = true
				connection = human.Changed:connect(function()
					human.Jump = true
				end)
				human:MoveTo(checkw(waypoint).Position)
			else
				human.Jump = false
			end

			hroot.Touched:connect(function(p)
				local bodypartnames = GetPlayersBodyParts(enemytorso)
				if p:IsA'Part' and not p.Name == bodypartnames and phit and phit.Name ~= bodypartnames and phit:IsA'Part' and rootr:Distance(phit.Position) < 5 then
					connection = human.Changed:connect(function()
						human.Jump = true
					end)
				else
					human.Jump = false
				end
			end)

			if connection then
				connection:Disconnect()
			end

		else
			for i = 3, #oldpoints do
				human:MoveTo( oldpoints[i].Position )	
			end
		end
	elseif enemytorso == nil and canWander then
		isWandering = 0
		path = nil
		waypoint = nil
		--human.MoveToFinished:Wait()
	end
end

now it just does exactly what I said in the original post

1 Like

i forgot to change this

function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torsos = {}
	local torso = nil
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				table.insert(torsos, charRoot)
			end
		end
	end
	table.sort(torsos, function (a, b) return b.Position > a.Position end)
	torso = torsos[1]
	return torso
end
1 Like

one of you get closer to the bot and see if it chases you.

tried, still only targets player that joined last

Oh wow you’re right, thats really weird. Hold on.

Yeah, the problem is definitely in your loop. Lemme see what could be causing this.

image
This is the error shown in the server-side console. Turns out it was trying to compare 2 Vector3 numbers when a player joins, forcing it to just stop working when there is more than one player.

I have found the solution as it works for me.

replace your gettorso function to:

function GetTorso(part)
	local chars = {}
	local torsos = {}
	local torso = nil
	for i,v in pairs(game.Players:GetChildren()) do
		table.insert(chars, v.Character)
	end
	if #chars == 0 then return nil end
	for _, v in pairs(chars) do
		local charRoot = v.PrimaryPart
		if (charRoot.Position - part).magnitude < SearchDistance then
			table.insert(torsos, charRoot)
		end
	end
	table.sort(torsos, function (a, b) return (b.Position - part).magnitude > (a.Position - part).magnitude  
 end) -- now it sorts both magnitudes.
	torso = torsos[1]
	return torso
end
3 Likes

Good news: the pathfinding works properly now
Bad news: damage doesn’t apply to the other player
damage part of the script incase you want to help with that
you really don’t have to help with this I’m just posting in case you do

for _, zambieparts in pairs(zombie:GetChildren()) do
	if zambieparts:IsA'Part' then
		zambieparts.Touched:connect(function(p)
			if p.Parent.Name == GetPlayerNames() and p.Parent.Name ~= zombie.Name then -- damage
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				enemyhuman:TakeDamage(ZombieDamage)
			end
		end)
	end
end
1 Like

Sure! Ill try fixing it right now.

1 Like

im gonna credit you in this game
i cannot thank you enough

1 Like

Greatly appreciated! You’re welcome! :slight_smile:

1 Like
for _, zambieparts in pairs(zombie:GetChildren()) do
	if zambieparts:IsA'Part' then
		zambieparts.Touched:connect(function(p)
			if p.Parent:FindFirstChild('Humanoid') then -- damage
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				enemyhuman:TakeDamage(ZombieDamage)
			end
		end)
	end
end

This works for me.

1 Like

Also make sure to mark this comment i replied to as solution, so people don’t come here and try to help when its already solved.

1 Like

was worried that this was gonna damage other npc’s but it doesn’t look like it damages them

Yea, it does, but NPCs have infinite health. They don’t die like the regular player, because they aren’t coded to die, so their health just goes below 0 and into the infinites.
image

weird, cause when I tested damage with a second dummy it didn’t do any damage to it

Well yeah, it doesn’t show that it did damage because you have to set it to show its health bar when its damaged in the dummy humanoid properties. But it does do damage you can check that by looking at the humanoid properties of the dummy and check the health.