package.loaded.Constants = nil; require("Constants");


---------------------------------------------------------
-- BRAIN INTEGRITY CHECK
function MetaFight:BrainCheck()

	-- Clear all objective markers, they get re-added each frame
	self:ClearObjectivePoints();
	-- Keep track of which teams we have set objective points for already, since multiple players can be on the same team
	local setTeam = { [Activity.TEAM_1] = false, [Activity.TEAM_2] = false, [Activity.TEAM_3] = false, [Activity.TEAM_4] = false };
	
	-----------------------------------------------------------------
	-- Brain integrity check logic for every player, Human or AI
	for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
		if self:PlayerActive(player) then

			-- The current player's team
			local team = self:GetTeamOfPlayer(player);
		
--				if not self.StartTimer:IsPastSimMS(3000) then
--					FrameMan:SetScreenText("Destroy the enemy brain(s)!", self:ScreenOfPlayer(player), 333, 5000, true);
--				end

			-- Only worry about brain death if the player ever had one yet
			if self:PlayerHadBrain(player) then
				-- Check if any player's brain is now dead
				if not MovableMan:IsActor(self:GetPlayerBrain(player)) or not self:GetPlayerBrain(player):HasObjectInGroup("Brains") then
					self:SetPlayerBrain(nil, player);

					-- Try to find a new brain and assign it
					newBrain = MovableMan:GetUnassignedBrain(team);
					if newBrain then
						self:SetPlayerBrain(newBrain, player);
						self:SwitchToActor(newBrain, player, team);
					-- All hope is lost for this player
					else
						self:ResetMessageTimer(player);
--						FrameMan:ClearScreenText(player);
--						FrameMan:SetScreenText("Your brain has been destroyed!", self:ScreenOfPlayer(player), 333, -1, false);
						-- Save the position of this player's brain's last known position as the last brain death position
						self.LastBrainDeathPos = self.LastBrainPos[player];

						-- Now see if all brains are dead of this player's team, and if so, check if there's only one team left with brains
						if not MovableMan:GetFirstBrainActor(team) then
							-- If only one team left with any brains, they are the winners!
							if self:OneOrNoneTeamsLeft() then
								-- Only do EndActivity if the winner outcome has changed
								local winnerChanged = self.WinnerTeam ~= self:WhichTeamLeft();
								self.WinnerTeam = self:WhichTeamLeft();
								if winnerChanged then
									ActivityMan:EndActivity();
								end
							end
							-- This whole loser team is done for, so self-destruct all of its actors
							for actor in MovableMan.Actors do
								if actor.Team == team then
									if IsAHuman(actor) and ToAHuman(actor).Head then
										ToAHuman(actor).Head:GibThis();
									-- Doors don't get destroyed, they just change teams to the winner
									elseif IsADoor(actor) then
										actor.Team = self.WinnerTeam;
									else
										actor:GibThis();
									end
								end
							end
							-- Finally, deactive the player entirely -  no don't this is done in AutoResolveOffensive if needed
--							self:DeactivatePlayer(player);
						end

						-- Check if all human-controlled brains are goners, and if so, end the activity also
						if self:HumanBrainCount() == 0 and self.ActivityState ~= Activity.OVER then
							ActivityMan:EndActivity();
						end
					end
				-- We do have a brain
				else
					-- Save the last known position of this player's brain
					self.LastBrainPos[player] = self:GetPlayerBrain(player).Pos;
					-- Continually set the observation target to the brain during play, so that if/when it dies, the view flies to it in observation mode
					if self.ActivityState ~= Activity.OVER and self:GetViewState(player) ~= Activity.OBSERVE then
						self:SetObservationTarget(self:GetPlayerBrain(player).Pos, player);
					end
					
					if not setTeam[team] then
						-- Add objective points
						self:AddObjectivePoint("Protect!", self:GetPlayerBrain(player).AboveHUDPos, team, GameActivity.ARROWDOWN);
						for otherPlayer = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
							if otherPlayer ~= player and self:PlayerActive(otherPlayer) and MovableMan:IsActor(self:GetPlayerBrain(otherPlayer)) then
								local otherTeam = self:GetTeamOfPlayer(otherPlayer);
								if otherTeam ~= team then
									self:AddObjectivePoint("Destroy!", self:GetPlayerBrain(otherPlayer).AboveHUDPos, team, GameActivity.ARROWDOWN);
								else
									self:AddObjectivePoint("Protect!", self:GetPlayerBrain(otherPlayer).AboveHUDPos, team, GameActivity.ARROWDOWN);
								end
							end
						end
						setTeam[team] = true;
					end
				end
			-- See if we can now find a brain to designate
			else
				foundBrain = MovableMan:GetUnassignedBrain(team);
				self:SetPlayerBrain(foundBrain, player);
				-- If this is a brain that has landed on the surface and gotten out of his ship, switch to him
				if foundBrain and foundBrain:IsInGroup("Brains") then
					self:SwitchToActor(foundBrain, player, team);
				end
			end
			
			-- If there's only one active team, it means they are clearing out wildlife in this mission
			if self.TeamCount == 1 then
				-- Check if there's any wildlife left to clear out, and the brain is standing on the surface (and not in a ship still)
				if not MovableMan:GetFirstTeamActor(Activity.NOTEAM, Activity.NOPLAYER) and MovableMan:IsActor(self:GetPlayerBrain(player)) and self:GetPlayerBrain(player):IsInGroup("Brains") then
					-- Only do EndActivity if the winner outcome has changed
					local winnerChanged = self.WinnerTeam ~= self:WhichTeamLeft();
					self.WinnerTeam = self:WhichTeamLeft();
					if winnerChanged then
						ActivityMan:EndActivity();
					end
				end
			end
		end
	end
