-- ######################################## --
-- ##           Survival *EXtreme*						## --
-- ##           Game Logic Script							## -- 
-- ##           (c) tecxx@rrs.at							## --
-- ##           29.11.07								## --
-- ######################################## --

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- imports and variables
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
local ScenarioUtils = import('/lua/sim/ScenarioUtilities.lua');
local ScenarioFramework = import('/lua/ScenarioFramework.lua');
local Utilities = import('/lua/utilities.lua');
local Entity = import('/lua/sim/Entity.lua').Entity;

-- global vars
local gameEnd = false;		-- true when game is over
local beatTime = 1;			-- seconds to wait between game logic calculations
local mv = nil;				-- pointer to mavor
local swActivated = false;	-- is superweapon activated?
local playercount = 0;		-- how many human players?

-- survival mode
local loopEnd = false;		-- true if all waves were spawned
local waveid = 0;			-- which wave gets spawned, starts at 0
local loop = 0;				-- which loop are we playing, starts at 0
local numGatesOpen = 0; 	-- how many spawn gates are alive
local gates = {nil, nil, nil, nil, nil, nil, nil, nil};	-- gate pointers
local numSpecialsOpen = 0;	-- how many special weapons are alive
local specials = {nil, nil, nil, nil}; -- special pointers

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- engine invoked main functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-- this function is called once before game start, it does some player initialization
function OnPopulate()
	-- make default settings work
	if (ScenarioInfo.Options.opt_difficulty == nil) then
		ScenarioInfo.Options.opt_difficulty = 1.00;
	end
	if (ScenarioInfo.Options.opt_startupTime == nil) then
		ScenarioInfo.Options.opt_startupTime = 90;
	end
	if (ScenarioInfo.Options.opt_waveTime == nil) then
		ScenarioInfo.Options.opt_waveTime = 60;
	end
	if (ScenarioInfo.Options.opt_numberRounds == nil) then
		ScenarioInfo.Options.opt_numberRounds = 1;
	end
	
	
	ScenarioUtils.InitializeArmies();
	
	-- count number of players
	local armies = ListArmies();
	playercount = table.getn(armies) - 2;	-- num armies minus 2 ai players
	LOG("___SE___ PlayerCount: "..playercount);
	
	
	-- set ai stuff TODO
	--import('/lua/aibrain.lua').OnCreateAI = function(self, planName) end
	--import('/lua/aibrain.lua').ExecuteAIThread = function(self) end
	--import('/lua/aibrain.lua').EvaluateAIPlanList = function(self) end
	--import('/lua/aibrain.lua').ExecutePlan = function(self) end
	--import('/lua/AI/aiarchetype-easy.lua').ExecutePlan = function(self) end
	--import('/lua/AI/aiarchetype-medium.lua').ExecutePlan = function(self) end
	--import('/lua/AI/aiarchetype-balanced.lua').ExecutePlan = function(self) end	
end

-- start of map script
function OnStart(self)
	-- initiate game modes and spawn players
	setupGame();
	
	-- start the game logic
	startScenarioThreads();
	
	-- set score calculation to show total kills
	for index, brain in ArmyBrains do
		brain.CalculateScore = function(thisBrain)
			return armyKills(ArmyIndexer(thisBrain));
		end
	end 
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- main game logic
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

