1--[[
2   Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
3   This file is part of OpenRA, which is free software. It is made
4   available to you under the terms of the GNU General Public License
5   as published by the Free Software Foundation, either version 3 of
6   the License, or (at your option) any later version. For more
7   information, see COPYING.
8]]
9
10AtreidesMainBase = { AConYard1, AOutpost1, APalace, ARefinery1, ARefinery2, AHeavyFactory1, ALightFactory1, ARepair1, AStarport1, AHiTechFactory, AResearch, ARock1, ARock2, ARock3, ARock4, ARock5, ARock6, ARock7, ARock8, ARock9, ARock10, ABarracks1, ABarracks2, APower1, APower2, APower3, APower4, APower5, APower6, APower7, APower8, APower9, APower10, APower11, ASilo1, ASilo2, ASilo3 }
11AtreidesSmallBase = { AConYard2, ARefinery3, ABarracks3, AHeavyFactory2, ALightFactory2, ARepair2, AGunt1, AGunt2, ARock11, APower12, APower13, APower14, APower15, APower16, APower17, APower18, APower19, APower20 }
12CorrinoMainBase = { CConYard, COutpost, CPalace, CRefinery1, CHeavyFactory, CResearch, CGunt1, CGunt2, CGunt3, CGunt4, CGunt5, CGunt6, CRock1, CRock2, CRock3, CRock4, CBarracks1, CBarracks2, CPower1, CPower2, CPower3, CPower4, CPower5, CPower6, CPower7, CPower8 }
13CorrinoSmallBase = { CRefinery2, CLightFactory, CStarport, CGunt7, CGunt8, CBarracks3, CPower9, CPower10, CPower11, CPower12, CSilo1, CSilo2, CSilo3, CSilo4 }
14
15AtreidesReinforcements =
16{
17	easy =
18	{
19		{ "combat_tank_a", "quad", "light_inf", "light_inf" },
20		{ "fremen", "trike", "combat_tank_a"},
21		{ "combat_tank_a", "quad", "light_inf", "light_inf" },
22		{ "trooper", "trooper", "trooper" },
23		{ "light_inf", "light_inf", "sonic_tank" },
24		{ "light_inf", "light_inf", "light_inf", "quad" }
25	},
26
27	normal =
28	{
29		{ "combat_tank_a", "quad", "quad", "light_inf", "light_inf" },
30		{ "fremen", "fremen", "trike", "combat_tank_a"},
31		{ "combat_tank_a", "quad", "quad", "light_inf", "light_inf" },
32		{ "trooper", "trooper", "trooper", "trooper" },
33		{ "light_inf", "light_inf", "light_inf", "sonic_tank" },
34		{ "light_inf", "light_inf", "light_inf", "quad", "quad" },
35		{ "combat_tank_a", "combat_tank_a", "missile_tank" }
36	},
37
38	hard =
39	{
40		{ "combat_tank_a", "quad", "quad", "quad", "light_inf", "light_inf" },
41		{ "fremen", "fremen", "fremen", "trike", "combat_tank_a"},
42		{ "combat_tank_a", "quad", "quad", "quad", "light_inf", "light_inf" },
43		{ "trooper", "trooper", "trooper", "trooper", "trooper" },
44		{ "light_inf", "light_inf", "light_inf", "light_inf", "sonic_tank" },
45		{ "light_inf", "light_inf", "light_inf", "quad", "quad", "quad" },
46		{ "combat_tank_a", "combat_tank_a", "combat_tank_a", "missile_tank" },
47		{ "fremen", "fremen", "fremen", "fremen", "fremen", "fremen", "fremen", "fremen" }
48	}
49}
50
51CorrinoStarportReinforcements =
52{
53	easy =
54	{
55		{ "sardaukar", "sardaukar", "sardaukar", "sardaukar", "sardaukar", "siege_tank", "siege_tank" },
56		{ "trooper", "trooper", "combat_tank_h", "combat_tank_h" },
57		{ "trike", "trike", "trike", "trooper", "trooper", "light_inf", "light_inf" }
58	},
59
60	normal =
61	{
62		{ "sardaukar", "sardaukar", "sardaukar", "sardaukar", "sardaukar", "sardaukar", "siege_tank", "siege_tank" },
63		{ "trooper", "trooper", "trooper", "combat_tank_h", "combat_tank_h" },
64		{ "trike", "trike", "trike", "trooper", "trooper", "trooper", "light_inf", "light_inf", "light_inf" }
65	},
66
67	hard =
68	{
69		{ "sardaukar", "sardaukar", "sardaukar", "sardaukar", "sardaukar", "sardaukar", "sardaukar", "siege_tank", "siege_tank" },
70		{ "trooper", "trooper", "trooper", "trooper", "combat_tank_h", "combat_tank_h" },
71		{ "trike", "trike", "trike", "trooper", "trooper", "trooper", "trooper", "light_inf", "light_inf", "light_inf", "light_inf" }
72	}
73}
74
75AtreidesAttackDelay =
76{
77	easy = DateTime.Minutes(3) + DateTime.Seconds(30),
78	normal = DateTime.Minutes(2) + DateTime.Seconds(30),
79	hard = DateTime.Minutes(1) + DateTime.Seconds(30)
80}
81
82CorrinoStarportDelay =
83{
84	easy = DateTime.Minutes(10),
85	normal = DateTime.Minutes(8),
86	hard = DateTime.Minutes(6)
87}
88
89AtreidesAttackWaves =
90{
91	easy = 6,
92	normal = 7,
93	hard = 8
94}
95
96FremenGroupSize =
97{
98	easy = 2,
99	normal = 4,
100	hard = 6
101}
102
103InitialAtreidesReinforcements =
104{
105	{ "combat_tank_a", "combat_tank_a", "quad", "trike" },
106	{ "trooper", "trooper", "trooper", "trooper", "trooper", "combat_tank_a" },
107	{ "combat_tank_a", "combat_tank_a", "quad", "quad", "trike" }
108}
109
110InitialCorrinoReinforcements =
111{
112	{ "trooper", "trooper", "trooper", "trooper", "quad", "quad" },
113	{ "trooper", "trooper", "trooper", "trooper", "trooper", "combat_tank_h", "combat_tank_h" },
114	{ "trooper", "trooper", "trooper", "combat_tank_h", "combat_tank_h", "combat_tank_h" }
115}
116
117AtreidesPaths =
118{
119	{ AtreidesEntry1.Location, AtreidesRally1.Location },
120	{ AtreidesEntry2.Location, AtreidesRally2.Location },
121	{ AtreidesEntry3.Location, AtreidesRally3.Location },
122	{ AtreidesEntry4.Location, AtreidesRally4.Location },
123	{ AtreidesEntry5.Location, AtreidesRally5.Location }
124}
125
126InitialAtreidesPaths =
127{
128	{ AConYard1.Location, AtreidesRally6.Location },
129	{ ARefinery2.Location, AtreidesRally7.Location },
130	{ AtreidesEntry6.Location, AtreidesRally8.Location }
131}
132
133InitialCorrinoPaths =
134{
135	{ CConYard.Location, CorrinoRally1.Location },
136	{ CHeavyFactory.Location, CorrinoRally2.Location },
137	{ CRefinery2.Location, CorrinoRally3.Location }
138}
139
140HarkonnenReinforcements = { "combat_tank_h", "combat_tank_h" }
141
142HarkonnenPath = { HarkonnenEntry.Location, HarkonnenRally.Location }
143
144SendStarportReinforcements = function()
145	Trigger.AfterDelay(CorrinoStarportDelay[Difficulty], function()
146		if CStarport.IsDead or CStarport.Owner ~= corrino_small then
147			return
148		end
149
150		reinforcements = Utils.Random(CorrinoStarportReinforcements[Difficulty])
151
152		local units = Reinforcements.ReinforceWithTransport(corrino_small, "frigate", reinforcements, { CorrinoStarportEntry.Location, CStarport.Location + CVec.New(1, 1) }, { CorrinoStarportExit.Location })[2]
153		Utils.Do(units, function(unit)
154			unit.AttackMove(AtreidesAttackLocation)
155			IdleHunt(unit)
156		end)
157
158		SendStarportReinforcements()
159	end)
160end
161
162SendHarkonnenReinforcements = function(delay)
163	Trigger.AfterDelay(delay, function()
164		Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", HarkonnenReinforcements, HarkonnenPath, { HarkonnenPath[1] })
165		Trigger.AfterDelay(DateTime.Seconds(5), function()
166			Media.PlaySpeechNotification(player, "Reinforce")
167		end)
168	end)
169end
170
171SendAirStrike = function()
172	if AHiTechFactory.IsDead or AHiTechFactory.Owner ~= atreides_main then
173		return
174	end
175
176	local targets = Utils.Where(player.GetActors(), function(actor)
177		return
178			actor.HasProperty("Sell") and
179			actor.Type ~= "wall" and
180			actor.Type ~= "medium_gun_turret" and
181			actor.Type ~= "large_gun_turret" and
182			actor.Type ~= "silo" and
183			actor.Type ~= "wind_trap"
184	end)
185
186	if #targets > 0 then
187		AHiTechFactory.SendAirstrike(Utils.Random(targets).CenterPosition, true, 0)
188	end
189
190	Trigger.AfterDelay(DateTime.Minutes(5), SendAirStrike)
191end
192
193BuildFremen = function()
194	if APalace.IsDead or APalace.Owner ~= atreides_main then
195		return
196	end
197
198	APalace.Produce("fremen")
199	APalace.Produce("fremen")
200
201	Trigger.AfterDelay(DateTime.Seconds(5), function()
202		IdleFremen = Utils.Where(atreides_main.GetActorsByType('fremen'), function(actor) return actor.IsIdle end)
203
204		if #IdleFremen >= FremenGroupSize[Difficulty] then
205			SendFremen()
206		end
207	end)
208
209	Trigger.AfterDelay(DateTime.Minutes(1) + DateTime.Seconds (30), BuildFremen)
210end
211
212SendFremen = function()
213	Utils.Do(IdleFremen, function(freman)
214		freman.AttackMove(AtreidesAttackLocation)
215		IdleHunt(freman)
216	end)
217end
218
219ChangeOwner = function(old_owner, new_owner)
220	local units = old_owner.GetActors()
221	Utils.Do(units, function(unit)
222		if not unit.IsDead then
223			unit.Owner = new_owner
224		end
225	end)
226end
227
228CheckSmugglerEnemies = function()
229	Utils.Do(SmugglerUnits, function(unit)
230		Trigger.OnDamaged(unit, function(self, attacker)
231			if unit.Owner == smuggler_neutral and attacker.Owner == player then
232				ChangeOwner(smuggler_neutral, smuggler_harkonnen)
233
234				--	Ensure that harvesters that was on a carryall switched sides.
235				Trigger.AfterDelay(DateTime.Seconds(15), function()
236					ChangeOwner(smuggler_neutral, smuggler_harkonnen)
237				end)
238			end
239
240			if unit.Owner == smuggler_ai and attacker.Owner == player then
241				ChangeOwner(smuggler_ai, smuggler_both)
242
243				--	Ensure that harvesters that was on a carryall switched sides.
244				Trigger.AfterDelay(DateTime.Seconds(15), function()
245					ChangeOwner(smuggler_ai, smuggler_both)
246				end)
247			end
248
249			if unit.Owner == smuggler_neutral and (attacker.Owner == atreides_main or attacker.Owner == atreides_small or attacker.Owner == corrino_main or attacker.Owner == corrino_small) then
250				ChangeOwner(smuggler_neutral, smuggler_ai)
251
252				--	Ensure that harvesters that was on a carryall switched sides.
253				Trigger.AfterDelay(DateTime.Seconds(15), function()
254					ChangeOwner(smuggler_neutral, smuggler_ai)
255				end)
256			end
257
258			if unit.Owner == smuggler_harkonnen and (attacker.Owner == atreides_main or attacker.Owner == atreides_small or attacker.Owner == corrino_main or attacker.Owner == corrino_small) then
259				ChangeOwner(smuggler_harkonnen, smuggler_both)
260
261				--	Ensure that harvesters that was on a carryall switched sides.
262				Trigger.AfterDelay(DateTime.Seconds(15), function()
263					ChangeOwner(smuggler_harkonnen, smuggler_both)
264				end)
265			end
266
267			if attacker.Owner == player and not message_check then
268
269				message_check = true
270				Media.DisplayMessage("The Smugglers are now hostile!", "Mentat")
271			end
272		end)
273	end)
274end
275
276Tick = function()
277	if player.HasNoRequiredUnits() then
278		atreides_main.MarkCompletedObjective(KillHarkonnen1)
279		atreides_small.MarkCompletedObjective(KillHarkonnen2)
280		corrino_main.MarkCompletedObjective(KillHarkonnen3)
281		corrino_small.MarkCompletedObjective(KillHarkonnen4)
282	end
283
284	if atreides_main.HasNoRequiredUnits() and atreides_small.HasNoRequiredUnits() and not player.IsObjectiveCompleted(KillAtreides) then
285		Media.DisplayMessage("The Atreides have been annihilated!", "Mentat")
286		player.MarkCompletedObjective(KillAtreides)
287	end
288
289	if corrino_main.HasNoRequiredUnits() and corrino_small.HasNoRequiredUnits() and not player.IsObjectiveCompleted(KillCorrino) then
290		Media.DisplayMessage("The Emperor has been annihilated!", "Mentat")
291		player.MarkCompletedObjective(KillCorrino)
292	end
293
294	if smuggler_neutral.HasNoRequiredUnits() and smuggler_harkonnen.HasNoRequiredUnits() and smuggler_ai.HasNoRequiredUnits() and smuggler_both.HasNoRequiredUnits() and not SmugglersKilled then
295		Media.DisplayMessage("The Smugglers have been annihilated!", "Mentat")
296		SmugglersKilled = true
297	end
298
299	local playerConYards = player.GetActorsByType("construction_yard")
300	if #playerConYards > 0 and not player.IsObjectiveCompleted(DeployMCV) then
301		player.MarkCompletedObjective(DeployMCV)
302	end
303
304	if DateTime.GameTime % DateTime.Seconds(10) == 0 and LastHarvesterEaten[atreides_main] then
305		local units = atreides_main.GetActorsByType("harvester")
306
307		if #units > 0 then
308			LastHarvesterEaten[atreides_main] = false
309			ProtectHarvester(units[1], atreides_main, AttackGroupSize[Difficulty])
310		end
311	end
312
313	if DateTime.GameTime % DateTime.Seconds(10) == 0 and LastHarvesterEaten[atreides_small] then
314		local units = atreides_small.GetActorsByType("harvester")
315
316		if #units > 0 then
317			LastHarvesterEaten[atreides_small] = false
318			ProtectHarvester(units[1], atreides_small, AttackGroupSize[Difficulty])
319		end
320	end
321
322	if DateTime.GameTime % DateTime.Seconds(10) == 0 and LastHarvesterEaten[corrino_main] then
323		local units = corrino_main.GetActorsByType("harvester")
324
325		if #units > 0 then
326			LastHarvesterEaten[corrino_main] = false
327			ProtectHarvester(units[1], corrino_main, AttackGroupSize[Difficulty])
328		end
329	end
330
331	if DateTime.GameTime % DateTime.Seconds(10) == 0 and LastHarvesterEaten[corrino_small] then
332		local units = corrino_small.GetActorsByType("harvester")
333
334		if #units > 0 then
335			LastHarvesterEaten[corrino_small] = false
336			ProtectHarvester(units[1], corrino_small, AttackGroupSize[Difficulty])
337		end
338	end
339end
340
341WorldLoaded = function()
342	atreides_main = Player.GetPlayer("Atreides Main Base")
343	atreides_small = Player.GetPlayer("Atreides Small Base")
344	corrino_main = Player.GetPlayer("Corrino Main Base")
345	corrino_small = Player.GetPlayer("Corrino Small Base")
346	smuggler_neutral = Player.GetPlayer("Smugglers - Neutral")
347	smuggler_harkonnen = Player.GetPlayer("Smugglers - Enemy to Harkonnen")
348	smuggler_ai = Player.GetPlayer("Smugglers - Enemy to AI")
349	smuggler_both = Player.GetPlayer("Smugglers - Enemy to Both")
350	player = Player.GetPlayer("Harkonnen")
351
352	InitObjectives(player)
353	DeployMCV = player.AddSecondaryObjective("Build an MCV and deploy it into a Construction Yard.")
354	KillAtreides = player.AddPrimaryObjective("Destroy the Atreides.")
355	KillCorrino = player.AddPrimaryObjective("Destroy the Imperial Forces.")
356	KillHarkonnen1 = atreides_main.AddPrimaryObjective("Kill all Harkonnen units.")
357	KillHarkonnen2 = atreides_small.AddPrimaryObjective("Kill all Harkonnen units.")
358	KillHarkonnen3 = corrino_main.AddPrimaryObjective("Kill all Harkonnen units.")
359	KillHarkonnen4 = corrino_small.AddPrimaryObjective("Kill all Harkonnen units.")
360
361	-- Wait for carryall drop
362	Trigger.AfterDelay(DateTime.Seconds(10), function()
363		SmugglerUnits = smuggler_neutral.GetActors()
364		CheckSmugglerEnemies()
365	end)
366
367	Camera.Position = HBarracks.CenterPosition
368	AtreidesAttackLocation = HBarracks.Location
369
370	Trigger.AfterDelay(DateTime.Minutes(5), SendAirStrike)
371	Trigger.AfterDelay(DateTime.Minutes(1) + DateTime.Seconds (30), BuildFremen)
372
373	Trigger.OnAllKilledOrCaptured(AtreidesMainBase, function()
374		Utils.Do(atreides_main.GetGroundAttackers(), IdleHunt)
375	end)
376
377	Trigger.OnAllKilledOrCaptured(AtreidesSmallBase, function()
378		Utils.Do(atreides_small.GetGroundAttackers(), IdleHunt)
379	end)
380
381	Trigger.OnAllKilledOrCaptured(CorrinoMainBase, function()
382		Utils.Do(corrino_main.GetGroundAttackers(), IdleHunt)
383	end)
384
385	Trigger.OnAllKilledOrCaptured(CorrinoSmallBase, function()
386		Utils.Do(corrino_small.GetGroundAttackers(), IdleHunt)
387	end)
388
389	local path = function() return Utils.Random(AtreidesPaths) end
390	local waveCondition = function() return player.IsObjectiveCompleted(KillAtreides) end
391	local huntFunction = function(unit)
392		unit.AttackMove(AtreidesAttackLocation)
393		IdleHunt(unit)
394	end
395	SendCarryallReinforcements(atreides_main, 0, AtreidesAttackWaves[Difficulty], AtreidesAttackDelay[Difficulty], path, AtreidesReinforcements[Difficulty], waveCondition, huntFunction)
396
397	SendStarportReinforcements()
398
399	Actor.Create("upgrade.barracks", true, { Owner = atreides_main })
400	Actor.Create("upgrade.light", true, { Owner = atreides_main })
401	Actor.Create("upgrade.heavy", true, { Owner = atreides_main })
402	Actor.Create("upgrade.hightech", true, { Owner = atreides_main })
403	Actor.Create("upgrade.barracks", true, { Owner = atreides_small })
404	Actor.Create("upgrade.light", true, { Owner = atreides_small })
405	Actor.Create("upgrade.heavy", true, { Owner = atreides_small })
406	Actor.Create("upgrade.barracks", true, { Owner = corrino_main })
407	Actor.Create("upgrade.heavy", true, { Owner = corrino_main })
408	Actor.Create("upgrade.barracks", true, { Owner = corrino_small })
409	Actor.Create("upgrade.light", true, { Owner = corrino_small })
410	Trigger.AfterDelay(0, ActivateAI)
411
412	SendHarkonnenReinforcements(DateTime.Minutes(2))
413end
414