--Game Logic Script and initial version by
--(c) tecxx@rrs.at
--29.11.07
--Survival *EXtremeDesertV3fA*	edited by Typo91
--Survival Campaign v1c Map and additions to skript by Cobaltur
--inspired by BrockSamson Insane Extension
--inspired by Extreme Script

---   difficulty   5 is normal   good to know

-- 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
local Cinematics = import('/lua/cinematics.lua')
local tvEn
local OpStrings
-------

local BaseManager = import('/lua/ai/opai/basemanager.lua')
local Objectives = import('/lua/ScenarioFramework.lua').Objectives
local PingGroups = import('/lua/ScenarioFramework.lua').PingGroups
local ScenarioPlatoonAI = import('/lua/ScenarioPlatoonAI.lua')
local TauntManager = import('/lua/TauntManager.lua')
local Explosions = import('/lua/defaultexplosions.lua')

ScenarioInfo.ARMY_1 = 1
ScenarioInfo.ARMY_2 = 2
ScenarioInfo.ARMY_3 = 3
ScenarioInfo.ARMY_4 = 4

-- global vars
local gameEnd = false		-- true when game is over
local beatTime = 1			-- seconds to wait between game logic calculations
local playercount = 0		-- how many human players?

-- survival mode
local HealthBuff = 1 -- health multiplier    --- what for bugs
local numGatesOpen = 0 	-- how many spawn gates are alive
local gates = {nil, nil, nil, nil, nil}	-- gate pointers
local numSpecialsOpen = 0	-- how many special weapons are alive
local specials = {nil, nil, nil, nil} -- special pointers
local antinukes = {nil, nil, nil, nil}
local killCount = { ARMY_1 = 0,		-- number of gates/specials killed by each player+2 ai's
    ARMY_2 = 0,
    ARMY_3 = 0,
    ARMY_4 = 0,
    ARMY_SUPERWEAPON = 0,
ARMY_SURVIVAL_ENEMY = 0}
local lvlmod = 1

-- as we now make an objective line it would have been easier to set these values in the mission steps
local platoonsize -- depends on difficult level up to level 12
local finalrif_a -- also accessible as gate4
local finalrif_b -- also accessible as gate5
local ShowDialogs = 20

-- 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 = 20
    end
    if (ScenarioInfo.Options.NoRushOption != 'Off') then
       ScenarioInfo.Options.opt_startupTime = tonumber(ScenarioInfo.Options.NoRushOption) * 60
    end
    if (ScenarioInfo.Options.opt_startupTime == nil) then
        ScenarioInfo.Options.opt_startupTime = 360
    end
    if (ScenarioInfo.Options.opt_waveTime == nil) then
        ScenarioInfo.Options.opt_waveTime = 30
    end
    if (ScenarioInfo.Options.opt_defenseObject == nil) then
        ScenarioInfo.Options.opt_defenseObject = 2
    end
    ScenarioUtils.InitializeArmies()
    ScenarioFramework.SetPlayableArea('AREA_1', false)
    -- count number of players
    local armies = ListArmies()
    playercount = table.getn(armies) - 2	-- num armies minus 2 ai players
--    LOG("___SE___ PlayerCount: "..playercount)
end


-- start of map script
function OnStart(self)
    -- initiate game modes and spawn players
    setupGame()
    ForkThread(spawnThread)
    MissionSetup1() -- forks
    -- set score calculation to show total kills
    for index, brain in ArmyBrains do
        brain.CalculateScore = function(thisBrain)
            --return armyKills(thisBrain) + killCount[ArmyIndexer(thisBrain)]*1000
            return armyKills(thisBrain) + killCount[ArmyIndexer(thisBrain)]*10000
        end
    end
end

-- main game logic
function MissionSetup1()
      --Creation of objective
 ---  MISSION 1      
  ScenarioInfo.SurvivalMission1End = Objectives.Kill(
          'primary',
          'incomplete', 
          OpStrings.EndGameTitle, 
          OpStrings.EndGameDescription,
          {
              MarkUnits = true,
              FlashVisible = false,
              Units = {gates[1]}
          }
      )
  ScenarioInfo.SurvivalMission1End:AddResultCallback(MissionSetup2)
  ---  MISSION 2
  ScenarioInfo.SurvivalMission2End = Objectives.Kill(
        'primary',
        'incomplete', 
        OpStrings.Mission2Title, 
        OpStrings.Mission2Description,
        {
            MarkUnits = true,
            FlashVisible = false,
            Units = {gates[2],gates[3]}
        }
    )
  ScenarioInfo.SurvivalMission2End:AddResultCallback(MissionSetupFinal)

  ---- Mission 3
  ScenarioInfo.SurvivalEndGame = Objectives.Kill(
        'primary',
        'incomplete', 
        OpStrings.EndGameTitle, 
        OpStrings.EndGameDescription,
        {
            MarkUnits = true,
            FlashVisible = false,
            Units = {finalrif_a, finalrif_b}
        }
    )
    ScenarioInfo.SurvivalEndGame:AddResultCallback(function(result)
        if(result) then
            PlayerWin()
        end
    end)  
	
  ForkThread(
		function()
			WaitSeconds(20)

		  ScenarioFramework.CreateVisibleAreaLocation( 15, ScenarioUtils.MarkerToPosition( 'Vis_1' ), 10, ArmyBrains[ScenarioInfo.ARMY_1] )
			WaitSeconds(2)
			WaitSeconds(2)
		
			DefenseMissionSetup()
		end
	)

end

function MissionSetup2() 
			ScenarioFramework.SetPlayableArea('AREA_2', false)
			   ScenarioFramework.CreateVisibleAreaLocation( 20, ScenarioUtils.MarkerToPosition( 'Vis_2' ), 10, ArmyBrains[ScenarioInfo.ARMY_1] )
			    ScenarioFramework.CreateVisibleAreaLocation( 20, ScenarioUtils.MarkerToPosition( 'Vis_3' ), 10, ArmyBrains[ScenarioInfo.ARMY_1] )
			WaitSeconds(10)
      removeRestrictions()
end

function MissionSetupFinal()
	ForkThread(
		function()
			ScenarioFramework.SetPlayableArea('AREA_3', false)

			WaitSeconds(5)
		  ScenarioFramework.CreateVisibleAreaLocation( 35, ScenarioUtils.MarkerToPosition( 'Vis_Rift_a' ), 10, ArmyBrains[ScenarioInfo.ARMY_1] )
			ScenarioFramework.CreateVisibleAreaLocation( 35, ScenarioUtils.MarkerToPosition( 'Vis_Rift_b' ), 10, ArmyBrains[ScenarioInfo.ARMY_1] )

			WaitSeconds(3)
      WaitSeconds(10)
			removeRestrictions2()
      WaitSeconds(50)
			WaitSeconds(120)
		end
	)		