end


---------------------------------------------------------
-- START
function MetaFight:StartActivity()

	-- Not editing mode, but a pre-game start mode where players get to place their brains initially
	self.ActivityState = Activity.PREGAME;
	-- Whether there are literally NO brains left in the scene
	self.NoBrainsLeft = false;

	-- Open all doors so we can do pathfinding through them with the brain placement
	MovableMan:OpenAllDoors(true, Activity.NOTEAM);
	
	-- Make all leftover craft take off
	for actor in MovableMan.Actors do
		-- Set all craft to fly away into orbit
		if IsACRocket(actor) or IsACDropShip(actor) then
			actor.AIMode = Actor.AIMODE_RETURN;
		end
	end

	-- Orbit Scene Scanning vars
	self.ScanStage = { PRESCAN = 0, SCANNING = 1, POSTSCAN = 2, DONESCAN = 3, SCANSTAGECOUNT = 4 };
--	self.CurrentScanStage = { [Activity.TEAM_1] = self.ScanStage.PRESCAN, [Activity.TEAM_2] = self.ScanStage.PRESCAN, [Activity.TEAM_3] = self.ScanStage.PRESCAN, [Activity.TEAM_4] = self.ScanStage.PRESCAN };
	-- Start by assming no teams have scanning scheduled
	self.CurrentScanStage = self.ScanStage.DONESCAN;
	self.ScanPosX = { [Activity.TEAM_1] = -1, [Activity.TEAM_2] = -1, [Activity.TEAM_3] = -1, [Activity.TEAM_4] = -1 };
	self.ScanTimer = { [Activity.TEAM_1] = Timer(), [Activity.TEAM_2] = Timer(), [Activity.TEAM_3] = Timer(), [Activity.TEAM_4] = Timer() };
	self.StartFunds = { [Activity.TEAM_1] = 0, [Activity.TEAM_2] = 0, [Activity.TEAM_3] = 0, [Activity.TEAM_4] = 0 };
	self.ScanEndPos = Vector();

	-- Reset scan timers and check that there's any teams with a scheduled scan at all
	for team = Activity.TEAM_1, Activity.MAXTEAMCOUNT - 1 do
		if self:TeamActive(team) then
			self.ScanTimer[team]:Reset();
			-- Yes there is at least one active team that has scanning scheduled
			if SceneMan.Scene:IsScanScheduled(team) then
				self.CurrentScanStage = self.ScanStage.PRESCAN;
			end
		end
	end

	-- Which are the invading teams
	self.InvadingTeam = { [Activity.TEAM_1] = false, [Activity.TEAM_2] = false, [Activity.TEAM_3] = false, [Activity.TEAM_4] = false };
	-- Which Teams are managed tactically by the AI? Only teams with NO human players on them
	self.TeamAIActive = { [Activity.TEAM_1] = false, [Activity.TEAM_2] = false, [Activity.TEAM_3] = false, [Activity.TEAM_4] = false };
	-- Team has given up and is evacuating their brain
	self.TeamEvacuating = { [Activity.TEAM_1] = false, [Activity.TEAM_2] = false, [Activity.TEAM_3] = false, [Activity.TEAM_4] = false };
	
	for team = Activity.TEAM_1, Activity.MAXTEAMCOUNT - 1 do
		if self:TeamActive(team) then
			-- Start out assuming all teams are all AI invaders, then disprove it
			self.InvadingTeam[team] = true;
			self.TeamAIActive[team] = true;
			self.TeamEvacuating[team] = false;
			-- Inspect all the players of each team
			for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
				if self:PlayerActive(player) and self:GetTeamOfPlayer(player) == team then
					-- Any team with a player that has a brain resident in the scene are DEFENDERS
					if SceneMan.Scene:GetResidentBrain(player) then
						self.InvadingTeam[team] = false;
					end
					-- Disable the tactical team management on any team that has ANY human players on it - the humans get full control
					if self:PlayerHuman(player) then
						self.TeamAIActive[team] = false;
					end
				end
			end
		end
	end	

	-- MetaFight-specific player parameters
	self.InvadingPlayer = { [Activity.PLAYER_1] = false, [Activity.PLAYER_2] = false, [Activity.PLAYER_3] = false, [Activity.PLAYER_4] = false };
	self.Ready = { [Activity.PLAYER_1] = false, [Activity.PLAYER_2] = false, [Activity.PLAYER_3] = false, [Activity.PLAYER_4] = false };
	self.InvadingPlayerCount = 0;
	self.OrderTimer = { [Activity.PLAYER_1] = Timer(), [Activity.PLAYER_2] = Timer(), [Activity.PLAYER_3] = Timer(), [Activity.PLAYER_4] = Timer() };
	-- At what funds level the AI starts thinking about going into brain evacuation mode
	self.EvacThreshold = { [Activity.PLAYER_1] = 100, [Activity.PLAYER_2] = 100, [Activity.PLAYER_3] = 100, [Activity.PLAYER_4] = 100 };
	-- The last known position of all players' active brains
	self.LastBrainPos = { [Activity.PLAYER_1] = Vector(), [Activity.PLAYER_2] = Vector(), [Activity.PLAYER_3] = Vector(), [Activity.PLAYER_4] = Vector() };
	-- The position of the last brain that died
	self.LastBrainDeathPos = Vector();
	
	-- Count how many invading players there are
	for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
		if self:PlayerActive(player) then -- and self:PlayerHuman(player) then
			if not SceneMan.Scene:GetResidentBrain(player) then
				self.InvadingPlayerCount = self.InvadingPlayerCount + 1;
			end
		end
	end
	
	-- Now init all players 
	for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
		if self:PlayerActive(player) then
			-- Reset the timer that will measure the delay between ordering of reinforcements
			self.OrderTimer[player]:Reset();
			-- Determine whether this player is invading or already has a brain to defend here
			residentBrain = SceneMan.Scene:GetResidentBrain(player);

			-- Human player; init things common to all humans
			if self:PlayerHuman(player) then
				-- Set the Buy menu to reflect the native tech of this metaplayer
				for metaPlayer = Activity.PLAYER_1, MetaMan.PlayerCount - 1 do
					if MetaMan:GetPlayer(metaPlayer).InGamePlayer == player then
						self:GetEditorGUI(player):SetNativeTechModule(MetaMan:GetPlayer(metaPlayer).NativeTechModule);
						self:GetEditorGUI(player):SetForeignCostMultiplier(MetaMan:GetPlayer(metaPlayer).ForeignCostMultiplier);
						self:GetBuyGUI(player):SetMetaPlayer(metaPlayer);
						-- Re-load all the loadouts now that the metaplayer has been set
						self:GetBuyGUI(player):LoadAllLoadoutsFromFile();
					end
				end
				
				-- Set a temporary LZ
				self:SetLandingZone(Vector(player*SceneMan.SceneWidth/4, 0), player);
			end

			-- Invading player
			if not residentBrain then
				self.InvadingPlayer[player] = true;
				-- Sanity check
				if self.InvadingPlayerCount < 1 then
					self.InvadingPlayerCount = 1;
				end
				
				-- Human player; init as appropriate for invaders
				if self:PlayerHuman(player) then
					-- Set an initial landing team based on appropriate for his Tech's default brain Loadout
					self:SetOverridePurchaseList("Infantry Brain", player);
				
					-- Set the mode to LZ select so the player can choose where to land first
					self:SetViewState(Activity.LZSELECT, player);
					FrameMan:SetScreenText("Choose where to land your assualt brain", self:ScreenOfPlayer(player), 250, 3500, false);
					self:ResetMessageTimer(player);
				end

			-- Defending player
			else
				self.InvadingPlayer[player] = false;
				
				-- Human player; init as appropriate for defenders
				if self:PlayerHuman(player) then
					-- Test if the brain is in a valid position
					if not self:GetEditorGUI(player):TestBrainResidence(false) then
						-- Force player to re-place his brain
						self:GetEditorGUI(player):Update();
						FrameMan:SetScreenText("PLACE YOUR BRAIN IN A VALID SPOT FIRST", self:ScreenOfPlayer(player), 250, 3500, false);
						self:ResetMessageTimer(player);
					else
						self.Ready[player] = true;
						-- Place this player's resident brain into the simulation and set it as the player's assigned brain
						SceneMan.Scene:PlaceResidentBrain(player, self);
						FrameMan:SetScreenText("READY to start - wait for others to finish...", self:ScreenOfPlayer(player), 333, -1, false);
						self:ResetMessageTimer(player);
					end

