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