end

DefenseMissionSetup = function()
	-- currently not forked
	-- execute in context of First Mission
	--Protect(Type,Complete,Title,Description,Target)
	ScenarioInfo.DefenseMission = Objectives.Protect(
		        'primary',
		        'incomplete', 
		        "Defense", 
		        "Defend the object at all cost or we will loose.",
		        {
		            Units = {ScenarioInfo.DefenseObject},
		            NumRequired = 1,
		            Timer = nil,
                PercentProgress = true,
		        }
		    )
	ScenarioInfo.DefenseMission:AddResultCallback(DefenseMissionLost)
	ScenarioFramework.PauseUnitDeath( ScenarioInfo.DefenseObject )
	ScenarioFramework.CreateVisibleAreaLocation( 65, ScenarioUtils.MarkerToPosition( 'HILL_CENTER' ), 0, ArmyBrains[ScenarioInfo.ARMY_1] )
	WaitSeconds(2)
end

DefenseMissionLost = function()
  local unit = ScenarioInfo.DefenseObject
	Cinematics.EnterNISMode()
	Cinematics.CameraMoveToRectangle(ScenarioUtils.AreaToRect('AREA_CAM_DEFENSE'), 5) -- take 5 sec to zoom in
	local scale = 2
  local Explosion = ScenarioInfo.DefenseObject #Entity(spec)
  local army = unit:GetArmy()
  Explosions.CreateConcussionRing( Explosion, scale )
  unit:ShakeCamera(50, 2, 0, 0.5)
  Explosions.CreateScalableUnitExplosion(unit)
  ForkThread( Explosions.CreateCompositeExplosionMeshes, Explosion )
	for i, army in ListArmies() do
		if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4") then
		killAllCommanders(army)
		GetArmyBrain(army):OnDefeat()
		end
	end
	msgOut("The defense object has been destroyed. You have lost!")
	loopEnd = true
	gameEnd = true
  WaitSeconds(4)
  Cinematics.ExitNISMode()
end



function PlayerWin()
    ForkThread(function()
--        LOG("___SE___ player win()")
        ScenarioInfo.DefenseObject:SetCanTakeDamage(false)
        ScenarioInfo.DefenseObject:SetCanBeKilled(false)
        Cinematics.EnterNISMode()
        ScenarioFramework.FlushDialogueQueue()
        while(ScenarioInfo.DialogueLock) do
            WaitSeconds(0.2)
        end
        ScenarioFramework.Dialogue(OpStrings.FinalMissionWon, KillGame, true)
        Cinematics.CameraMoveToRectangle(ScenarioUtils.AreaToRect('AREA_VIS_RIFT'),0.3) -- take 5 sec to zoom in
        WaitSeconds(4)
        UnlockInput()
        Cinematics.ExitNISMode()
    end)
end


function KillGame()
--    LOG("___SE___ KillGame()")
    GetArmyBrain("ARMY_SURVIVAL_ENEMY"):OnDefeat()
    gameEnd = true
    ForkThread(
    function()
        WaitSeconds(4)
        -- 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
        ScenarioFramework.EndOperationSafety() -- so our commanders got now longer be killed   -- parameter could be a list of protective units as well
    end
    )
end