-- Use this in the update later
--				  self:SetLandingZone(residentBrain.Pos, player);
					-- Set the observation target to the brain, so that if/when it dies, the view flies to it in observation mode
--				  self:SetObservationTarget(residentBrain.Pos, player);

				-- AI defending player; init its brain which should already exist and signal ready
				else
					-- AI brain should be ready to rock
					self.Ready[player] = true;
					SceneMan.Scene:PlaceResidentBrain(player, self);

					-- Still no brain of this player? Last ditch effort to find one and assign it to this player
					if not self:GetPlayerBrain(player) then
						self:SetPlayerBrain(MovableMan:GetUnassignedBrain(self:GetTeamOfPlayer(player)), player);
					end
					-- Something went wrong.. recover
					if not self:GetPlayerBrain(player) then
-- TODO: spawn a new brain in an appropriate spot
					end
				end
			end
		end
	end

	-- Starting in pregame, so play some appropriate music
	if self.ActivityState == Activity.PREGAME then
		AudioMan:ClearMusicQueue();
		AudioMan:PlayMusic("Base.rte/Music/dBSoundworks/ccambient4.ogg", -1, -1);
	end

	-- Disable AI if we are editing pregame
	self:DisableAIs(self.ActivityState == Activity.PREGAME, Activity.NOTEAM);

	-- Set the brain-tracking landing zones of each player
	self:SetBrainLZWidth(Activity.PLAYER_1, 600);
	self:SetBrainLZWidth(Activity.PLAYER_2, 600);
	self:SetBrainLZWidth(Activity.PLAYER_3, 600);
	self:SetBrainLZWidth(Activity.PLAYER_4, 600);

	-- Move any brains resident in the Scene to the MovableMan
-- No, this is taken care of by GameActivity::UpdateEditing
--	SceneMan.Scene:PlaceResidentBrains(self);

	self.StartTimer = Timer();