--creates the different scenario threads 
startScenarioThreads = function()
	--start main thread
	local mainThread = ForkThread(function(self)
		LOG("___SE___ Main thread running!");
	
		while (gameEnd == false) do
			--wait time between each beat
			WaitSeconds(beatTime);
			-- run game logic
			gameLogic();
		end
		
		KillThread(self);
    end);
	
	--this creates a new spawn thread,, only in survival mode
	local spawnThread = ForkThread(spawnThread);
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- survival mode
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- unit tables, seperated by air/ground
local unitTableAir = {
	{ -- tech1
		'UAA0103',	-- aeon t1 bomber
		'UAA0102',	-- aeon interceptor
		'URA0103',	-- cybran bomber
		'URA0103',	-- cybran interceptor
		'UEA0103',	-- uef bomber
		'UEA0102',	-- uef interceptor
		
		'xra0105', 	-- cybran t1 light gunship
		'xsa0103',	-- seraphim bomber
		'xsa0102',	-- seraphim interceptor
		
	}, 
	{ -- tech2
		'UAA0203',	-- aeon gunship
		'URA0203',	-- cybran gunship
		'UEA0203',	-- uef gunship
		'DRA0202',	-- cybran corsair
		'DEA0202',	-- uef janus
		
		'xaa0202', 	-- aeon t2 combat fighter
		'xsa0202',	-- seraphim fighter/bomber
		'xsa0203',	-- seraphim gunship
		
	}, 
	{ -- tech3
		'UAA0303',	-- aeon air superiority
		'UAA0304',	-- aeon t3 bomber
		'URA0303',	-- cybran air superiority
		'URA0304',	-- cybran t3 bomber
		'UEA0303',	-- uef air superiority
		'UEA0304',	-- uef t3 bomber
		'UEA0305',	-- uef t3 gunship
		
		'xaa0305', 	-- aeon T3 AA gunship
		'xra0305', 	-- cybran T3 gunship
		'xsa0303',	-- seraphim air superiority
		'xsa0304',	-- seraphim bomber
	}, 
	{ -- tech4
		'xea0306',	-- UEF t3 heavy air transport
	}, 
	{ -- tech 5
		'URA0401', -- soul ripper
		'xsa0402', -- seraphim exp bomber
                'vaa0305', -- Aeon Gunship
	        'vsa0203', -- seraphim Gunship
                'vra0304', -- Cybran exp bomber
         	'vua0304', -- UEF exp bomber
	}, 
};

local unitTableGround = {
	{ -- tech 1
		'UAL0101', -- aeon scout
		'UAL0106', -- aeon light assault
		'UAL0201', -- aeon light tank
		'UAL0104', -- aeon t1 aa
		'UAL0103', -- aeon t1 arty
		'URL0101', -- cybran scout
		'URL0106', -- cybran light assault
		'URL0107', -- cybran heavy assault
		'URL0104', -- cybran t1 aa
		'URL0103', -- cybran t1 arty
		'UEL0101', -- uef scout
		'UEL0106', -- uef light assault
		'UEL0201', -- uef medium tank
		'UEL0104', -- uef t1 aa
		'UEL0103', -- uef t1 arty	
		
		'xsl0101', -- seraphim combat scout
		'xsl0201', -- seraphim medium tank
		'xsl0104', -- seraphim anti air
		'xsl0103', -- seraphim light artillery
	},
	{ -- tech 2
		'UAL0202', -- aeon t2 tank
		'UAL0111', -- aeon t2 missile launcher
		'UAL0307', -- aeon t2 shield
		'UAL0205', -- aeon t2 aa
		'URL0202', -- cybran t2 tank
		'URL0203', -- cybran t2 amphibious tank
		'URL0111', -- cybran t2 missile launcher
		'URL0306', -- cybran t2 stealth
		'URL0205', -- cybran t2 aa
		'UEL0202', -- uef t2 tank
		'UEL0203', -- uef t2 amphibious tank
		'UEL0111', -- uef t2 missile launcher
		'UEL0307', -- uef t2 shield
		'UEL0205', -- uef t2 aa	
		'DRL0204', -- cybran t2 rocket
		'DEL0204', -- uef t2 gatling
		
		'xal0203', -- aeon t2 assault tank
		'xrl0302', -- cybran mobile bomb
		'xsl0202', -- seraphim t2 assault bot
		'xsl0203', -- seraphim t2 hover tank
		'xsl0205', -- seraphim t2 anti air
		'xsl0111', -- seraphim t2 mobile missile
		
	},
	{ -- tech 3
		'UAL0303', -- aeon t3 assault bot
		'URL0303', -- cybran t3 assault bot
		'UEL0303', -- uef t3 assault bot
		'DAL0310', -- aeon shield disruptor	
		'xal0305', -- aeon sniper bot
		'xrl0305', -- cybran t3 armored assault bot
		'xel0305', -- uef t3 armored assault bot
		'xel0306', -- uef t3 mobile missile platform
		'xsl0307', -- seraphim t3 mobile shield gen
		'xsl0303', -- seraphim t3 siege tank
		'xsl0305', -- seraphim t3 sniper bot
		
	},
	{ -- tech 4
 

		'vel0101', -- UEF T4 Scout
		'val0303', -- Aeon T4 Harb
		'vsl0305', -- seraphim T4 bot
	},
	{ -- tech 5
		'val0001',	-- Aeon T4 ACU
		'val0001',	-- Cybran T4 ACU
		'UAL0401', -- aeon galactic colossus
		'URL0402', -- cybran monkeylord
		'UEL0401', -- uef fatboy	
		'xrl0403',	-- cybran experimental megabot
		'xsl0401',	-- seraphim experimental assault bot

		--'URL0401', -- cybran scathis disabled for now - it's too strong

		
	},
};