-- survival mode
-- unit tables, seperated by air/ground
-- unit tables designed by BrockSamson
-- Cobaltur : I removed the t2 from the first wave . It would be enough up form the 2nd ( 8min)
local unitTableGroundCoba = {
    { -- Wave Set 1
        'UAL0106', -- aeon light assault
        'UAL0201', -- aeon light tank
        'URL0106', -- cybran light assault
        'URL0107', -- cybran heavy assault
        'UEL0106', -- uef light assault
        'UEL0201', -- uef medium tank
        'xsl0101', -- seraphim combat scout
        'xsl0201', -- seraphim medium tank

        'UAL0101', -- aeon scout
        'UAL0103', -- aeon t1 arty
        'URL0101', -- cybran scout
        'URL0103', -- cybran t1 arty
        'UEL0101', -- uef scout
        'UEL0103', -- uef t1 arty
        'xsl0101', -- seraphim combat scout
        'xsl0103', -- seraphim light artillery
--no t2 in wave 1 to hard
--		'UAL0202', -- aeon t2 tank
--		'UAL0205', -- aeon t2 aa
--		'URL0202', -- cybran t2 tank
        --'URL0203', -- cybran t2 amphibious tank
--		'URL0205', -- cybran t2 aa
--		'UEL0202', -- uef t2 tank
        --'UEL0203', -- uef t2 amphibious tank
--		'UEL0205', -- uef t2 aa
--		'DRL0204', -- cybran t2 rocket
        --'DEL0204', -- uef t2 gatling
--		'xal0203', -- aeon t2 assault tank
        --'xsl0202', -- seraphim t2 assault bot
--		'xsl0203', -- seraphim t2 hover tank
--		'xsl0205', -- seraphim t2 anti air
    },
    { -- Wave Set 2
        'UAL0201', -- aeon light tank
        'URL0107', -- cybran heavy assault
        'UEL0201', -- uef medium tank
        'xsl0201', -- seraphim medium tank

        'UAL0101', -- aeon scout
        'UAL0103', -- aeon t1 arty
        'URL0101', -- cybran scout
        'URL0103', -- cybran t1 arty
        'UEL0101', -- uef scout
        'UEL0103', -- uef t1 arty
        'xsl0101', -- seraphim combat scout
        'xsl0103', -- seraphim light artillery

        '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 -- originally inactive here
        '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
        'xsl0202', -- seraphim t2 assault bot
        'xsl0203', -- seraphim t2 hover tank
        'xsl0205', -- seraphim t2 anti air
        'xsl0111', -- seraphim t2 mobile missile
    },
    { -- Wave Set 3
        '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 -- originally active here
        '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
        'xsl0202', -- seraphim t2 assault bot
        'xsl0203', -- seraphim t2 hover tank
        'xsl0205', -- seraphim t2 anti air
        'xsl0111', -- seraphim t2 mobile missile
    },
    { -- Wave Set 4
        '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 -- originally active here
        '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
        'xsl0202', -- seraphim t2 assault bot
        'xsl0203', -- seraphim t2 hover tank
        'xsl0205', -- seraphim t2 anti air
        'xsl0111', -- seraphim t2 mobile missile

        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank
    },
    { -- Wave Set 5
        'UAL0111', -- aeon t2 missile launcher
        'UAL0307', -- aeon t2 shield
        'UAL0205', -- aeon t2 aa
        'URL0203', -- cybran t2 amphibious tank
        'URL0111', -- cybran t2 missile launcher
        'URL0306', -- cybran t2 stealth
        'URL0205', -- cybran t2 aa
        '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
        'xsl0202', -- seraphim t2 assault bot
        'xsl0205', -- seraphim t2 anti air
        'xsl0111', -- seraphim t2 mobile missile

        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank
        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank
    },
    { -- Wave Set 6
        'UAL0111', -- aeon t2 missile launcher
        'UAL0307', -- aeon t2 shield
        'UAL0205', -- aeon t2 aa
        'URL0111', -- cybran t2 missile launcher
        'URL0306', -- cybran t2 stealth
        'URL0205', -- cybran t2 aa
        'UEL0111', -- uef t2 missile launcher
        'UEL0307', -- uef t2 shield
        'UEL0205', -- uef t2 aa
        'xsl0205', -- seraphim t2 anti air
        'xsl0111', -- seraphim t2 mobile missile

        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank
        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank

        'DAL0310', -- aeon shield disruptor
        'xal0305', -- aeon sniper bot
        'xel0306', -- uef t3 mobile missile platform
        'xsl0307', -- seraphim t3 mobile shield gen
        'xsl0305', -- seraphim t3 sniper bot
    },
    { -- Wave Set 7
        'UAL0111', -- aeon t2 missile launcher
        'UAL0307', -- aeon t2 shield
        'UAL0205', -- aeon t2 aa
        'URL0111', -- cybran t2 missile launcher
        'URL0306', -- cybran t2 stealth
        'URL0205', -- cybran t2 aa
        'UEL0111', -- uef t2 missile launcher
        'UEL0307', -- uef t2 shield
        'UEL0205', -- uef t2 aa
        'xsl0205', -- seraphim t2 anti air
        'xsl0111', -- seraphim t2 mobile missile

        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank
        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank

        'DAL0310', -- aeon shield disruptor
        'xal0305', -- aeon sniper bot
        'xel0306', -- uef t3 mobile missile platform
        'xsl0307', -- seraphim t3 mobile shield gen
        'xsl0305', -- seraphim t3 sniper bot

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform
        --'URS0201', -- cybran salem class -- originally active here
    },
    { -- Wave Set 8
        'UAL0307', -- aeon t2 shield
        'UAL0205', -- aeon t2 aa
        'URL0306', -- cybran t2 stealth
        'URL0205', -- cybran t2 aa
        'UEL0307', -- uef t2 shield
        'UEL0205', -- uef t2 aa
        'xsl0205', -- seraphim t2 anti air

        'UAL0303', -- aeon t3 assault bot
        'URL0303', -- cybran t3 assault bot
        'UEL0303', -- uef t3 assault bot
        'xsl0303', -- seraphim t3 siege tank

        'DAL0310', -- aeon shield disruptor
        'xal0305', -- aeon sniper bot
        'xel0306', -- uef t3 mobile missile platform
        'xsl0307', -- seraphim t3 mobile shield gen
        'xsl0305', -- seraphim t3 sniper bot

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform
        --'URS0201', -- cybran salem class -- originally active here
        --'URS0201', -- cybran salem class
    },
    { -- Wave Set 9
        'UAL0307', -- aeon t2 shield
        'UAL0205', -- aeon t2 aa
        'URL0306', -- cybran t2 stealth
        'URL0205', -- cybran t2 aa
        'UEL0307', -- uef t2 shield
        'UEL0205', -- uef t2 aa
        'xsl0205', -- seraphim t2 anti air

        'DAL0310', -- aeon shield disruptor
        'xal0305', -- aeon sniper bot
        'xel0306', -- uef t3 mobile missile platform
        'xsl0307', -- seraphim t3 mobile shield gen
        'xsl0305', -- seraphim t3 sniper bot

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform
        --'URS0201', -- cybran salem class
        --'URS0201', -- cybran salem class

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform


        'UAL0301', -- aeon support commander
        'URL0301', -- cybran support commander
        'UEL0301', -- uef support commander
        'xsl0301', -- seraphim support commander
    },
    { -- Wave Set 10
        'UAL0307', -- aeon t2 shield
        'UAL0205', -- aeon t2 aa
        'URL0306', -- cybran t2 stealth
        'URL0205', -- cybran t2 aa
        'UEL0307', -- uef t2 shield
        'UEL0205', -- uef t2 aa
        'xsl0205', -- seraphim t2 anti air

        'DAL0310', -- aeon shield disruptor
        'xal0305', -- aeon sniper bot
        'xel0306', -- uef t3 mobile missile platform
        'xsl0307', -- seraphim t3 mobile shield gen
        'xsl0305', -- seraphim t3 sniper bot

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform

        --'URS0201', -- cybran salem class
        --'URS0201', -- cybran salem class

        'UAL0301', -- aeon support commander
        'URL0301', -- cybran support commander
        'UEL0301', -- uef support commander
        'xsl0301', -- seraphim support commander
        'UAL0301', -- aeon support commander
        'URL0301', -- cybran support commander
        'UEL0301', -- uef support commander
        'xsl0301', -- seraphim support commander

        'UAL0304', -- aeon t3 arty
        'URL0304', -- cybran t3 arty
        'UEL0304', -- uef t3 arty
        'xsl0304',	-- seraphim t3 heavy artillery
    },
    { -- Wave Set 11  same as 10 but experimentals will be spawned too look in ....
        'UAL0307', -- aeon t2 shield
        'UAL0205', -- aeon t2 aa
        'URL0306', -- cybran t2 stealth
        'URL0205', -- cybran t2 aa
        'UEL0307', -- uef t2 shield
        'UEL0205', -- uef t2 aa
        'xsl0205', -- seraphim t2 anti air

        'DAL0310', -- aeon shield disruptor
        'xal0305', -- aeon sniper bot
        'xel0306', -- uef t3 mobile missile platform
        'xsl0307', -- seraphim t3 mobile shield gen
        'xsl0305', -- seraphim t3 sniper bot

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform

        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xrl0305', -- cybran t3 armored assault bot
        'xel0305', -- uef t3 armored assault bot
        'xel0306', -- uef t3 mobile missile platform

        --'URS0201', -- cybran salem class
        --'URS0201', -- cybran salem class

        'UAL0301', -- aeon support commander
        'URL0301', -- cybran support commander
        'UEL0301', -- uef support commander
        'xsl0301', -- seraphim support commander
        'UAL0301', -- aeon support commander
        'URL0301', -- cybran support commander
        'UEL0301', -- uef support commander
        'xsl0301', -- seraphim support commander

        'UAL0304', -- aeon t3 arty
        'URL0304', -- cybran t3 arty
        'UEL0304', -- uef t3 arty
        'xsl0304',	-- seraphim t3 heavy artillery

        'UAL0304', -- aeon t3 arty
        'URL0304', -- cybran t3 arty
        'UEL0304', -- uef t3 arty
        'xsl0304',	-- seraphim t3 heavy artillery
    },
}