--	self.TimeLeft = (self.BaseSpawnTime + math.random(self.RandomSpawnTime)) * rte.SpawnIntervalScale;
end

---------------------------------------------------------
-- PAUSE
function MetaFight:PauseActivity(pause)

end

---------------------------------------------------------
-- END
function MetaFight:EndActivity()

	-- If there's no brains left in the scene at all after game over, then re-do the outcome
	self.NoBrainsLeft = self:NoTeamLeft();
	
	-- This is now no-man's land
	if self.NoBrainsLeft then
		self.WinnerTeam = Activity.NOTEAM;
		SceneMan.Scene.TeamOwnership = Activity.NOTEAM;
		SceneMan.Scene:ClearPlacedObjectSet(Scene.BLUEPRINT);
		-- Sad music
		AudioMan:ClearMusicQueue();
		AudioMan:PlayMusic("Base.rte/Music/dBSoundworks/udiedfinal.ogg", 2, -1.0);
		AudioMan:QueueSilence(10);
		AudioMan:QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg");
	-- Also play sad music if no humans are left
	elseif self:HumanBrainCount() == 0 then
		AudioMan:ClearMusicQueue();
		AudioMan:PlayMusic("Base.rte/Music/dBSoundworks/udiedfinal.ogg", 2, -1.0);
		AudioMan:QueueSilence(10);
		AudioMan:QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg");		
	-- But if humans are left, then play happy music!
	else
		-- Win music!
		AudioMan:ClearMusicQueue();
		AudioMan:PlayMusic("Base.rte/Music/dBSoundworks/uwinfinal.ogg", 2, -1.0);
		AudioMan:QueueSilence(10);
		AudioMan:QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg");
	end

	-- Display appropriate message for each player, winner or loser
	for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
		if self:PlayerActive(player) and self:PlayerHuman(player) then
			-- Winners
			if self:GetTeamOfPlayer(player) == self.WinnerTeam then
				-- Owned the site/base at start
				if SceneMan.Scene.TeamOwnership == self:GetTeamOfPlayer(player) then
					FrameMan:SetScreenText("Your team has successfully defended this site!", self:ScreenOfPlayer(player), 0, -1, false);
				else
					FrameMan:SetScreenText("Your team has successfully taken over this site!", self:ScreenOfPlayer(player), 0, -1, false);
				end
			-- Losers
			else
				-- Owned the site/base at start
				if SceneMan.Scene.TeamOwnership == self:GetTeamOfPlayer(player) then
					FrameMan:SetScreenText("Your team's brains have been destroyed and therefore lost this site!", self:ScreenOfPlayer(player), 0, -1, false);
				else
					FrameMan:SetScreenText("Your attempt to take over this site has failed!", self:ScreenOfPlayer(player), 0, -1, false);
				end
			end
			-- Set the observation targets if everyone to the last brain's death
			if not self.LastBrainDeathPos:IsZero() then
				self:SetViewState(Activity.OBSERVE, player);
				self:SetObservationTarget(self.LastBrainDeathPos, player);
			end
			self:ResetMessageTimer(player);
		end
	end
				
	-- Give the victorious team ownership of the played scene
	if self.WinnerTeam ~= Activity.NOTEAM then
		-- If these are new owners, delete the previous team's blueprint building plans for this base
		if SceneMan.Scene.TeamOwnership ~= self.WinnerTeam then
			SceneMan.Scene:ClearPlacedObjectSet(Scene.BLUEPRINT);
		end
		
		-- If there was only one active team to begin with, it means they are clearing out wildlife in this mission
		if self.TeamCount == 1 then
			-- Play less exctatic music
--			AudioMan:ClearMusicQueue();
--			AudioMan:PlayMusic("Base.rte/Music/dBSoundworks/ccambient4.ogg", -1, -1.0);
		else
			-- Win music!
--			AudioMan:ClearMusicQueue();
--			AudioMan:PlayMusic("Base.rte/Music/dBSoundworks/uwinfinal.ogg", 2, -1.0);
--			AudioMan:QueueSilence(10);
--			AudioMan:QueueMusicStream("Base.rte/Music/dBSoundworks/ccambient4.ogg");
		end

		-- Actually change ownership
		SceneMan.Scene.TeamOwnership = self.WinnerTeam;
	end
	
	-- Open all doors
	MovableMan:OpenAllDoors(true, Activity.NOTEAM);
	
end

---------------------------------------------------------
-- UPDATE
function MetaFight:UpdateActivity()
	--------------------------------------------------------
	-- Immediately do scanning for teams who have scheduled it
	local scanMessage = "Scanning";
	local messageBlink = 500;
	
	-- Wait a sec first before starting to scan, so player can get what's going on
	if self.CurrentScanStage == self.ScanStage.PRESCAN then
		scanMessage = "Preparing to scan site from orbit";
		for dotCount = 0, math.floor(self.ScanTimer[Activity.TEAM_1].ElapsedSimTimeMS / 500) do
			scanMessage = " " .. scanMessage .. ".";
		end
		messageBlink = 0;
