local w={Vector3.new(5,0,0);Vector3.new(-5,0,0);Vector3.new(0,0,5),Vector3.new(0,0,-5)}local I={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(y)local p=(require(game.ReplicatedStorage.PathfindFunctions))(y)local x=(require(game.ReplicatedStorage.EnemyAi.Movement))(y)local X={}local B={}local m={}X.CheckSquare=function(w)if table.find(y,Vector3.new(w.X,0,w.Z))then return true end return false end X.GetDangerSpots=function(y)local p={}for I,y in pairs(y)do if y.IsAttacking then for w,I in pairs(w)do table.insert(p,(y.Position-Vector3.new(0,y.Position.Y))+I)end end end for w,I in pairs(workspace.ActiveProjectiles:GetChildren())do local y=I.PrimaryPart.Position+I.MoveDirection.Value local x=Vector3.new(math.floor(y.X/5)*5,0,math.floor(y.Z/5)*5)table.insert(p,x)end for w,y in pairs(workspace.ActiveTraps:GetChildren())do table.insert(p,y.PrimaryPart.Position)if y.PrimaryPart.Size.X>=12 then for w,I in pairs(I)do table.insert(p,y.PrimaryPart.Position+I)end end end return p end X.GetAttackersOfSpot=function(w)local I={}for y,p in pairs(workspace.ActiveEnemies:GetChildren())do if(p.PrimaryPart.Position-w).Magnitude<=7 then table.insert(I,p)end end return I end X.GetEnemies=function()local w={}for I,y in pairs(workspace.ActiveEnemies:GetChildren())do local p={}p.Position=y.PrimaryPart.Position p.IsAttacking=y.PrimaryPart.BrickColor==BrickColor.new("\082\101\097\108\108\121 \114\101\100")table.insert(w,p)end return w end X.GetEnemyAtPosition=function(w,I)for I,y in pairs(I)do if Vector3.new(y.Position.X,0,y.Position.Z)==Vector3.new(w.X,0,w.Z)then return y end end return false end X.CheckIfDangerSpot=function(w,I)for I,y in pairs(I)do if Vector3.new(y.X,0,y.Z)==Vector3.new(w.X,0,w.Z)then return y end end return false end X.GetBestCostingSpot=function(w)local I=-math.huge local y={}for w,p in pairs(w)do if p.Cost>I then I=p.Cost y={}table.insert(y,p)elseif p.Cost==I then table.insert(y,p)end end return y[math.random(1,#y)]end X.PositionReachedAmount=function(w)local I=0 for y,p in pairs(B)do if Vector3.new(p.X,0,p.Z)==w-Vector3.new(w.Y)then I+=1 end end return I end X.UpdateEnemies=function(w)local I={}for w,I in pairs(w)do if I.IsAttacking then local w=x.GetNearestPlayerDirection(I.Position,false,false,nil,true)local y={}y.Position=I.Position+w y.IsAttacking=true end end return I end X.GetOutsideSquares=function()local I={}for y,p in pairs(y)do for w,y in pairs(w)do if not X.CheckSquare(p+y)then table.insert(I,p)break end end end m=I end X.IsOutsideSquare=function(w,I)if table.find(m,Vector3.new(w.X,0,w.Z))then return true else return false end end X.GetMoveInfo=function(I,y,x,m)local l={}local K=x local V=X.GetDangerSpots(I)local H=workspace.Loot:GetChildren()local J=nil local N=nil local Q=math.huge if#H~=0 then for w,I in pairs(workspace.Loot:GetChildren())do local y=(I.PrimaryPart.Position-K).Magnitude if y<Q then J=I Q=y end end N=J.PrimaryPart.Position end local i for w,x in pairs(w)do V=X.GetDangerSpots(I)local B=K local H=B+x i=(not X.CheckSquare(H)or X.GetEnemyAtPosition(H,I))and B or H local Q if m then Q=m table.remove(Q,1)end local R=(not m and N)and p.GetDirections(K,N)local F=X.CheckIfDangerSpot(i,V)local Y=X.GetEnemyAtPosition(H,I)local e=X.PositionReachedAmount(i)local d=J and(J.PrimaryPart.Position-i).Magnitude<3 local C=X.IsOutsideSquare(i)local s=R and R[1]==x local f=X.GetAttackersOfSpot(K)local D=#f==1 and(f[1].Health.Value==1 and f[1].PrimaryPart.Position==H+Vector3.new(0,-H.Y+f[1].PrimaryPart.Position.Y))local P=(F and not D)and-10 or 0 P+=e*-0.1 P+=C and-1 or 0 P+=(Y and not F)and 2 or 0 P+=D and 2 or 0 P+=s and 4 or 0 P+=d and 4 or 0 if y>0 then P+=(X.GetMoveInfo(X.UpdateEnemies(I),y-1,K+x,R)).Cost end local u={}u.Direction=x u.Cost=P table.insert(l,u)end local R=X.GetBestCostingSpot(l)table.insert(B,K)return R end X.MovePlayer=function()local w=game.Players.LocalPlayer.Character.PrimaryPart.Position w=Vector3.new(math.floor(w.X+.5),0,math.floor(w.Z+.5))X.GetOutsideSquares()local I=X.GetMoveInfo(X.GetEnemies(),0,w)game.ReplicatedStorage.Events.Move:FireServer(I.Direction.Unit)end return X 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