local unitTableExperimentalsCo = {
    { -- Wave Set Special
        'URL0402', -- cybran monkeylord
        'URL0402', -- cybran monkeylord
        'URL0402', -- cybran monkeylord
        'URL0402', -- cybran monkeylord
        'UAL0401', -- aeon galactic colossus
        'UAL0401', -- aeon galactic colossus
        'UAL0401', -- aeon galactic colossus
        'xsl0401',	-- seraphim experimental assault bot
        'xsl0401',	-- seraphim experimental assault bot

        'UEL0401', -- uef fatboy
        'xrl0403',	-- cybran experimental megabot

    }	,

    { -- Wave Set Special 40:00+
        'UAA0310', -- czar
        'URA0401', -- soul ripper
        'xsa0402', -- seraphim exp bomber
        'xsa0402', -- seraphim exp bomber
    },
}

local unitTableAirCoba = {
    { -- pseudo wave this is for compensating that air units are faster than land units
        -- so we want to delay the air waves a little bit
    },
    { -- Wave Set 1
        'UAA0103',	-- aeon t1 bomber
        'UAA0102',	-- aeon interceptor
        'URA0103',	-- cybran bomber
        'URA0103',	-- cybran interceptor
        'xra0105', 	-- cybran t1 light gunship
        'UEA0103',	-- uef bomber
        'UEA0102',	-- uef interceptor
        'xsa0103',	-- seraphim bomber
        'xsa0102',	-- seraphim interceptor
    },
    { -- Wave Set 2
        'UAA0103',	-- aeon t1 bomber
        'UAA0102',	-- aeon interceptor
        'URA0103',	-- cybran bomber
        'URA0103',	-- cybran interceptor
        'xra0105', 	-- cybran t1 light gunship
        'UEA0103',	-- uef bomber
        'UEA0102',	-- uef interceptor
        'xsa0103',	-- seraphim bomber
        'xsa0102',	-- seraphim interceptor

        'UAA0203',	-- aeon gunship
        'URA0203',	-- cybran gunship
        'UEA0203',	-- uef gunship
        'xsa0203',	-- seraphim gunship
        'DRA0202',	-- cybran corsair
        'DEA0202',	-- uef janus
        'xaa0202', 	-- aeon t2 combat fighter
        'xsa0202',	-- seraphim fighter/bomber
    },
    { -- Wave Set 3
        'UAA0203',	-- aeon gunship
        'URA0203',	-- cybran gunship
        'UEA0203',	-- uef gunship
        'xsa0203',	-- seraphim gunship
        'DRA0202',	-- cybran corsair
        'DEA0202',	-- uef janus
        'xaa0202', 	-- aeon t2 combat fighter
        'xsa0202',	-- seraphim fighter/bomber
    },
    { -- Wave Set 4
        'UAA0203',	-- aeon gunship
        'URA0203',	-- cybran gunship
        'UEA0203',	-- uef gunship
        'xsa0203',	-- seraphim gunship
        'DRA0202',	-- cybran corsair
        'DEA0202',	-- uef janus
        'xaa0202', 	-- aeon t2 combat fighter
        'xsa0202',	-- seraphim fighter/bomber

        'UAA0303',	-- aeon air superiority
        'URA0303',	-- cybran air superiority
        'UEA0303',	-- uef air superiority
        'xsa0303',	-- seraphim air superiority
        'UEA0305',	-- uef t3 gunship
        'xra0305', 	-- cybran T3 gunship
    },
    { -- Wave Set 5
        'UAA0303',	-- aeon air superiority
        'UAA0303',	-- aeon air superiority
        'UAA0304',	-- aeon t3 bomber
        'xaa0305', 	-- aeon T3 AA gunship

        'URA0303',	-- cybran air superiority
        'URA0304',	-- cybran t3 bomber
        'xra0305', 	-- cybran T3 gunship

        'UEA0303',	-- uef air superiority
        'UEA0304',	-- uef t3 bomber
        'UEA0305',	-- uef t3 gunship

        'xsa0202',	-- seraphim fighter/bomber
        'xsa0303',	-- seraphim air superiority
        'xsa0304',	-- seraphim bomber
    },

}

-- times are now an offset to the first wave
local unitTableTimesCoba = {
    { -- ground
        '0', -- 0m 1
        '180', -- 3m
        '480', -- 8m
        '720', -- 12m
        '960', -- 16m
        '1200', -- 20m
        '1440', -- 24m
        '1680', -- 28m
        '1920', -- 32m
        '2160', -- 36m
        '2760', -- 44m
    },
    { -- air
        '0', -- 0m  empty wave
        '220', -- 2m 1
        '660', -- 6m 2
        '1140', -- 14m 3
        '1580', -- 23m 4
        '2200', -- 30m 5
--		'3300', -- 55 specials
    },
}

-- upgrades list for SCU's
local upgradeTable = {
    -- aeon
    A={
        'Shield',
        'ShieldHeavy',
        'StabilitySuppressant',
        'SystemIntegrityCompensator',
    },
    -- cybran
    R={
        'CloakingGenerator',
        'EMPCharge',
        'FocusConvertor',
        'NaniteMissileSystem',
        'SelfRepairSystem',
        'StealthGenerator', -- NOTE requires cloaking generator first
    },
    -- uef
    E={
        'AdvancedCoolingUpgrade',
        'HighExplosiveOrdnance',
        'RadarJammer',
        'Shield',
        'ShieldGeneratorField',
    },
    -- seraphim
    S={
        'DamageStabilization',
        'Missile',
        'Overcharge',
        'Shield',
    },
}

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

setupGame = function()
    ScenarioInfo.Options.Victory = 'sandbox' 			-- custom victory condition
    -- misc settings
    ScenarioInfo.Options['CivilianAlliance'] = 'enemy'
    -- 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
            -- restrict players from building walls
            addRestricions(army)
            -- 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)
    HealthBuff = (ScenarioInfo.Options.opt_difficulty -1)/ 4 -- so on 9 health is *2   on 5(normal) is *1
    lvlmod = HealthBuff
    -- Healthbuff squared on 1 is *1, diff 20  it is ~22,5
    --platoonsize = 5 + (ScenarioInfo.Options.opt_difficulty * 5)
    --  now that we spawn more groups we need  a lower count
    --   4,  9 , 12  , 15
    platoonsize = ScenarioInfo.Options.opt_difficulty
    -- check if Total Veterancy is Enabled
    IsTv()
    -- the muliplier will be balanced by number of players which means how many plattos will be spawned at each gate ! see spawn fucntion

    -- before spawning the gates : or the health muliplier will effect muliple times
    adjustDefense()
    spawnAntinukes()
    spawnGates()
    spawnDefenseObject()
end