--			self:SetObservationTarget(Vector(0, 0), player);
		if self.ScanTimer[Activity.TEAM_1]:IsPastSimMS(2000) then
			self.CurrentScanStage = self.ScanStage.SCANNING;
		end
	-- Do actual scanning process for those teams that have scheduled it
	elseif self.CurrentScanStage == self.ScanStage.SCANNING then
		local doneScan = true;
		for team = Activity.TEAM_1, Activity.MAXTEAMCOUNT - 1 do
			if self:TeamActive(team) and SceneMan.Scene:IsScanScheduled(team) then
				-- Time to do a scan step?
				if self.ScanTimer[team]:IsPastRealMS(SceneMan:GetUnseenResolution(team).X * 2) then
					-- Scan the column, find the end where the ray is blocked
					SceneMan:CastSeeRay(team, Vector(self.ScanPosX[team], 0), Vector(0, SceneMan.Scene.Height), self.ScanEndPos, 50, SceneMan:GetUnseenResolution(team).Y / 2);
					-- Adjust up a bit so one sees more of the sky than blackness
					self.ScanEndPos.Y = self.ScanEndPos.Y - (FrameMan.PlayerScreenHeight / 4);
					-- Also a bit more behind the scanning front so we see more of the terrain
					self.ScanEndPos.X = self.ScanEndPos.X - (FrameMan.PlayerScreenWidth / 4);
					if self.ScanEndPos.X < 0 then
						self.ScanEndPos.X = 0;
					end

					if (self.ScanPosX[team] < SceneMan.Scene.Width) then
						scanMessage = "Scanning";
						messageBlink = 500;
						-- Move on to the next column
						self.ScanPosX[team] = self.ScanPosX[team] + SceneMan:GetUnseenResolution(team).X;
						-- Set the proportionate amount from the team funds to represent payment for the scanning
-- This has already been paid when the scan was scheduled in the metagame GUI
--							self:SetTeamFunds((self.StartFunds[team] * (1.0 - (self.ScanPosX[team] / SceneMan.Scene.Width))) * rte.StartingFundsScale, team);
						self.ScanTimer[team]:Reset();
						-- Set all screens of the teammates to the ray end pos so their screens follow the scanning
