NodUnitsVehicle = { 'bike', 'bike', 'bggy', 'ltnk', 'bike', 'bike' }
NodUnitsRocket = { 'e1', 'e1', 'e1', 'e1' }
NodUnitsGunner = { 'e3', 'e3', 'e3', 'e3' }
GDIReinforceUnits = { 'e2', 'e2', 'e2', 'e2', 'e2' }

Gdi1Units = { ['e1'] = 3, ['e2'] = 1 }
Gdi2Units = { ['e1'] = 2, ['e2'] = 1 }
Gdi3Units = { ['jeep'] = 1 }
Gdi4Units = { ['mtnk'] = 1 }
Gdi5Units = { ['e1'] = 1, ['e2'] = 2 }
Gdi6Units = { ['e1'] = 3 }
Gdi7Units = { ['e2'] = 2 }
Gdi8Units = { ['e2'] = 5 }

AllUnits = { Gdi1Units, Gdi2Units, Gdi3Units, Gdi4Units, Gdi5Units, Gdi6Units, Gdi7Units, Gdi8Units }

AirstrikeDelay = DateTime.Minutes(1) + DateTime.Seconds(40)
YyyyTriggerFunctionTime = DateTime.Minutes(1) + DateTime.Seconds(30)
ZzzzTriggerFunctionTime = DateTime.Minutes(2) + DateTime.Seconds(30)
Grd1TriggerFunctionTime = DateTime.Seconds(3)
Atk2TriggerFunctionTime = DateTime.Minutes(1) + DateTime.Seconds(10)
Atk3TriggerFunctionTime = DateTime.Minutes(3) + DateTime.Seconds(10)
Atk4TriggerFunctionTime = DateTime.Minutes(4) + DateTime.Seconds(40)
Atk6TriggerFunctionTime = DateTime.Minutes(2) + DateTime.Seconds(30)

DelyCellTriggerActivator = { CPos.New(29,30), CPos.New(28,30), CPos.New(27,30), CPos.New(26,30), CPos.New(25,30), CPos.New(24,30), CPos.New(23,30), CPos.New(22,30), CPos.New(21,30), CPos.New(29,29), CPos.New(28,29), CPos.New(27,29), CPos.New(26,29), CPos.New(25,29), CPos.New(24,29), CPos.New(23,29), CPos.New(22,29) }
DelzCellTriggerActivator = { CPos.New(29,27), CPos.New(28,27), CPos.New(27,27), CPos.New(26,27), CPos.New(25,27), CPos.New(24,27), CPos.New(29,26), CPos.New(28,26), CPos.New(27,26), CPos.New(26,26), CPos.New(25,26), CPos.New(24,26) }
Atk5CellTriggerActivator = { CPos.New(10,33), CPos.New(9,33), CPos.New(8,33), CPos.New(9,32), CPos.New(8,32), CPos.New(7,32), CPos.New(8,31), CPos.New(7,31), CPos.New(6,31) }
Atk1CellTriggerActivator = { CPos.New(10,33), CPos.New(9,33), CPos.New(8,33), CPos.New(9,32), CPos.New(8,32), CPos.New(7,32), CPos.New(8,31), CPos.New(7,31), CPos.New(6,31) }

Gdi1Waypoints = { waypoint0, waypoint1, waypoint3, waypoint4 }
Gdi2Waypoints = { waypoint0, waypoint1, waypoint3, waypoint4, waypoint5 }
Gdi3Waypoints = { waypoint0, waypoint1, waypoint2 }
Gdi5Waypoints = { waypoint0, waypoint1, waypoint3, waypoint1, waypoint6 }
Gdi11Waypoints = { waypoint0, waypoint1, waypoint3, waypoint4, waypoint7, waypoint8 }
Gdi12Waypoints = { waypoint0, waypoint1, waypoint3, waypoint11, waypoint12 }

AllWaypoints = { Gdi1Waypoints, Gdi2Waypoints, Gdi3Waypoints, Gdi5Waypoints, Gdi11Waypoints, Gdi12Waypoints }

PrimaryTargets = { Tower1, Tower2, Radar, Silo1, Silo2, Silo3, Refinery, Barracks, Plant1, Plant2, Yard, Factory }

GDIStartUnits = { }

SendGDIAirstrike = function()
	if not Radar.IsDead and Radar.Owner == GDI then
		local target = getAirstrikeTarget()

		if target then
			Radar.SendAirstrike(target, false, Facing.NorthEast + 4)
			Trigger.AfterDelay(AirstrikeDelay, SendGDIAirstrike)
		else
			Trigger.AfterDelay(AirstrikeDelay/4, SendGDIAirstrike)
		end
	end
end

YyyyTriggerFunction = function()
	if not YyyyTriggerSwitch then
		for type, count in pairs(Gdi2Units) do
			MyActors = Utils.Take(count, GDI.GetActorsByType(type))
			Utils.Do(MyActors, function(actor)
				WaypointMovementAndHunt(actor, Gdi2Waypoints)
			end)
		end
	end