-- special units, get spawned at end of loop
local unitTableSpecials = {
	{
		-- artilleries
		'uab2302', -- Aeon emissary
		'xab2307', -- Aeon salvation
		'urb2302', -- cybran disruptor
		'ueb2302', -- UEF duke
		'ueb2401', -- UEF mavor
		'xsb2302', -- seraphim hovatham

		-- nukes
		'xsb2305', -- seraphim hastue
		'xsb2401', -- seraphim yolana oss
		'ueb2305', -- UEF Stonager
		'urb2305', -- Cybran Liberator
		'uab2305', -- Aeon apocalypse
	}, 
};



-- defines possible formations for the units
local formationTable = {
	'AttackFormation',
	'GrowthFormation',
	'SixWideFormation',
	'TravellingFormation',
};

-- this table defines the wave combos: spawn how many techlevel = {ground, air}  units
local waveTable = {
--	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
-- round 1
	{t1 = { 5,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {10,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {15,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {15,  0}, t2 = { 1,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 6
	{t1 = { 0, 0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {15,  0}, t2 = { 0,  0} , t3 = { 3,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {15,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {15,  0}, t2 = { 2,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 11
	{t1 = { 5, 10}, t2 = { 4,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {20, 10}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = { 2,  0}, t2 = { 5,  0} , t3 = { 5,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {20,  5}, t2 = { 5,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 16
	{t1 = {20,  0}, t2 = {10,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {20,  0}, t2 = {15,  0} , t3 = { 7,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = { 0, 10}, t2 = { 0,  0} , t3 = { 6,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {20,  0}, t2 = { 5,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 21	
	{t1 = {20,  5}, t2 = {15,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {20,  0}, t2 = {20,  0} , t3 = { 0,  0}, t4 = { 5,  0}, t5 = { 0,  0}},
	{t1 = {20,  0}, t2 = {20,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = { 0, 10}, t2 = { 0,  5} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 26	
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 5,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = {20,  0} , t3 = { 5,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = {10,  0}, t4 = { 6,  1}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = {20,  0} , t3 = {10,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 31
	{t1 = { 0,  0}, t2 = { 0,  2} , t3 = { 0,  5}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {30,  0}, t2 = { 0,  0} , t3 = { 5,  0}, t4 = { 7,  0}, t5 = { 0,  0}},
	{t1 = {30,  0}, t2 = {10,  0} , t3 = {10,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = {30,  0}, t2 = { 0,  0} , t3 = {15,  0}, t4 = { 0,  1}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 36
	{t1 = {30,  0}, t2 = {10,  0} , t3 = {20,  0}, t4 = { 0,  0}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = { 0,  5} , t3 = { 0, 10}, t4 = { 8,  0}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 5,  0}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = { 5,  0} , t3 = { 5,  0}, t4 = { 5,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 41
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 5,  1}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = { 5,  0} , t3 = { 0,  0}, t4 = {10,  0}, t5 = { 0,  0}},
	{t1 = { 0, 10}, t2 = { 0, 10} , t3 = { 0, 12}, t4 = { 8,  1}, t5 = { 2,  0}},
	{t1 = {10,  0}, t2 = { 0,  0} , t3 = { 5,  0}, t4 = { 5,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 46
	{t1 = {10,  0}, t2 = { 5,  0} , t3 = {10,  0}, t4 = { 5,  1}, t5 = { 0,  0}},
	{t1 = {10,  0}, t2 = { 5,  0} , t3 = {20,  0}, t4 = { 8,  0}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = {10,  0}, t4 = {10,  0}, t5 = { 3,  0}},
	{t1 = { 0, 10}, t2 = { 0, 10} , t3 = { 0, 15}, t4 = { 0,  1}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 51
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 1,  0}},
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  1}, t5 = { 4,  1}},
	{t1 = { 0,  0}, t2 = {10,  0} , t3 = { 0,  0}, t4 = { 1,  0}, t5 = { 0,  0}},
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 5,  0}, t4 = { 5,  0}, t5 = { 0,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},

-- round 55
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 0,  0}, t4 = { 0,  0}, t5 = { 2,  0}},
	{t1 = { 0,  0}, t2 = { 0,  0} , t3 = { 0, 10}, t4 = { 0,  0}, t5 = { 0,  2}},
	{t1 = { 0,  0}, t2 = { 5,  0} , t3 = { 0,  0}, t4 = { 1,  0}, t5 = { 5,  0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},
	{t1={1,0},t2={0,0},t3={0,0},t4={0,0},t5={0,0}},
}

setupGame = function()
	ScenarioInfo.Options.Victory = 'sandbox'; 			-- custom victory condition
	-- misc settings
	Utilities.UserConRequest("ui_ForceLifbarsOnEnemy");	--show enemy life bars

	-- setup army specific things
	for i, army in ListArmies() do
		if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4") then 

			-- set alliances
			SetAlliance(army, "ARMY_SURVIVAL_ENEMY", 'Enemy'); 
			SetAlliance(army, "ARMY_SUPERWEAPON", 'Ally'); 
			SetAlliedVictory(army, true); 
			-- set alliance with all other hill players!
			for j, army2 in ListArmies() do
				if (army2 == "ARMY_1" or army2 == "ARMY_2" or army2 == "ARMY_3" or army2 == "ARMY_4") then 
					SetAlliance(army, army2, 'Ally'); 
				end
			end			
		end
	end

	-- spawnwaves and superweapon are enemies
	SetAlliance("ARMY_SURVIVAL_ENEMY", "ARMY_SUPERWEAPON", 'Enemy'); 
	-- spawnwaves don't have a unit cap
	SetIgnoreArmyUnitCap('ARMY_SURVIVAL_ENEMY', true);
	
	-- create the mavor in the middle
	local pos = ScenarioUtils.MarkerToPosition('HILL_CENTER');
	local rotation = (math.pi/2) * (Utilities.GetRandomInt(1,4)-1);
	mv = CreateUnitHPR( 'UEB2401', "ARMY_SUPERWEAPON", pos[1], pos[2], pos[3], 0,rotation,0);
	mv:SetMaxHealth(100000);
	mv:SetHealth(nil,100000);
	mv:SetReclaimable(false);
	mv.OldOnKilled = mv.OnKilled;
	-- setup weapon to be a mission objective
	mv.OnKilled = function(self, instigator, type, overkillRatio)
		msgOut("Mavor has been destroyed! You have lost.");
		mv.OldOnKilled(self, instigator, type, overkillRatio);
		mv = nil; -- remove pointer
		gameEnd = true;
		-- spawn a nuke
		nuke(self:GetPosition());
		-- now defeat all the hill players commanders
		for i, army in ListArmies() do
			killAllCommanders(army);
		end
		-- make sure game ends
		local defeatThread = ForkThread(function(self)
			WaitSeconds(10);
			for i, army in ListArmies() do
				if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4" or
				    army == "ARMY_5" or army == "ARMY_6" or army == "ARMY_7" or army == "ARMY_8") then
					GetArmyBrain(army):OnDefeat();
				end
			end
			KillThread(self);
		end);		
	end
	
	-- adjust weapon to be really brutal
	local wp = mv:GetWeapon(1);
	--wp:ChangeRateOfFire(1);			-- shoots ultra fast
	wp:SetWeaponEnabled(false);		-- disable it for now
	local bp = wp:GetBlueprint();
	bp.EnergyDrainPerSecond = 0;	-- doesn't drain energy
	bp.EnergyRequired = 0;			-- doesn't need energy
	
	-- spawn the gates
	spawnGates();
	--spawnSpecials();
end

spawnGates = function()
	-- spawn gates at the outer edges for enemy units to teleport in
	-- 8 gates have to be destroyed
	numGatesOpen = 8;
	for i = 1,8 do
		local gateName = "Gate"..i;
		local pos = ScenarioUtils.MarkerToPosition(gateName);
		local r = Utilities.GetRandomInt(1,4);
		local gateFaction;
		if (r == 1) then
			gateFaction = 'UAB0304';
		elseif (r == 2) then
			gateFaction = 'URB0304';
		elseif (r == 3) then
			gateFaction = 'UEB0304';
		else
			gateFaction = 'XSB0304';
		end
		gates[i] = CreateUnitHPR( gateFaction, "ARMY_SURVIVAL_ENEMY", pos[1], pos[2], pos[3], 0,0,0);
		gates[i]:SetMaxHealth(10000000);
		gates[i]:SetHealth(nil, 10000000);
		gates[i]:SetReclaimable(false);
		gates[i]:SetCapturable(false);
		gates[i]:SetProductionPerSecondEnergy(99999);
		
		-- set gate onkilled function
		gates[i].OldOnKilled = gates[i].OnKilled;
		gates[i].myID = i;
		gates[i].OnKilled = function(self, instigator, type, overkillRatio)
			--spawn a nuke
			nuke(self:GetPosition());

			-- handle game logic
			numGatesOpen = numGatesOpen - 1;
			msgOut("A spawngate was destroyed by " .. getUsername(instigator:GetArmy()) .. "! "..numGatesOpen.. " gates left.");
			self.OldOnKilled(self, instigator, type, overkillRatio);
			gates[self.myID] = nil;
			LOG("___SE___ GATE KILLED. iD="..self.myID);
			if (numGatesOpen == 0) then
				msgOut("All gates were destroyed!");

			end
		end
	end
end

spawnSpecials = function()
	-- spawn t3 artillery at the 4 outer edges of the map per player
	numSpecialsOpen = numSpecialsOpen + playercount;
	for i = 1,playercount do
		local specialName = "Special"..i;
		local pos = ScenarioUtils.MarkerToPosition(specialName);
		
		local bpCount = table.getn(unitTableSpecials[1]);
		local rr = Utilities.GetRandomInt(1, bpCount);
		local special = unitTableSpecials[1][rr];

		specials[i] = CreateUnitHPR( special, "ARMY_SURVIVAL_ENEMY", pos[1], pos[2], pos[3], 0,0,0);
		specials[i]:SetMaxHealth(500000);
		specials[i]:SetHealth(nil, 500000);
		specials[i]:SetReclaimable(false);
		specials[i]:SetCapturable(false);
		specials[i]:SetProductionPerSecondEnergy(99999);
		
		-- set special onkilled function
		specials[i].OldOnKilled = specials[i].OnKilled;
		specials[i].myID = i;
		specials[i].OnKilled = function(self, instigator, type, overkillRatio)
			--spawn a nuke
			nuke(self:GetPosition());

			-- handle game logic
			numSpecialsOpen = numSpecialsOpen - 1;
			msgOut("Enemy weapon was destroyed by " .. getUsername(instigator:GetArmy()) .. "! "..numSpecialsOpen.. " weapons left.");
			self.OldOnKilled(self, instigator, type, overkillRatio);
			specials[self.myID] = nil;
			LOG("___SE___ SPECIAL KILLED. iD="..self.myID);
		end
		
		-- if it's a nuke, fill it with missiles
		if (special == 'xsb2305' or special == 'xsb2401' or special == 'ueb2305' or special == 'urb2305' or special == 'uab2305') then
			specials[i]:GiveNukeSiloAmmo(5);
			-- todo fire the nuke
		end
	end
end

gameLogic = function()
	-- check if all gates and specials were destroyed
	if (numGatesOpen == 0 and numSpecialsOpen == 0) then
		-- all gates down, display victory and end game
		for i, army in ListArmies() do
			if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4" or
			    army == "ARMY_5" or army == "ARMY_6" or army == "ARMY_7" or army == "ARMY_8") then
				GetArmyBrain(army):OnVictory();
			end
		end
		GetArmyBrain("ARMY_SURVIVAL_ENEMY"):OnDefeat();
		gameEnd = true;
		return;
	end
	
	-- update mavor timer
	if (mv != nil and swActivated == false) then
		local roundsleft = table.getn(waveTable) - waveid;
		mv:SetCustomName('Waves left: '..roundsleft);
	end

end

spawnThread = function(self)
	LOG("___SE___ Spawn thread running!");
	-- wait before starting the unit spam
	local startupTime = ScenarioInfo.Options.opt_startupTime;
	local now = 0;
	while (now < startupTime) do
		now = GetGameTimeSeconds();
		local rest = startupTime - now;
		if (rest < 0) then rest = 0; end
		Sync.ObjectiveTimer = math.floor(rest);
		WaitSeconds(1);
	end
	msgOut("Attack waves have spawned!");
	--start sending the units
	while (gameEnd == false) do 
		-- check superweapon activation  condition
		if (loop >= ScenarioInfo.Options.opt_numberRounds) then
			activateSuperWeapon();
			mv:SetCustomName('Activated!');	
		end
		-- start at wave 1 in loop0, wave 20 in loop 1, wave 30 in loop 2 etc...
		if (loop == 0) then
			waveid = 1;
		elseif (loop == 1) then
			waveid = 20;
		elseif (loop == 2) then
			waveid = 30;
		else
			waveid = 40;
		end
		-- spawn the waves
		spawnWaves();
		-- next loop
		loop = loop + 1;
		loopEnd = false;
		-- at loop end spawn the special weapons
		spawnSpecials();
	end
	--no more waves to send, kill thread
	KillThread(self);
end


-- iterates through wavetable and calls unit spawn function
spawnWaves = function()
	while (loopEnd == false and gameEnd == false) do
		--if the wave exists
		if (waveid > table.getn(waveTable)) then
			loopEnd = true;
			return;
		end

		LOG("___SE___ sending in	wave number: " .. waveid);
		Sync.ObjectiveTimer = table.getn(waveTable)-waveid;
		if (math.mod(waveid, 5) == 0) then
			msgOut("Attack wave "..waveid.." has spawned!");
		end
		
		--spawn one wave per player
		for i = 1,playercount do
			spawnWave();
		end

		--wait time until next wave
		local waitTime = ScenarioInfo.Options.opt_waveTime;
		if (loop > 0) then
			waitTime = waitTime / 2;
		end
		WaitSeconds(waitTime);
		waveid = waveid+1;

	end
end

spawnWave = function()
	-- fetch wave data
	local wave = waveTable[waveid];
	-- end if end of wavetable reached
	if (wave == nil) 		then return; end
	-- if no more gates are open, do nothing
	if (numGatesOpen == 0)	then return; end	
	
	-- try to fetch a random gate and it's position
	local pos = nil;	-- position of the gate
	local tryCount = 0;	-- don' try more than 8 times, as there are only 8 gates
	local r = Utilities.GetRandomInt(1,8);	-- start with a random gate
	while (true) do
		local gateName = "Gate"..r;
		if (gates[r] != nil) then
			pos = ScenarioUtils.MarkerToPosition(gateName);
			break;	-- we found a valid gate
		end
		-- gate is already dead, increase trycount
		tryCount = tryCount + 1;
		if (tryCount > 8) then 	-- no open gates. stop trying.
			return;
		end
		-- try next gate
		r = r + 1;
		if (r > 8) then
			r = 1;
		end
	end
	if (pos == nil) then
		LOG("___SE___ error: trying to spawn at nil gate");
		return;
	end
	-- create a list of possible blueprint files we can to spawn
	local spawnList = {};
	
	-- go through the techlevels, as described in the wave table
	-- table entry contains: 	{t1 = {10, 0}, t2 = {0,0} , t3 = {0, 0}, t4 = {0, 0}, t5 = {0, 0},},
	for techlevel = 1,5 do
		local numGroundAir = wave["t"..techlevel];
		local numGround = math.ceil(numGroundAir[1] * ScenarioInfo.Options.opt_difficulty);
		local numAir = math.ceil(numGroundAir[2] * ScenarioInfo.Options.opt_difficulty);
		-- ground units
		local bpCount = table.getn(unitTableGround[techlevel]);
		for c = 1, numGround do
			local rr = Utilities.GetRandomInt(1, bpCount);
			table.insert(spawnList, unitTableGround[techlevel][rr]);
		end		
		-- air units
		local bpCount = table.getn(unitTableAir[techlevel]);
		for c = 1, numAir do
			local rr = Utilities.GetRandomInt(1, bpCount);
			table.insert(spawnList, unitTableAir[techlevel][rr]);
		end
	end
		
	-- create an empty array to combine spawned units to one platoon
	local platoonUnits = {};
	-- iterate through the blueprint-list we have just generated and spawn the units
	for index, bp in spawnList do
		doSpawnUnit(bp, pos, platoonUnits);
	end		
	--give attack order
	startAttackMove(platoonUnits);
end

-- does the actual spawning of a unit
doSpawnUnit = function(bp, pos, platoon)
	--calculate offset position of unit
	local xside = math.random(0,1);
	local yside = math.random(0,1);
	local xoff = -10 + xside*16 + math.random(0,1)*4;
	local yoff = -10 + yside*16 + math.random(0,1)*4;

	-- spawn unit
	local unit = CreateUnitHPR( bp, "ARMY_SURVIVAL_ENEMY", pos[1]+xoff, pos[2], pos[3]+yoff, 0,0,0);		
	if (unit == nil) then
		return;
	end
	-- no wreckages after first round (to speed up game, can be removed when we all have quadcores/octacores ;=)
	if (loop > 0) then
		local bp = unit:GetBlueprint();
		if (bp != nil) then
			bp.Wreckage = nil; -- bp.Wreckage.WreckageLayers.Land=false;
		end
	end

	-- insert to platoon list
	table.insert(platoon, unit); 
	
	
	-- check for upgrades
	if (bp == "UAL0301") then		-- aeon
		local ucnt = table.getn(upgradeTable["A"]);
		local enh = upgradeTable["A"][Utilities.GetRandomInt(1, ucnt)];
		unit:CreateEnhancement(enh);
	elseif (bp == "URL0301") then	-- cybran
		local ucnt = table.getn(upgradeTable["R"]);
		local enh = upgradeTable["R"][Utilities.GetRandomInt(1, ucnt)];
		if (enh == 'StealthGenerator') then	-- stealth needs cloaking first, bug in game
			LOG("___SE___ enhancing cybran scu with: ".. enh);
			unit:CreateEnhancement('CloakingGenerator');
		end
		unit:CreateEnhancement(enh);
	elseif (bp == "UEL0301") then	-- uef
		local ucnt = table.getn(upgradeTable["E"]);
		local enh = upgradeTable["E"][Utilities.GetRandomInt(1, ucnt)];
		unit:CreateEnhancement(enh);
	elseif (bp == "xsl0301") then	-- seraphim
		local ucnt = table.getn(upgradeTable["S"]);
		local enh = upgradeTable["S"][Utilities.GetRandomInt(1, ucnt)];
		unit:CreateEnhancement(enh);
	end
				
	--apply buffs based on round count and difficulty
	local loopBuff = loop*5;
	if (loopBuff == 0) then loopBuff = 1; end
	local difficultyBuff = ScenarioInfo.Options.opt_difficulty;
	unit:SetMaxHealth(unit:GetHealth()*loopBuff*difficultyBuff);
	unit:SetHealth(nil, unit:GetHealth()*loopBuff*difficultyBuff);
end


--forms a platoon to attack the hill with a given set of units
startAttackMove = function(units)	
	if (table.getn(units) > 0) then
		local aiBrain = GetArmyBrain("ARMY_SURVIVAL_ENEMY");
		local attackPlatoon = aiBrain:MakePlatoon('','');
		local fcnt = table.getn(formationTable);
		local formation = formationTable[Utilities.GetRandomInt(1, fcnt)];
		aiBrain:AssignUnitsToPlatoon(attackPlatoon, units, 'Attack', formation);
		-- add a random waypoint
		local r = Utilities.GetRandomInt(1,8);
		local waypointName = "WP"..r;
		local posWP = ScenarioUtils.MarkerToPosition(waypointName);
		attackPlatoon:AggressiveMoveToLocation(posWP);

		local posCENTER = ScenarioUtils.MarkerToPosition('HILL_CENTER');
		attackPlatoon:AggressiveMoveToLocation(posCENTER);
	end
end
	
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- superweapon funcs
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-- activate superweapon
activateSuperWeapon = function()
	swActivated = true;
	Sync.ObjectiveTimer = 0;
	if (mv == nil) then	return;	end
	local wp = mv:GetWeapon(1);
	if (wp == nil) then	return;	end
	wp:SetWeaponEnabled(true);		-- fire away!
	msgOut("The Cake was activated!");
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- misc funcs
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-- spawn acu and kill it to create a fake nuke explosion :=) *hack hack hack, but the gpg devs said it's easiest way atm*
nuke = function(position)
	local acu = CreateUnitHPR( "ual0001", "ARMY_SURVIVAL_ENEMY", position[1], position[2], position[3], 0,0,0);
	if (acu != nil) then
		acu:Kill();
	end
end

-- kills all commanders of a specified army
killAllCommanders = function(army)
	local list = GetArmyBrain(army):GetListOfUnits(categories.COMMAND , false);
	for i, u in list do
		if (u:IsDead() == false) then
			u:Kill();
		end
	end
end


--given a player returns a proper username
getUsername = function(army)
	return GetArmyBrain(army).Nickname;
end

--given an ai brain returns an army index
function ArmyIndexer(aiBrain)
	local army = aiBrain:GetArmyIndex();
	for i, v in ListArmies() do
		if (i == army) then
			return v;
		end
	end
end

--given a player returns the number of kills
armyKills = function(army)
	local kills = GetArmyBrain(army):GetArmyStat('Enemies_Killed',0.0).Value;
	return kills;
end

-- display mm:ss instead of seconds
secondsToTime = function(seconds)
	return string.format("%02d:%02d", math.floor(seconds/60), math.mod(seconds, 60));
end

-- display a text message
msgOut = function(text, army)
	local color = nil;
	if (army != nil) then
		local colorIndex = ScenarioInfo.ArmySetup[army].PlayerColor;
		color = import('/lua/GameColors.lua').GameColors.ArmyColors[colorIndex];
	end
	PrintText(text, 20, color, 10, 'center') ;	
end

--EOF