--[[
						for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
							if self:PlayerActive(player) and self:PlayerHuman(player) then
								if self:GetTeamOfPlayer(player) == team then
-- Not applicable in metafight; just scan while the player does placing etc
--										self:SetViewState(Activity.OBSERVE, player);
--										self:SetObservationTarget(self.ScanEndPos, player);
								end
							end
						end
--]]
					end
				end
				-- Check if anyone isn't done scanning yet
				if self.ScanPosX[team] < SceneMan.Scene.Width then
					doneScan = false;
				end 
			end		   
		end
		
		-- If done scanning ALL TEAMS, move on the the post pause phase
		if doneScan then
			self.CurrentScanStage = self.ScanStage.POSTSCAN;
		end
	-- After scan, pause for a second before moving on to gameplay
	elseif self.CurrentScanStage == self.ScanStage.POSTSCAN then
		if self.ScanTimer[Activity.TEAM_1]:IsPastRealMS(2500) then
			for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
				if self:PlayerActive(player) and self:PlayerHuman(player) and SceneMan.Scene:IsScanScheduled(self:GetTeamOfPlayer(player)) then
					FrameMan:ClearScreenText(self:ScreenOfPlayer(player));
				end
			end
			self.CurrentScanStage = self.ScanStage.DONESCAN;
			-- Reset all scanning scheduling since it has now been completed
			for team = Activity.TEAM_1, Activity.MAXTEAMCOUNT - 1 do
				if self:TeamActive(team) then
					SceneMan.Scene:SetScheduledScan(team, false);
				end
			end
		else
			scanMessage = "Complete!";
			messageBlink = 0;
		end
	end
	
	-- Display the scanning text on all players' screens
	if self.CurrentScanStage < self.ScanStage.DONESCAN then
		for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
			if self:PlayerActive(player) and self:PlayerHuman(player) then
				-- The current player's team
				local team = self:GetTeamOfPlayer(player);
	--				if (self.ActivityState == Activity.RUNNING) then
				if (SceneMan.Scene:IsScanScheduled(team)) then
					FrameMan:ClearScreenText(self:ScreenOfPlayer(player));
					FrameMan:SetScreenText(scanMessage, self:ScreenOfPlayer(player), messageBlink, 8000, false);
				end
			end
		end
	end

	-----------------------------------------------
	-- Players are placing brains and intital invading forces
	if self.ActivityState == Activity.PREGAME then
		-- See if players are getting done with placing etc and ready to start		
		local allReady = true;
		
		for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
			if self:PlayerActive(player) and self:PlayerHuman(player) then
				----------------------------------
				-- DEFENDERS
				-- Update the editor if we're placing a brain
				if not self.InvadingPlayer[player] and not self.Ready[player] then
					self:GetEditorGUI(player):Update();
					-- This defending player appears to be done placing his brain
					if self:GetEditorGUI(player).EditorMode == SceneEditorGUI.PICKINGOBJECT or self:GetEditorGUI(player).EditorMode == SceneEditorGUI.DONEEDITING then
						-- Check if we're really ready - if not, TestBrainRes will kick us back to installing brain mode
						self.Ready[player] = self:GetEditorGUI(player):TestBrainResidence(false);
					end
				end

				----------------------------------
				-- INVADERS
				if self.InvadingPlayer[player] and not self.Ready[player] then
					-- If not in LZSelect anymore, then this guy must be done placing his initial landing spot
					if self:GetViewState(player) ~= Activity.LZSELECT then
						-- Hm, no deliveries coming? then go back and try again, punk
						if self:GetDeliveryCount(self:GetTeamOfPlayer(player)) < 1 then
							-- Set the mode to LZ selct so the player can choose where to land first
							self:SetViewState(Activity.LZSELECT, player);
							FrameMan:SetScreenText("Choose where to land your assualt brain", self:ScreenOfPlayer(player), 250, 3500, false);
							self:ResetMessageTimer(player);
						else
							-- Ok, done for real
							self.Ready[player] = true;
						end
					end
				end

				-- Keep showing ready message for those who are
				if self.Ready[player] then
					FrameMan:SetScreenText("READY to start - wait for others to finish...", self:ScreenOfPlayer(player), 333, -1, false);
				else
					allReady = false;
				end
			end
		end
		
		-- YES, we are allegedly all ready to stop editing and start the game!
		if allReady then
			for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
				if self:PlayerActive(player) and self:PlayerHuman(player) then
					-- Place all defending human brains into the simulation
					if not self.InvadingPlayer[player] then
						-- If not already done, place this player's resident brain into the simulation and set it as the player's assigned brain
						SceneMan.Scene:PlaceResidentBrain(player, self);
						-- Still no brain of this player? Last ditch effort to find one and assign it to this player
						if not self:GetPlayerBrain(player) then
							self:SetPlayerBrain(MovableMan:GetUnassignedBrain(self:GetTeamOfPlayer(player)), player);
						end
						-- Something went wrong.. we're not done placing brains after all?
						if not self:GetPlayerBrain(player) then
							allReady = false;
							-- Get the brains back into residency so the players who are OK are still so
							SceneMan.Scene:RetrieveResidentBrains(self);
							break;
						end
						
						-- Set the brain to be the selected actor at start
						self:SwitchToActor(self:GetPlayerBrain(player), player, self:GetTeamOfPlayer(player));
						self:SetLandingZone(self:GetPlayerBrain(player).Pos, player);
						-- Update the observation target to the brain, so that if/when it dies, the view flies to it in observation mode
						self:SetObservationTarget(self:GetPlayerBrain(player).Pos, player);
						-- Clear the messages before starting the game
						self:ResetMessageTimer(player);
						FrameMan:ClearScreenText(player);
						-- Reset the screen occlusion if any players are still in menus
						SceneMan:SetScreenOcclusion(Vector(), self:ScreenOfPlayer(player));
					end
				end
			end
		end
		
		-- Still good to go? then START!
		if allReady then
			-- START the game
			self.ActivityState = Activity.RUNNING;
			-- Close all doors after placing brains so our fortresses are secure
			MovableMan:OpenAllDoors(false, Activity.NOTEAM);
			-- Activate the AIs
			self:DisableAIs(false, Activity.NOTEAM);
			self:InitAIs()
			-- Reset the mouse value and pathfinding so it'll know about the newly placed stuff
			UInputMan:SetMouseValueMagnitude(0);
			SceneMan.Scene:ResetPathFinding();
			-- Start the in-game music track
			AudioMan:ClearMusicQueue();
			AudioMan:PlayMusic("Base.rte/Music/dBSoundworks/cc2g.ogg", 0, -1.0);
			AudioMan:QueueSilence(30);
			AudioMan:QueueMusicStream("Base.rte/Music/Watts/Last Man.ogg");
			AudioMan:QueueSilence(30);
			AudioMan:QueueMusicStream("Base.rte/Music/dBSoundworks/cc2g.ogg");
		end
	end

	-----------------------------------------------
	-- Game is RUNNING
	if self.ActivityState == Activity.RUNNING then
		if not self.LZsDesignated then
			self.LZsDesignated = true	-- only run once
			self:DesignateLZs()
		end
		
		-------------------------------------
		-- Tactical AI team management
		for team = Activity.TEAM_1, Activity.MAXTEAMCOUNT - 1 do
			if self:TeamActive(team) and self.TeamAIActive[team] then				
				-- INVADERS
				if self.InvadingTeam[team] then
					
-- TODO finish this
					
					-- Give all existing team actors appropriate AI orders
					for actor in MovableMan.Actors do
						if actor.Team == team then
							-- The brain should try to dig itself into the ground to fortify itself agasint counterattack
							if actor:IsInGroup("Brains") then
								actor.AIMode = Actor.AIMODE_GOLDDIG;
							-- Not a brain, so is it a humanoid that can go hunt the enemy brain?
							elseif IsAHuman(actor) then
								actor.AIMode = Actor.AIMODE_BRAINHUNT;
							end
						end
					end
					
					
					
				-- DEFENDERS
				else
					
