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

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