addRestricions = function( army )
--		ScenarioFramework.AddRestriction(army, categories.WALL)
    ScenarioFramework.AddRestriction(army, categories.STRUCTURE*categories.ARTILLERY)
    ScenarioFramework.AddRestriction(army, categories.url0401)
    ScenarioFramework.AddRestriction(army, categories.xeb2402) 
    ScenarioFramework.AddRestriction(army, categories.NUKE)
    ScenarioFramework.AddRestriction(army, categories.xab1401)
    ScenarioFramework.AddRestriction(army, categories.NAVAL)
    ScenarioFramework.RemoveRestriction(army, categories.TECH2*categories.ARTILLERY)
    ScenarioFramework.AddRestriction(army,
    categories.xrl0403 + --megalit
    categories.uaa0310 + --zar
    categories.xsa0402 + -- bomber
    categories.ura0401  -- soulripper
    )
end

removeRestrictions = function()
    for i, army in ListArmies() do
        if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4") then
            ScenarioFramework.RemoveRestriction(army,
            categories.xrl0403 + --megalit
            categories.uaa0310 + --zar
            categories.xsa0402 + -- bomber
            categories.ura0401  -- soulripper
            )
            ScenarioFramework.RemoveRestriction(army, categories.NUKE*categories.TECH3)
            ScenarioFramework.RemoveRestriction(army, categories.TECH3*categories.ARTILLERY)
            ScenarioFramework.AddRestriction(army, categories.xab2307)
        end
    end
end

removeRestrictions2 = function()
    for i, army in ListArmies() do
        if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4") then
            ScenarioFramework.RemoveRestriction(army, categories.EXPERIMENTAL)
            ScenarioFramework.RemoveRestriction(army, categories.STRUCTURE)
            ScenarioFramework.RemoveRestriction(army, categories.url0401)
            ScenarioFramework.AddRestriction(army, categories.NAVAL)
        end
    end
end

spawnDefenseObject = function()
    -- spawn the defense object
    if (ScenarioInfo.Options.opt_defenseObject < 5) then
        --object = "xac2101"--Aeon Temple
        --Black Sun 50k
        -- if (ScenarioInfo.Options.opt_defenseObject == 1 or
        -- ScenarioInfo.Options.opt_defenseObject == 2 or
        -- ScenarioInfo.Options.opt_defenseObject == 3 ) then
        local object = "uec1901"
        -- end
        --Aeon Temple 12k
        if (ScenarioInfo.Options.opt_defenseObject == 4) then
            -- QAI Mainframe  1k
            object = "xrc201"
        end
        local pos = ScenarioUtils.MarkerToPosition("HILL_CENTER")
        ScenarioInfo.DefenseObject =CreateUnitHPR( object, "ARMY_SUPERWEAPON", pos[1], pos[2], pos[3], 0,0,0)
        ScenarioInfo.DefenseObject:SetReclaimable(false)
        ScenarioInfo.DefenseObject:SetCapturable(false)

        --  to cheap to repair  unless we find a solution to increase econmy values
        if (ScenarioInfo.Options.opt_defenseObject == 1) then
            ScenarioInfo.DefenseObject:SetMaxHealth( 10000 )
            ScenarioInfo.DefenseObject:SetHealth(nil,10000)
        end
        if (ScenarioInfo.Options.opt_defenseObject == 3) then
            ScenarioInfo.DefenseObject:SetMaxHealth( 5000 )
            ScenarioInfo.DefenseObject:SetHealth(nil,5000)
        end
        -- set onkilled function
        ScenarioInfo.DefenseObject.OldOnKilled = ScenarioInfo.DefenseObject.OnKilled
        ScenarioInfo.DefenseObject.OnKilled = function(self, instigator, type, overkillRatio)
            -- we keep this function to execute our mission code first
            -- it calls then the "old" function where the original is stored
        end
        -- damged percent based onto 50k
        if (ScenarioInfo.Options.opt_defenseObject == 2) then
            ScenarioFramework.CreateUnitDamagedTrigger(defenseObjectDamaged80, ScenarioInfo.DefenseObject, .2) -- 40000
        end
        if (ScenarioInfo.Options.opt_defenseObject == 1) then
            ScenarioFramework.CreateUnitDamagedTrigger(defenseObjectDamaged12k, ScenarioInfo.DefenseObject, .86) -- 7000
        end
    end
end

