local F={Vector3.new(5,0,0);Vector3.new(-5,0,0),Vector3.new(0,0,5),Vector3.new(0,0,-5)}local c={Vector3.new(5,0,0),Vector3.new(-5,0,0),Vector3.new(0,0,5);Vector3.new(0,0,-5);Vector3.new(5,0,5);Vector3.new(-5,0,5);Vector3.new(5,0,-5),Vector3.new(-5,0,-5)}return function(b)local D=(require(game.ReplicatedStorage.PathfindFunctions))(b)local W=(require(game.ReplicatedStorage.EnemyAi.Movement))(b)local e={}local V={}local J={}e.CheckSquare=function(F)if table.find(b,Vector3.new(F.X,0,F.Z))then return true end return false end e.GetDangerSpots=function(b)local D={}for c,b in pairs(b)do if b.IsAttacking then for F,c in pairs(F)do table.insert(D,(b.Position-Vector3.new(0,b.Position.Y))+c)end end end for F,c in pairs(workspace.ActiveProjectiles:GetChildren())do local b=c.PrimaryPart.Position+c.MoveDirection.Value local W=Vector3.new(math.floor(b.X/5)*5,0,math.floor(b.Z/5)*5)table.insert(D,W)end for F,b in pairs(workspace.ActiveTraps:GetChildren())do table.insert(D,b.PrimaryPart.Position)if b.PrimaryPart.Size.X>=12 then for F,c in pairs(c)do table.insert(D,b.PrimaryPart.Position+c)end end end return D end e.GetAttackersOfSpot=function(F)local c={}for b,D in pairs(workspace.ActiveEnemies:GetChildren())do if(D.PrimaryPart.Position-F).Magnitude<=7 then table.insert(c,D)end end return c end e.GetEnemies=function()local F={}for c,b in pairs(workspace.ActiveEnemies:GetChildren())do local D={}D.Position=b.PrimaryPart.Position D.IsAttacking=b.PrimaryPart.BrickColor==BrickColor.new("\082\101\097\108\108\121 \114\101\100")table.insert(F,D)end return F end e.GetEnemyAtPosition=function(F,c)for c,b in pairs(c)do if Vector3.new(b.Position.X,0,b.Position.Z)==Vector3.new(F.X,0,F.Z)then return b end end return false end e.CheckIfDangerSpot=function(F,c)for c,b in pairs(c)do if Vector3.new(b.X,0,b.Z)==Vector3.new(F.X,0,F.Z)then return b end end return false end e.GetBestCostingSpot=function(F)local c=-math.huge local b={}for F,D in pairs(F)do if D.Cost>c then c=D.Cost b={}table.insert(b,D)elseif D.Cost==c then table.insert(b,D)end end return b[math.random(1,#b)]end e.PositionReachedAmount=function(F)local c=0 for b,D in pairs(V)do if Vector3.new(D.X,0,D.Z)==F-Vector3.new(F.Y)then c+=1 end end return c end e.UpdateEnemies=function(F)local c={}for F,c in pairs(F)do if c.IsAttacking then local F=W.GetNearestPlayerDirection(c.Position,false,false,nil,true)local b={}b.Position=c.Position+F b.IsAttacking=true end end return c end e.GetOutsideSquares=function()local c={}for b,D in pairs(b)do for F,b in pairs(F)do if not e.CheckSquare(D+b)then table.insert(c,D)break end end end J=c end e.IsOutsideSquare=function(F,c)if table.find(J,Vector3.new(F.X,0,F.Z))then return true else return false end end e.GetMoveInfo=function(c,b,W,J)local v={}local R=W local z=e.GetDangerSpots(c)local u=workspace.Loot:GetChildren()local l=nil local q=nil local E=math.huge if#u~=0 then for F,c in pairs(workspace.Loot:GetChildren())do local b=(c.PrimaryPart.Position-R).Magnitude if b<E then l=c E=b end end q=l.PrimaryPart.Position end local U for F,W in pairs(F)do z=e.GetDangerSpots(c)local V=R local u=V+W U=(not e.CheckSquare(u)or e.GetEnemyAtPosition(u,c))and V or u local E if J then E=J table.remove(E,1)end local f=(not J and q)and D.GetDirections(R,q)local A=e.CheckIfDangerSpot(U,z)local t=e.GetEnemyAtPosition(u,c)local y=e.PositionReachedAmount(U)local k=l and(l.PrimaryPart.Position-U).Magnitude<3 local O=e.IsOutsideSquare(U)local X=f and f[1]==W local m=e.GetAttackersOfSpot(R)local L=#m==1 and(m[1].Health.Value==1 and m[1].PrimaryPart.Position==u+Vector3.new(0,-u.Y+m[1].PrimaryPart.Position.Y))local H=(A and not L)and-10 or 0 H+=y*-0.1 H+=O and-1 or 0 H+=(t and not A)and 2 or 0 H+=L and 2 or 0 H+=X and 4 or 0 H+=k and 4 or 0 if b>0 then H+=(e.GetMoveInfo(e.UpdateEnemies(c),b-1,R+W,f)).Cost end local N={}N.Direction=W N.Cost=H table.insert(v,N)end local f=e.GetBestCostingSpot(v)table.insert(V,R)return f end e.MovePlayer=function()local F=game.Players.LocalPlayer.Character.PrimaryPart.Position F=Vector3.new(math.floor(F.X+.5),0,math.floor(F.Z+.5))e.GetOutsideSquares()local c=e.GetMoveInfo(e.GetEnemies(),0,F)game.ReplicatedStorage.Events.Move:FireServer(c.Direction.Unit)end return e end
--to do
--go towards boxes
--account for arrows and paladins
--account for boss size
local MoveDirections = { Vector3.new(5, 0, 0), Vector3.new(-5, 0, 0), Vector3.new(0, 0, 5), Vector3.new(0, 0, -5) }
local GasDirections = {
	Vector3.new(5, 0, 0),
	Vector3.new(-5, 0, 0),
	Vector3.new(0, 0, 5),
	Vector3.new(0, 0, -5),
	Vector3.new(5, 0, 5),
	Vector3.new(-5, 0, 5),
	Vector3.new(5, 0, -5),
	Vector3.new(-5, 0, -5),
}

return function(Map)
	local Pathfinding = require(game.ReplicatedStorage.PathfindFunctions)(Map)
	local Movement = require(game.ReplicatedStorage.EnemyAi.Movement)(Map)
	local Self = {}
	local ExploredPositions = {}
	local OutsidePositions = {}

	Self.CheckSquare = function(Position)
		if table.find(Map, Vector3.new(Position.X, 0, Position.Z)) then
			return true
		end
		return false
	end

	Self.GetDangerSpots = function(Enemies)
		local Spots = {}
		for i, Enemy in pairs(Enemies) do
			if Enemy.IsAttacking then
				for i, v in pairs(MoveDirections) do
					table.insert(Spots, Enemy.Position - Vector3.new(0, Enemy.Position.Y) + v)
				end
			end
		end
		for i, Projectile in pairs(workspace.ActiveProjectiles:GetChildren()) do
			local NewPos = Projectile.PrimaryPart.Position + Projectile.MoveDirection.Value
			local FlooredSpot = Vector3.new(math.floor(NewPos.X / 5) * 5, 0, math.floor(NewPos.Z / 5) * 5)
			table.insert(Spots, FlooredSpot)
		end
		for i, Trap in pairs(workspace.ActiveTraps:GetChildren()) do
			table.insert(Spots, Trap.PrimaryPart.Position)
			if Trap.PrimaryPart.Size.X >= 12 then
				for i, v in pairs(GasDirections) do
					table.insert(Spots, Trap.PrimaryPart.Position + v)
				end
			end
		end
		return Spots
	end

	Self.GetAttackersOfSpot = function(Position)
		local Attackers = {}
		for i, Model in pairs(workspace.ActiveEnemies:GetChildren()) do
			if (Model.PrimaryPart.Position - Position).Magnitude <= 7 then
				table.insert(Attackers, Model)
			end
		end
		return Attackers
	end

	Self.GetEnemies = function()
		local EnemyTable = {}
		for i, v in pairs(workspace.ActiveEnemies:GetChildren()) do
			local SpotData = {}
			SpotData.Position = v.PrimaryPart.Position
			SpotData.IsAttacking = v.PrimaryPart.BrickColor == BrickColor.new("Really red")
			table.insert(EnemyTable, SpotData)
		end
		return EnemyTable
	end

	Self.GetEnemyAtPosition = function(Position, EnemyTable)
		for i, v in pairs(EnemyTable) do
			if Vector3.new(v.Position.X, 0, v.Position.Z) == Vector3.new(Position.X, 0, Position.Z) then
				return v
			end
		end
		return false
	end

	Self.CheckIfDangerSpot = function(Position, DangerSpots)
		for i, v in pairs(DangerSpots) do
			if Vector3.new(v.X, 0, v.Z) == Vector3.new(Position.X, 0, Position.Z) then
				return v
			end
		end
		return false
	end

	Self.GetBestCostingSpot = function(Spots)
		local BestCost = -math.huge
		local BestSpots = {}
		for i, v in pairs(Spots) do
			if v.Cost > BestCost then
				BestCost = v.Cost
				BestSpots = {}
				table.insert(BestSpots, v)
			elseif v.Cost == BestCost then
				table.insert(BestSpots, v)
			end
		end
		return BestSpots[math.random(1, #BestSpots)]
	end

	Self.PositionReachedAmount = function(Position)
		local Amount = 0
		for i, v in pairs(ExploredPositions) do
			if Vector3.new(v.X, 0, v.Z) == Position - Vector3.new(Position.Y) then
				Amount += 1
			end
		end
		return Amount
	end

	Self.UpdateEnemies = function(EnemyTable)
		local NewTable = {}
		for i, v in pairs(EnemyTable) do
			if v.IsAttacking then
				local MoveDirection = Movement.GetNearestPlayerDirection(v.Position, false, false, nil, true)
				local NewData = {}
				NewData.Position = v.Position + MoveDirection
				-- you could check but this is the lazy way
				NewData.IsAttacking = true
			end
		end
		return NewTable
	end

	Self.GetOutsideSquares = function()
		local Squares = {}
		for i, Spot in pairs(Map) do
			for i, v in pairs(MoveDirections) do
				if not Self.CheckSquare(Spot + v) then
					table.insert(Squares, Spot)
					break
				end
			end
		end
		OutsidePositions = Squares
	end

	Self.IsOutsideSquare = function(Position, Squares)
		if table.find(OutsidePositions, Vector3.new(Position.X, 0, Position.Z)) then
			return true
		else
			return false
		end
	end

	Self.GetMoveInfo = function(EnemyTable, Depth, playerPos, SentDirections)
		local Spots = {}
		local PlayerPos = playerPos
		local DangerSpots = Self.GetDangerSpots(EnemyTable)
		local Loot = workspace.Loot:GetChildren()
		local Closestloot = nil
		local LootPos = nil
		local ClosestLootDistance = math.huge
		if #Loot ~= 0 then
			for i, v in pairs(workspace.Loot:GetChildren()) do
				local Distance = (v.PrimaryPart.Position - PlayerPos).Magnitude
				if Distance < ClosestLootDistance then
					Closestloot = v
					ClosestLootDistance = Distance
				end
			end
			LootPos = Closestloot.PrimaryPart.Position
		end

		local NewPos
		for i, v in pairs(MoveDirections) do
			DangerSpots = Self.GetDangerSpots(EnemyTable)
			--PlayerPos = game.Players.LocalPlayer.Character.PrimaryPart.Position

			local OriginPos = PlayerPos
			local AttackDirection = OriginPos + v

			--something wrong

			NewPos = (not Self.CheckSquare(AttackDirection) or Self.GetEnemyAtPosition(AttackDirection, EnemyTable))
					and OriginPos
				or AttackDirection

			local Directions
			if SentDirections then
				Directions = SentDirections
				table.remove(Directions, 1)
			end

			local Directions = (not SentDirections and LootPos) and Pathfinding.GetDirections(PlayerPos, LootPos)

			-- could check if there are still boxes

			local IsDangerous = Self.CheckIfDangerSpot(NewPos, DangerSpots)
			local Enemy = Self.GetEnemyAtPosition(AttackDirection, EnemyTable)
			local TimesPositionReached = Self.PositionReachedAmount(NewPos)
			local HasLoot = Closestloot and (Closestloot.PrimaryPart.Position - NewPos).Magnitude < 3
			local IsOutsideSquare = Self.IsOutsideSquare(NewPos)
			local IsDirectionOfCrate = Directions and Directions[1] == v

			-- make it realize that an enemy dies when it's attacked at 0 health
			-- make this account for paladins
			local AttackersOfPlayerSpot = Self.GetAttackersOfSpot(PlayerPos)
			local CanAttackThoughRed = #AttackersOfPlayerSpot == 1
				and AttackersOfPlayerSpot[1].Health.Value == 1
				and AttackersOfPlayerSpot[1].PrimaryPart.Position
					== AttackDirection + Vector3.new(
						0,
						-AttackDirection.Y + AttackersOfPlayerSpot[1].PrimaryPart.Position.Y
					)
			local Cost = (IsDangerous and not CanAttackThoughRed) and -10 or 0
			Cost += TimesPositionReached * -0.1
			Cost += IsOutsideSquare and -1 or 0
			Cost += (Enemy and not IsDangerous) and 2 or 0
			Cost += CanAttackThoughRed and 2 or 0
			Cost += IsDirectionOfCrate and 4 or 0
			Cost += HasLoot and 4 or 0

			if Depth > 0 then
				Cost += Self.GetMoveInfo(Self.UpdateEnemies(EnemyTable), Depth - 1, PlayerPos + v, Directions).Cost
			end

			local Data = {}
			Data.Direction = v
			Data.Cost = Cost
			table.insert(Spots, Data)
		end

		local BestSpot = Self.GetBestCostingSpot(Spots)

		table.insert(ExploredPositions, PlayerPos)

		return BestSpot
	end

	Self.MovePlayer = function()
		local PlayerPos = game.Players.LocalPlayer.Character.PrimaryPart.Position
		PlayerPos = Vector3.new(math.floor(PlayerPos.X + 0.5), 0, math.floor(PlayerPos.Z + 0.5))
		Self.GetOutsideSquares()
		local MoveInfo = Self.GetMoveInfo(Self.GetEnemies(), 0, PlayerPos)
		game.ReplicatedStorage.Events.Move:FireServer(MoveInfo.Direction.Unit)
	end

	return Self
end