end

ZzzzTriggerFunction = function()
	if not ZzzzTriggerSwitch then
		for type, count in pairs(Gdi1Units) do
			MyActors = Utils.Take(count, GDI.GetActorsByType(type))
			Utils.Do(MyActors, function(actor)
				WaypointMovementAndHunt(actor, Gdi1Waypoints)
			end)
		end
	end
end

Grd1TriggerFunction = function()
	MyActors = Utils.Take(2, GDI.GetActorsByType('jeep'))
	Utils.Do(MyActors, function(actor)
		WaypointMovementAndHunt(actor, Gdi5Waypoints)
	end)
end

Atk5TriggerFunction = function()
	WaypointMovementAndHunt(GDI.GetActorsByType('mtnk')[1], Gdi12Waypoints)
end

Atk2TriggerFunction = function()
	for type, count in pairs(Gdi1Units) do
		MyActors = Utils.Take(count, GDI.GetActorsByType(type))
		Utils.Do(MyActors, function(actor)
			WaypointMovementAndHunt(actor, Gdi1Waypoints)
		end)
	end
end

Atk3TriggerFunction = function()
	for type, count in pairs(Gdi2Units) do
		MyActors = Utils.Take(count, GDI.GetActorsByType(type))
		Utils.Do(MyActors, function(actor)
			WaypointMovementAndHunt(actor, Gdi2Waypoints)
		end)
	end
end

Atk4TriggerFunction = function()
	WaypointMovementAndHunt(GDI.GetActorsByType('jeep')[1], Gdi3Waypoints)
end

Atk6TriggerFunction = function()
	WaypointMovementAndHunt(GDI.GetActorsByType('mtnk')[1], Gdi2Waypoints)
end

Atk1TriggerFunction = function()
	Reinforcements.ReinforceWithTransport(GDI, 'tran', GDIReinforceUnits, { waypoint9.Location, waypoint26.Location }, nil,
	function(transport, cargo)
		transport.UnloadPassengers()
		Utils.Do(cargo, function(actor)
			IdleHunt(actor)
		end)
	end)
end