--- messages only once :(   perhaps later I will find a better solutuin
defenseObjectDamaged80 = function ()
    msgOut("Defense Object at 80%")
    ScenarioFramework.CreateUnitDamagedTrigger(defenseObjectDamaged50, ScenarioInfo.DefenseObject, .5)
end
defenseObjectDamaged50 = function ()
    msgOut("Defense Object at 50%")
    -- this is not possible , will end in recursive call and crashes the application
    --ScenarioFramework.CreateUnitDamagedTrigger(defenseObjectDamaged80, ScenarioInfo.DefenseObject, .2)
end
defenseObjectDamaged12k = function ()
    msgOut("Defense Object at 60%")
    ScenarioFramework.CreateUnitDamagedTrigger(defenseObjectDamaged5k, ScenarioInfo.DefenseObject, .92) -- 4000
end
defenseObjectDamaged5k = function ()
    msgOut("Defense Object at 33%")
end


spawnAntinukes = function()
    for i = 1, 25 do
        local an = "nukedefense_"..i
        antinukes[i] = BuildArmedStrategicMissilesDefense( "xsb4302", "ARMY_SURVIVAL_ENEMY", an, 7 )
        antinukes[i].OldOnKilled = antinukes[i].OnKilled -- keep the original kill function
        antinukes[i].myID = i
        antinukes[i].OnKilled = antinukeHasBeenKilled
    end
end

adjustDefense = function()
    local ai = GetArmyBrain("ARMY_SURVIVAL_ENEMY")
    for subk, subv in ai:GetListOfUnits( categories.STRUCTURE, false ) do
        if ( subv.MyShield != nil ) then
            changebp(subv,HealthBuff*HealthBuff*4)
            subv.MyShield:SetMaxHealth(subv.MyShield:GetHealth()  * HealthBuff+2)
            subv.MyShield:SetHealth(nil, (subv.MyShield:GetHealth() * HealthBuff+2))
            subv.MyShield:SetRechargeTime(20*HealthBuff) -- because it becomes stronger with difficulty shields it should take longer to recharge
            subv.MyShield:SetShieldRegenStartTime(20) -- sera t3 default 1s
        else
            changebp(subv,HealthBuff*HealthBuff)
        end
    end
end

-- it would have been easier to to an average healt  like  (a+b)/2  but then it would be twice hard to kill
--  because this way the damage would only be half
--  so we need threads to sync to the weakest of both
FinalRiftHealth = function()
    while(finalrif_a and not finalrif_a:IsDead() and finalrif_b and not finalrif_b:IsDead()) do
        local health1 = finalrif_a:GetHealth()
        local health2 = finalrif_b:GetHealth()
        local diff = health1 - health2
        if( diff < 1000 and diff > 1000 ) then -- otherwise the regen does not work so well
            return
        end
        if(health1 < health2) then
            finalrif_b:SetHealth(finalrif_b, health1)
        elseif(health2 < health1) then
            finalrif_a:SetHealth(finalrif_a, health2)
        end
        WaitSeconds(.5)
    end
end
-- we need them if only one half has been killed to kill the other half too
finalrif_a_Dead = function()
    if(finalrif_b and not finalrif_b:IsDead()) then
        finalrif_b:Kill()
    end
end
finalrif_b_Dead  = function()
    if(finalrif_a and not finalrif_a:IsDead()) then
        finalrif_a:Kill()
    end
end


spawnGates = function()
    -- spawn gates at the outer edges for enemy units to teleport in
    -- 8 gates have to be destroyed
    numGatesOpen = 4
    -- yes we iterate 5 times because rift need 2 loops
    -- and the rift should double spawn units
    for i = 1,5 do
        if (gates[i] == nil) then
            if( i==4 ) then
                local pos2 = ScenarioUtils.MarkerToPosition("Gate4") -- renamed the rift to gate 5 makes it later easier
                finalrif_a = CreateUnitHPR( "xsc1901", "ARMY_SURVIVAL_ENEMY", pos2[1], pos2[2], pos2[3], 0,-1.53,0)
                changebp(finalrif_a,(math.pow(HealthBuff,3.5)))
--                finalrif_a:SetRegenRate(10)
                finalrif_a.SpawnPos = ScenarioUtils.MarkerToPosition("RiftSpawn")
                gates[i]=finalrif_a
            elseif( i==5 ) then
                local pos3 = ScenarioUtils.MarkerToPosition("Gate5")
                finalrif_b = CreateUnitHPR( "xsc1901", "ARMY_SURVIVAL_ENEMY", pos3[1], pos3[2], pos3[3], 0,1.60,0)
                changebp(finalrif_b,(math.pow(HealthBuff,3.5)))
--                finalrif_b:SetRegenRate(10)
                finalrif_b.SpawnPos = ScenarioUtils.MarkerToPosition("RiftSpawn")
                gates[i]=finalrif_b
            else
                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].SpawnPos = pos
                if i == 1 then
                    changebp(gates[i],(math.pow(HealthBuff,2)*(HealthBuff / Random(1,HealthBuff))))
                else
                    changebp(gates[i],(math.pow(HealthBuff,4)))
                end
--                if(i!=1) then
--                    gates[i]:SetRegenRate(10)
--                end
            end
            gates[i]:SetReclaimable(false)
            gates[i]:SetCapturable(false)
            gates[i]:SetProductionPerSecondEnergy(9999999)
--            gates[i]:SetProductionPerSecondMass(999999) --apparently enables the sc's to repair way too much stuff
            -- remove gate wreckage
            local bpw = gates[i]:GetBlueprint()
            if (bpw != nil) then
                bpw.Wreckage = nil -- bp.Wreckage.WreckageLayers.Land=false
            end
            -- set gate onkilled function
            gates[i].OldOnKilled = gates[i].OnKilled -- keep the original kill function
            gates[i].myID = i
            gates[i].OnKilled = gateHasBeenKilled
        end
    end
    --ForkThread(FinalRiftHealth) -- sync the rift healt
    ScenarioFramework.CreateUnitDeathTrigger(finalrif_a_Dead, finalrif_a)
    ScenarioFramework.CreateUnitDeathTrigger(finalrif_b_Dead, finalrif_b)
end


gateHasBeenKilled = function(self, instigator, type, overkillRatio) -- inline function
    ForkThread( function()
        if(self==finalrif_a or self==finalrif_b) then
--            LOG("___SE___ rift half killed")
            local unit = self
            local scale = 2
            local Explosion = finalrif_a --Entity(spec)
            Explosions.CreateConcussionRing( Explosion, scale )
            unit:ShakeCamera(50, 2, 0, 0.5)
            Explosions.CreateScalableUnitExplosion(unit)
            ForkThread( Explosions.CreateCompositeExplosionMeshes, Explosion )
        end
        if(instigator != nil) then
            -- add points to player
            local instArmy = instigator:GetArmy()
            local armyName = ArmyIndexer(GetArmyBrain(instArmy))
--            LOG("___SE___ gate killed, id="..self.myID.." instigator army: "..instArmy.."/"..armyName)
            killCount[armyName] = killCount[armyName] + 1
            if(numGatesOpen>1) then
                msgOut("A gate was destroyed by " .. getUsername(instArmy) .. "! "..(numGatesOpen-1) .. " gates left.")
            end
        else
            -- this happens on destryoing on a rift half as expected
            -- hm figure out if deathtriggers are not neccessary
        end
        -- handle game logic
        local spos = self:GetPosition()
        self.OldOnKilled(self, instigator, type, overkillRatio) -- call the orignial onKill
        WaitSeconds(1)
        --spawn a nuke
        --fork
        nuke(spos)
        gates[self.myID] = nil
        numGatesOpen = numGatesOpen - 1
    end
    )
end

antinukeHasBeenKilled = function(self, instigator, type, overkillRatio) -- inline function
    antinukes[self.myID] = nil
--    LOG("____ Antinuked killed :" ..self.myID)
    self.OldOnKilled(self, instigator, type, overkillRatio) -- call the orignial onKill
end


spawnThread = function(self)
--    LOG("___SE___ Spawn thread running!")
    -- wait before starting the unit spam
    local startupTime = ScenarioInfo.Options.opt_startupTime
    local now = 1
--    LOG("___SEBS___ spawnThread: " .. startupTime .. now)
    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
    Sync.ObjectiveTimer = 0
    msgOut("Attack waves have spawned!")
-- the waves start at id 1
-- the while loop repeats every spawn interval to spawn a new wave
    -- set the default waves to 1
    local groundwaveid = 1
    local airwaveid = 1
    -- loop each wave interval to spawn a new set of waves
    while (gameEnd == false) do
        -- first figure out which wave sets to spawn
        now = GetGameTimeSeconds() -- get the game time
        timetolerance = 5 -- tolerance because this tends to loop several seconds before the actual time, resulting in delayed wave sets
        -- check game time vs wave times to figure out which wave sets we are on
        local groundtimes = unitTableTimesCoba[1]
--		LOG("___SEBS___ groundtimes: " .. now .. "   " .. table.getn(groundtimes) .. "   " .. groundtimes[1])
        for i = groundwaveid, table.getn(groundtimes) do -- loop through remaining ground waves
            if ((now + timetolerance ) >= tonumber(groundtimes[i])+ScenarioInfo.Options.opt_startupTime) then -- if the current time is past the time for this wave...
                groundwaveid = i -- update the ground wave