-- TODO finish this

					-- Give all existing team actors appropriate AI orders
					for actor in MovableMan.Actors do
						if actor.Team == team then
							-- The brain should stay put, presumably in a safe spot as dictated by the base plan
							if actor:IsInGroup("Brains") then
								actor.AIMode = Actor.AIMODE_SENTRY;
							-- Not a brain, so is it a humanoid that can go hunt the enemy brain?
							-- If so, check if it was ordered during the fight as opposed to placed beforehand as defensive sentry
							elseif IsAHuman(actor) and actor.PlacedByPlayer == Activity.NOPLAYER then
								actor.AIMode = Actor.AIMODE_BRAINHUNT;
							end
						end
					end

					
					
					
				end
			end
		end

		-----------------------------------------------------------------
		-- Brain integrity check logic for every player, Human or AI
		MetaFight:BrainCheck();
		
		-----------------------------------------------------------
		-- AI players on AI-controlled teams order in reinforcements while they can afford them
		for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
			if self:PlayerActive(player) and not self:PlayerHuman(player) and self.TeamAIActive[self:GetTeamOfPlayer(player)] and self:GetPlayerBrain(player) then
				-- Only proceed if we have enough funds to justify a new order
				if self:GetTeamFunds(self:GetTeamOfPlayer(player)) > self.EvacThreshold[player] then
					-- Only order stuff if we don't have stuff already on the way, and with a delay between each shipment
					if self:GetDeliveryCount(self:GetTeamOfPlayer(player)) < 1 and self.OrderTimer[player].ElapsedSimTimeMS > 15000 then
						-- Select LZ's that are close to team brains, but not on top of them
						local lzX
						if PosRand() > 0.5 then	-- Land to the left of the brain
							lzX = self:GetPlayerBrain(player).Pos.X - math.random(80, 250);
						else	-- Land to the right of the brain
							lzX = self:GetPlayerBrain(player).Pos.X + math.random(80, 250);
						end
						
						-- Make sure we're not sending AI goons to land outside of the scene
						self:WrapLZ(lzX)
						-- Set the LZ so the delivery will pop up there
						self:SetLandingZone(Vector(lzX, 0), player);
						-- Set an initial landing team based on appropriate for his Tech's default brain Loadout
						self:SetOverridePurchaseList(rte.OffensiveLoadouts[math.random(#rte.OffensiveLoadouts)], player);
						-- Create the actual delivery so it'll come down soon
						self:CreateDelivery(player);
						-- Have a delay between ordering things
						self.OrderTimer[player]:Reset();
					end
				-- Oh shit we are out of money! Figure out if we have enough guys on the ground still or if we should go into evac mode
				else
-- TODO	
				end
			end
		end
	else
		self.StartTimer:Reset();
	end

	-----------------------------------------------
	-- Game is OVER
	if self.ActivityState == Activity.OVER then
		-- Continue to make sure any remaining brains which were declared victorious are still okay.. and if not, change the outcome
		if not self.NoBrainsLeft then
			-- Keep checking the health of remaining brains
			MetaFight:BrainCheck();
		end
	end
end


-----------------------------------------------
-- Make sure that the LZ is inside the map
function MetaFight:WrapLZ(lzX)
	if SceneMan.SceneWrapsX then
		if lzX > SceneMan.SceneWidth then
			lzX = lzX - SceneMan.SceneWidth;
		elseif lzX < 0 then
			lzX = lzX + SceneMan.SceneWidth;
		end
	else
		if lzX > SceneMan.SceneWidth then
			lzX = SceneMan.SceneWidth - 100;
		elseif lzX < 0 then
			lzX = 100;
		end							
	end
	
	return lzX
end


-----------------------------------------------
-- Make sure that all players have a LZ
function MetaFight:DesignateLZs()
	-- Get scene LZs
	local SceneLZs = {}
	for i = 1, 4 do
		local Area = SceneMan.Scene:GetArea("LZ Team "..i)
		if Area then
			table.insert(SceneLZs, Area)
		end
	end
	
	local OccupiedLZs = {}
	
	-- First, get the player LZs
	for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
		if self:PlayerHuman(player) then
			-- The players have placed the LZs by now, insert them in to the occupied list
			local PlayerLZ = self:GetLandingZone(player)
			table.insert(OccupiedLZs, {x=PlayerLZ.X, team=self:GetTeamOfPlayer(player)})
		end
	end
	
	-- Now search for AI LZs
	for player = Activity.PLAYER_1, Activity.MAXPLAYERCOUNT - 1 do
		if self:PlayerActive(player) and not self:PlayerHuman(player) and self.InvadingPlayer[player] then
			local team = self:GetTeamOfPlayer(player)
			if self.TeamAIActive[team] then				
				-- Find a good spot to land (allow the AI to cheat and see all enemy units)
				local lzX = self:FindLZ(team, OccupiedLZs, SceneLZs, false)
				-- Mark this LZ as occupied
				table.insert(OccupiedLZs, {x=lzX, team=team})
				-- Set the LZ so the delivery will pop up there
				self:SetLandingZone(Vector(lzX, 0), player)
				-- Set an initial landing team based on appropriate for his Tech's default brain Loadout
				self:SetOverridePurchaseList("Infantry Brain", player)
				-- Create the actual delivery so it'll come down soon
				self:CreateDelivery(player)
			end
		end
	end
end


-----------------------------------------------
-- Find the best LZ possible
function MetaFight:FindLZ(myTeam, OccupiedLZs, SceneLZs, ignoreUnseen)
	-- Assing a value to each XY position on the map depending on how many units have LOS to it
	local BrainPoints = {}
	local LOSgrid = {}
	local Dist, SeePos, mag, range
	local Free = Vector()
	local size = 40
	for Act in MovableMan.Actors do
		if Act.Team ~= myTeam and Act.ClassName ~= "ADoor" then
			if not ignoreUnseen or not SceneMan:IsUnseen(Act.Pos.X, Act.Pos.Y, myTeam) then	-- don't calculate the LOS for unseen actors if ignoreUnseen is true 
				-- Cast rays in all directions (if the actor is visible to this team)
				for ang = -1, 4.14, 0.15 do
					if 0 ~= SceneMan:CastObstacleRay(Act.EyePos, Vector(FrameMan.PlayerScreenWidth, 0):RadRotate(ang), Vector(), Free, Act.ID, Act.IgnoresWhichTeam, rte.grassID, 7) then
						Dist = SceneMan:ShortestDistance(Act.EyePos, Free, false)
						mag = Dist.Magnitude
						for i = 10, mag, size do
							range = i / mag
							SeePos = Act.EyePos + Dist * range
							SeePos.X = math.floor(SeePos.X/size+0.5) * size
							SeePos.Y = math.floor(SeePos.Y/size+0.5) * size
							if not LOSgrid[SeePos.X] then
								LOSgrid[SeePos.X] = {}
							end
							
							-- Assign a score to this XY-position, higher means worse LZ
							LOSgrid[SeePos.X][SeePos.Y] = (LOSgrid[SeePos.X][SeePos.Y] or 0) + (2-range)
						end
					end
				end
			end
			
			-- Don't land above enemy brains since they can deliver things ontop of us
			if Act:IsInGroup("Brains") then
				table.insert(BrainPoints, {x=math.floor(Act.Pos.X/size+0.5) * size, team=Act.Team})
			end
		end
	end
	
	-- Cast rays down towards the ground and caluculate how visible our descent is to the enemy
	local LZs = {}
	local Hit = Vector()
	local height = math.floor((SceneMan.SceneHeight-size)/size)*size
	local score
	for x = 0, SceneMan.SceneWidth, size do
		if SceneMan:CastObstacleRay(Vector(x, 0), Vector(0, height), Hit, Vector(), 0, -1, rte.grassID, 7) > size*2 then
			score = 0
			if LOSgrid[x] then
				for y = 0, Hit.Y, size do	
					if LOSgrid[x][y] then
						score = score + LOSgrid[x][y]
					end
				end
			end
			
			LZs[#LZs+1] = {Pos=Vector(x, Hit.Y), score=score}
		else
			LZs[#LZs+1] = {Pos=Vector(x, 0), score=2}	-- This LZ is not accessible, so it might be dangerous to try to land next to it
		end
	end
	
	-- Inspect all potential LZs and adjust the score depending on adjacent LZs
	local weight = 0.8	-- n% of the new score come from the LZ's own score, the rest from the neighbours
	local NextLZ
	for i = 2, #LZs do
		if LZs[i+1] then
			NextLZ = LZs[i+1]
		else
			NextLZ = LZs[1]
		end
		
		LZs[i].score = LZs[i-1].score*(1-weight)/2 + LZs[i].score*weight + NextLZ.score*(1-weight)/2
		
		-- Adjust score for LZ "flattnes"
		LZs[i].score = LZs[i].score + (math.abs(LZs[i-1].Pos.Y - LZs[i].Pos.Y) - 20) / 200	-- a Y difference above 20 reduce the chance of picking this LZ
		LZs[i].score = LZs[i].score + (math.abs(NextLZ.Pos.Y - LZs[i].Pos.Y) - 20) / 200
	end
	
	if OccupiedLZs and #OccupiedLZs > 0 then
		local distance, worst
		for i = 1, #LZs do
			worst = -100
			
			-- Increase the score around any existing LZs to avoid collisions
			for _, OcLZ in pairs(OccupiedLZs) do
				distance = SceneMan:ShortestDistance(LZs[i].Pos, Vector(OcLZ.x, LZs[i].Pos.Y), false).Largest / SceneMan.SceneWidth
				if OcLZ.team == myTeam then
					worst = math.max(worst, LZs[i].score + 10 * math.exp(-(distance*distance)/0.05))	-- Increase the score around the LZ
				else
					worst = math.max(worst, LZs[i].score + 15 * math.exp(-(distance*distance)/0.2))	-- Increase the score over the whole scene, but with an exponential drop off
				end
			end
			
			-- Increase the score around any resident brains to avoid sharing LZ
			for _, OcLZ in pairs(BrainPoints) do
				distance = SceneMan:ShortestDistance(LZs[i].Pos, Vector(OcLZ.x, LZs[i].Pos.Y), false).Largest / SceneMan.SceneWidth
				if OcLZ.team == myTeam then
					worst = math.max(worst, LZs[i].score + 3 * math.exp(-(distance*distance)/0.05))	-- Increase the score around the brain
				else
					worst = math.max(worst, LZs[i].score + 6 * math.exp(-(distance*distance)/0.2))	-- Increase the score over the whole scene, but with an exponential drop off
				end
			end
			
			LZs[i].score = worst
		end
	end
	
	-- Pick one of the four best LZs at random
	table.sort(LZs, function(A, B) return A.score > B.score end)
	local BestLZ
	for i = 1, math.random(4) do
		BestLZ = table.remove(LZs)
	end
	
	if BestLZ then
		return BestLZ.Pos.X
	end
	
	-- No safe LZ found; pick a LZ from the scene LZ-list and grab a random X-pos from it
	if SceneLZs then
		local tmpLZ = table.remove(SceneLZs, math.random(#LZs))
		if tmpLZ then
			return math.floor((tmpLZ:GetRandomPoint()).X/size+0.5) * size
		end
	end
	
	-- If everyting else fails:
	return math.floor(math.random(SceneMan.SceneWidth)/size+0.5) * size	-- There are no pre-defined LZs left in the list, pick a point at random
end