AutoTriggerFunction = function()
	local units = AllUnits[DateTime.GameTime % #AllUnits + 1]
	local waypoints = AllWaypoints[DateTime.GameTime % #AllWaypoints + 1]

	for type, count in pairs(units) do
		MyActors = Utils.Take(count, GDI.GetActorsByType(type))
		Utils.Do(MyActors, function(actor)
			WaypointMovementAndHunt(actor, waypoints)
		end)
	end
end

HuntTriggerFunction = function()
	local list = GDI.GetGroundAttackers()
	Utils.Do(list, function(unit)
		IdleHunt(unit)
	end)
end

WaypointMovementAndHunt = function(unit, waypoints)
	if unit ~= nil then
		Utils.Do(waypoints, function(waypoint)
			unit.AttackMove(waypoint.Location)
		end)
		IdleHunt(unit)
	end
end

InsertNodUnits = function()
	Camera.Position = UnitsEntry.CenterPosition

	Media.PlaySpeechNotification(Nod, "Reinforce")
	Reinforcements.Reinforce(Nod, NodUnitsVehicle, { UnitsEntry.Location, UnitsRallyVehicle.Location }, 1)
	Reinforcements.Reinforce(Nod, NodUnitsRocket, { UnitsEntry.Location, UnitsRallyRocket.Location }, 50)
	Reinforcements.Reinforce(Nod, NodUnitsGunner, { UnitsEntry.Location, UnitsRallyGunner.Location }, 50)
	Trigger.AfterDelay(DateTime.Seconds(6), function()
		Reinforcements.Reinforce(Nod, { 'mcv' }, { UnitsEntry.Location, UnitsRallyMCV.Location })
	end)
end

WorldLoaded = function()
	GDI = Player.GetPlayer("GDI")
	Nod = Player.GetPlayer("Nod")

	InsertNodUnits()

	Trigger.OnObjectiveAdded(Nod, function(p, id)
		Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective")
	end)

	Trigger.OnObjectiveCompleted(Nod, function(p, id)
		Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed")
	end)

	Trigger.OnObjectiveFailed(Nod, function(p, id)
		Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed")
	end)

	Trigger.OnPlayerWon(Nod, function()
		Media.PlaySpeechNotification(Nod, "Win")
	end)

	Trigger.OnPlayerLost(Nod, function()
		Media.PlaySpeechNotification(Nod, "Lose")
	end)

	NodObjective1 = Nod.AddPrimaryObjective("Build 3 SAMs.")
	NodObjective2 = Nod.AddPrimaryObjective("Destroy the GDI base.")
	GDIObjective = GDI.AddPrimaryObjective("Kill all enemies.")

	Trigger.AfterDelay(AirstrikeDelay, SendGDIAirstrike)
	Trigger.AfterDelay(YyyyTriggerFunctionTime, YyyyTriggerFunction)
	Trigger.AfterDelay(ZzzzTriggerFunctionTime, ZzzzTriggerFunction)

	Trigger.OnEnteredFootprint(DelyCellTriggerActivator, function(a, id)
		if a.Owner == Nod then
			YyyyTriggerSwitch = true
			Trigger.RemoveFootprintTrigger(id)
		end
	end)

	Trigger.OnEnteredFootprint(DelzCellTriggerActivator, function(a, id)
		if a.Owner == Nod then
			ZzzzTriggerSwitch = true
			Trigger.RemoveFootprintTrigger(id)
		end
	end)

	Trigger.AfterDelay(Grd1TriggerFunctionTime, Grd1TriggerFunction)

	Trigger.OnEnteredFootprint(Atk5CellTriggerActivator, function(a, id)
		if a.Owner == Nod then
			Atk5TriggerFunction()
			Trigger.RemoveFootprintTrigger(id)
		end
	end)

	Trigger.AfterDelay(Atk2TriggerFunctionTime, Atk2TriggerFunction)
	Trigger.AfterDelay(Atk3TriggerFunctionTime, Atk3TriggerFunction)
	Trigger.AfterDelay(Atk4TriggerFunctionTime, Atk4TriggerFunction)
	Trigger.AfterDelay(Atk6TriggerFunctionTime, Atk6TriggerFunction)

	Trigger.OnEnteredFootprint(Atk1CellTriggerActivator, function(a, id)
		if a.Owner == Nod then
			Atk1TriggerFunction()
			Trigger.RemoveFootprintTrigger(id)
		end
	end)

	Trigger.OnDiscovered(Tower1, AutoTriggerFunction)
	Trigger.OnDiscovered(Tower2, AutoTriggerFunction)

	Trigger.OnAllKilledOrCaptured(PrimaryTargets, function()
		Nod.MarkCompletedObjective(NodObjective2)
		HuntTriggerFunction()
	end)

	Trigger.AfterDelay(0, getStartUnits)
end

Tick = function()
	if Nod.HasNoRequiredUnits() then
		if DateTime.GameTime > 2 then
			GDI.MarkCompletedObjective(GDIObjective)
		end
	end

	if not Nod.IsObjectiveCompleted(NodObjective1) and CheckForSams(Nod) then
		Nod.MarkCompletedObjective(NodObjective1)
	end

	if DateTime.GameTime % DateTime.Seconds(3) == 0 then
		checkProduction(GDI)
	end

	if DateTime.GameTime % DateTime.Seconds(45) == 0 then
		AutoTriggerFunction()
	end
end

IdleHunt = function(unit)
	if not unit.IsDead then
		Trigger.OnIdle(unit, unit.Hunt)
	end
end

CheckForSams = function(player)
	local baseBuildings = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(actor)
		return actor.Owner == Nod and actor.Type == 'sam' end)

	return #baseBuildings >= 3
end

checkProduction = function(player)
	local Units = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(actor)
		return actor.Owner == GDI
	end)

	local UnitsType = { }
	for type, count in pairs(GDIStartUnits) do
		counter = 0
		Utils.Do(Units, function(unit)
			if unit.Type == type then
				counter = counter + 1
			end
		end)
		if counter < count then
			for i = 1, count - counter, 1 do
				UnitsType[i] = type
			end
		end
		if #UnitsType > 0 then
			if (type == 'jeep' or type == 'mtnk') and not Factory.IsDead and Factory.Owner == gdi then
				Factory.Build(UnitsType)
			elseif (type == 'e1' or type == 'e2') and not Barracks.IsDead and Barracks.Owner == gdi then
				Barracks.Build(UnitsType)
			end
		end
		UnitsType = { }
	end
end

getStartUnits = function()
	local Units = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(actor)
		return actor.Owner == GDI and ( actor.Type == 'e2' or actor.Type == 'e1' or actor.Type == 'jeep' or actor.Type == 'mtnk')
	end)
	Utils.Do(Units, function(unit)
		if not GDIStartUnits[unit.Type] then
			GDIStartUnits[unit.Type] = 1
		else
			GDIStartUnits[unit.Type] = GDIStartUnits[unit.Type] + 1
		end
	end)
end

searches = 0
getAirstrikeTarget = function()
	local list = Nod.GetGroundAttackers()
	local target = list[DateTime.GameTime % #list + 1].CenterPosition

	local sams = Map.ActorsInCircle(target, WDist.New(8 * 1024), function(actor)
		return actor.Type == "sam" end)

	if #sams == 0 then
		searches = 0
		return target
	elseif searches < 6 then
		searches = searches + 1
		return getAirstrikeTarget()
	else
		searches = 0
		return nil
	end
end