--                LOG("___SEBS___ groundwaveid: " .. groundwaveid )
            else -- if the next wave time hasn't been reached we can break the loop
                break -- break the loop
            end
        end
        local airtimes = unitTableTimesCoba[2]
        for i = airwaveid, table.getn(airtimes) do -- loop through remaining ground waves
            if ((now + timetolerance) >= tonumber(airtimes[i])+ScenarioInfo.Options.opt_startupTime) then -- if the current time is past the time for this wave...
                airwaveid = i -- update the ground wave
            else -- if the next wave time hasn't been reached we can break the loop
                break -- break the loop
            end
        end
        -- now that we know which ground and air waves to spawn...
        -- spawn some waves
        spawnWaveCoba(groundwaveid, airwaveid)
        for i =1, 25 do
            if( antinukes[i]!=nil) then
                --LOG(" ___CO: antinuke " .. i -.." refilled")
                BuildTacticalMissiles( antinukes[i], 7 )
            end
        end
        -- now wait for next wave time
        -- wait for next wave
        local waitTime = ScenarioInfo.Options.opt_waveTime
--		LOG("___SEBS___ waveTimes: " .. ScenarioInfo.Options.opt_waveTime .. waitTime)
        WaitSeconds(waitTime)
        -- add massive final wave stuff -- nuke launches etc)
    end
    --no more waves to send, kill thread
    KillThread(self)
end

local iplus = 1 + lvlmod
spawnWaveCoba = function(gwaveid, awaveid)
    if( numGatesOpen < 1 ) then
        return
    end
    lvlmod = (math.ceil((math.pow(iplus,0.5))*HealthBuff))
    iplus = iplus + 1
    AirCount = (platoonsize/2)+1
    local spawnpoints = activeGates()
    for idx, gate in  spawnpoints do
        spawnwave( gate, unitTableGroundCoba[gwaveid], platoonsize-gwaveid) --less units each wave
        if(awaveid!=1) then
            spawnwave( gate, unitTableAirCoba[awaveid], AirCount)
        end
    end
    spawnwaveExperimentals(spawnpoints,gwaveid)
end

activeGates = function()
    local gl = {}
    if(gates[1]!=nil ) then
        table.insert(gl,gates[1])
    elseif( gates[2]!=nil) then
        table.insert(gl,gates[2])
        if( gates[3]!=nil) then
            table.insert(gl,gates[3])
        end
    elseif( gates[3]!=nil) then
        table.insert(gl,gates[3])
    elseif( gates[4]!=nil and gates[5]!=nil) then
        table.insert(gl,gates[4]) -- or 5 does not matter for our purpose
    end
    return gl
end

spawnwave = function(gate, spawntable, spawncount)
    local attackplan = getTopLevelAttackplan(gate.myID)
    local plattooncount =table.getn(attackplan)
    for i = 1,plattooncount do
        local platoonUnits = spawnPlatoon(gate.SpawnPos, spawntable, spawncount)
        startAttackMove(platoonUnits,gate.myID, attackplan[i])
    end
end

spawnPlatoon = function( pos, spawntable, spawncount )
    local spawnList = {} -- empty spawn list
    local bpCount = table.getn(spawntable) -- how many unit types are available in this wave set
    -- pull a random entry from the available units table and add it to the spawning table
    -- repeat for as many units as we will spawn
    for c = 1, spawncount do
        local rr = Utilities.GetRandomInt(1, bpCount)
        table.insert(spawnList, spawntable[rr])
    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
    return 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]+2*xoff, pos[2], pos[3]+2*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
    -- insert to platoon list
    table.insert(platoon, unit)
    -- check for upgrades
    local skip = Utilities.GetRandomInt(1, 4)
    if (skip != 3) then	-- one out of four commanders has no 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
                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
    end
--apply buffs based on  difficulty
    changebp(unit,HealthBuff)
end


local attackplan1 = {
    {
        {24,9}, {24,9},
        {24,8}, {24,8},
        {23,7},
        {6},
    },
    {
        {23,7},
        {6},
        {5},
        {4},
        {22,3},
    },
    {
        {4},
        {22,3},
        {21,2},{21,2},
        {21,1},{21,1},
    },
}
local attackplan2_3 ={
    {
        {22,5},{22,5},
        {22,4},{22,4},
        {3},
    },
    {
        {3},
        {21,2},{21,2},
        {21,1},{21,1},
    },
}
local attackplan2 ={
    {
        {21,1},
        {21,2},
        {3},
    },
    {
        {3},
        {22,4},
        {22,5},
    },
    {
        {23,6}, {23,6},
        {23,7}, {23,7},
        {23,8},
        {24,9},
    },
}
local attackplan3_2 ={
    {
        {24,9},{24,9},
        {24,8},{24,8},
        {7},
    },
    {
        {7},
        {23,6},{23,6},
        {23,5},{23,5},
    },
}
local attackplan3 ={
    {
        {24,9},
        {24,8},
        {7},
    },
    {7},
    {23,6},
    {23,5},
    {
    },
    {
        {22,4},{22,4},
        {22,5},{22,5},
        {22,2},
        {21,1},
    },
}
local attackplan4={
    {
        {34,9},{34,24,9},
        {24,8},
        {33,7},
    },
    {
        {34,8},
        {33,7},
        {23,6},
    },
    {
        {23,6},
        {5},
        {22,4},
    },
    {
        {22,4},
        {32,3},
        {31,2},
    },
    {
        {32,3},
        {31,2},
        {31,1},{31,21,1},
    },
}
local xWave = 0
spawnwaveExperimentals = function(spawnpoints, waveid)
    if( waveid== table.getn(unitTableGroundCoba) ) then
        xWave = xWave-ScenarioInfo.Options.opt_difficulty
        if(xWave<0) then
            xWave = 19
            for player =1, playercount do
                -- local gate = spawnpoints[Utilities.GetRandomInt(1,table.getn(spawnpoints))]
                local gate = gates[Utilities.GetRandomInt(4,5)]
                local EXPspawnList = {} -- empty spawn list
                local whichEXPList
                if( player==1 or player==3 ) then
                    whichEXPList = unitTableExperimentalsCo[1]
                else
                    whichEXPList = unitTableExperimentalsCo[2]
                end
                local bpCount = table.getn(whichEXPList)
                local rrr = Utilities.GetRandomInt(1, bpCount )
                table.insert(EXPspawnList,whichEXPList[rrr])

                local experimentals = {}
                for index, bp in EXPspawnList do
                    doSpawnUnit(bp, gate.SpawnPos, experimentals)
                end
                startAttackMove(experimentals,gate.myID,attackplan4[Utilities.GetRandomInt(1,table.getn(attackplan4))])
            end
        end
    end
end
getTopLevelAttackplan = function ( gateID )
    if(gateID==4 or gateID==5) then
--        LOG("___:  Top plan")
        return attackplan4
    elseif(gateID==2 ) then
        if(gates[3]==nil) then
            return attackplan2
        else
            return attackplan2_3
        end
    elseif(gateID==3 ) then
        if(gates[3]==nil) then
            return attackplan3
        else
            return attackplan3_2
        end
    else
        return attackplan1
    end
end
--forms a platoon to attack the hill with a given set of units
startAttackMove = function(units,gateID, partialAttackplan)
    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)
        local movepoints = partialAttackplan[Utilities.GetRandomInt(1, table.getn(partialAttackplan))]
        for idx, waypointid in movepoints do
            attackPlatoon:AggressiveMoveToLocation(ScenarioUtils.MarkerToPosition("WP".. waypointid))
        end
        -- continue
        local posCENTER = ScenarioUtils.MarkerToPosition('HILL_CENTER')
        attackPlatoon:AggressiveMoveToLocation(posCENTER)
    end
end

-- misc funcs
shake = function( orig )
    local markers = table.copy(orig)
    local retMarkers = {}
    local numMarkers = table.getn( markers )
    for i = 1, numMarkers do
        rand = Random(1, numMarkers + 1 - i)
        table.insert(retMarkers, markers[rand])
        table.remove(markers, rand)
    end
    return retMarkers -- double entries as input
end

function BuildArmedStrategicMissilesDefense( unitId,  forArmy, markerName, NumToBuild )
    local Strategic_Defense_1 = BuildUnitAtLocation( unitId, forArmy, markerName )
    BuildAntiNukes( Strategic_Defense_1, NumToBuild, markerName  )
    changebp(Strategic_Defense_1,math.pow(HealthBuff,3.3))
--    Strategic_Defense_1:SetRegenRate(3) -- no multiplier !
    return Strategic_Defense_1
end

function BuildAntiNukes( unit, markerName, NumToBuild )
    BuildTacticalMissiles( unit, markerName, NumToBuild )
end

function BuildTacticalMissiles( unit, NumToBuild, markerName )
    if unit and unit != nil then
        unit:GiveTacticalSiloAmmo( NumToBuild )
        --LOG("MANIMAL:   Missiles Silo Ammo = ".. NumToBuild .." at "..markerName.."  .")
    else
        error('MANIMAL:  *ERROR* Attempting to build Missiles in a non-existent unit ')
    end
end

function BuildUnitAtLocation( unitId, forArmy, markerName )
    --[[  Spawn unitId @ markerName ]]--
    local spawnLoc = ScenarioUtils.MarkerToPosition( markerName )
    unit = CreateUnitHPR( unitId, forArmy, spawnLoc[1], spawnLoc[2], spawnLoc[3], 0, 0, 0 )
    --LOG("MANIMAL:  SPAWN  unitId = "..unitId..")  for Army = "..forArmy.."  at marker = "..markerName.."  .")
    return unit
end

-- 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

armyKills = function(brain)
    local kills = brain: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

-- this function creates all mex and hydrocarbon markers
-- resource scale script (c) Jotto, reworked by tecxx
function ScenarioUtils.CreateResources()
    -- fetch markers and iterate them
    local markers = ScenarioUtils.GetMarkers()
    for i, tblData in pairs(markers) do
        -- spawn resources?
        local doit = false
        if (tblData.resource and not tblData.SpawnWithArmy) then
            -- standard resources, spawn it
            doit = true
        elseif (tblData.resource and tblData.SpawnWithArmy) then
            -- resources bound to player, check if army is presend
            for j, army in ListArmies() do
                if (tblData.SpawnWithArmy == army) then
                    doit = true  -- we made sure the army is present, allow spawn
                    break
                end
            end
        end

        if (doit) then
            -- check type of resource and set parameters
            local bp, albedo, sx, sz, lod
            if (tblData.type == "Mass") then
                albedo = "/env/common/splats/mass_marker.dds"
                bp = "/env/common/props/massDeposit01_prop.bp"
                sx = 2
                sz = 2
                lod = 100
            else
                albedo = "/env/common/splats/hydrocarbon_marker.dds"
                bp = "/env/common/props/hydrocarbonDeposit01_prop.bp"
                sx = 6
                sz = 6
                lod = 200
            end
            -- create the resource
            CreateResourceDeposit(tblData.type,	tblData.position[1], tblData.position[2], tblData.position[3], tblData.size)
            -- create the resource graphic on the map
            CreatePropHPR(bp, tblData.position[1], tblData.position[2], tblData.position[3], Random(0,360), 0, 0)
            -- create the resource icon on the map
            CreateSplat(
            tblData.position,           -- Position
            0,                          -- Heading (rotation)
            albedo,                     -- Texture name for albedo
            sx, sz,                     -- SizeX/Z
            lod,                        -- LOD
            0,                          -- Duration (0 == does not expire)
            -1,                         -- army (-1 == not owned by any single army)
            0							-- ???
            )
        end
    end
end

OpStrings = {
    EndGameTitle = 'Destroy the enemy rift',
    EndGameDescription = 'Attack and destroy enemy rift. Hurry it is getting worse.',
    Mission2Title = '<LOC X06_M01_OBJ_010_010>Destroy the Forward Seraphim Position',
    Mission2Description = '<LOC X06_M01_OBJ_010_010>Destroy the Forward Seraphim Position',
    FinalMissionWon = {
        {text = '<LOC X06_M03_105_010>[{i Hall}]: The Arch is down! The Arch is down!', vid = 'X06_Hall_M03_04503.sfd', bank = 'X06_VO', cue = 'X06_Hall_M03_04503', faction = 'UEF'},
    },
}

-- additional code
function changebp(unit, BF)
    if tvEn == true then
        unit:AddLevels(BF+lvlmod)
    end
    if tvEn == false then
        if lvlmod > 5 then
            unit:SetVeterancy(5)
        else
            unit:SetVeterancy(math.ceil(lvlmod))
        end
        local health = math.ceil(unit:GetHealth()*(BF+lvlmod)*0.1)
        unit:SetMaxHealth(health)
        unit:SetHealth(nil, health)
    end
end

function IsTv()
    local ai = GetArmyBrain("ARMY_SURVIVAL_ENEMY")
    for k,unit  in ai:GetListOfUnits( categories.STRUCTURE, false ) do
        if unit:GetBlueprint().Economy.xpValue == nil then
--            LOG('___false')
            tvEn = false
            return
        end
--        LOG('___true')
        tvEn = true
        return true
    end
end

--EOF
