--[[ Copyright (c) The OpenRA Developers and Contributors This file is part of OpenRA, which is free software. It is made available to you under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. For more information, see COPYING. Mission Overview: Soviet10 features an escort sequence where non-player controlled convoy trucks follow the nearest player controlled ground unit. This is handled by TrucksFollow() and its related functions. A timer which was linked to capturing buildings or destroying the first conyard has been changed to starting after a few minutes set by difficulty level. The conesquences of having the timer run out have been increased as well, surrounding the player with several enemy units instead of sending just four tanks. The starting number of tanks has been altered as well, with hard difficulty having the original amount of four. Ommissions from original: 1. Radar Dome had a sell trigger when 3 rockets north of it were killed. Plan to add when if/when we add sell triggers across all missions. 2. Turrets at gauntlet rebuild after being destroyed. Plan to add if/when we fix/add base rebuilding across all miissions. 3. At gauntlet, all four north side turrets in were killed by barrels. Omitted for balance, add when #2 is addressed. 4. At gauntlet, two of the south side turrets to the west end were killed by barrels. Omitted for balance, add when #2 is addressed. ]] ConvoyEscort = { easy = { "3tnk", "3tnk", "3tnk", "3tnk", "3tnk", "3tnk" }, normal = { "3tnk", "3tnk", "3tnk", "3tnk", "3tnk" }, hard = { "3tnk", "3tnk", "3tnk", "3tnk" } } StartingPlanes = { Mig1, Mig2, Yak1, Yak2, Yak3 } Airfields = { Airfield1, Airfield2, Airfield3, Airfield4, Airfield5 } Migs = { Mig1, Mig2 } ConvoyTrucks = { "truk", "truk", "truk" } LightVehicleAttack = { Jeep1, Jeep2, Ltnk } LightVehicleAttackTrigger = { CPos.New(94, 55), CPos.New(94, 56), CPos.New(94, 57), CPos.New(94, 58), CPos.New(94, 59), CPos.New(94, 70), CPos.New(94, 71), CPos.New(94, 72), CPos.New(94, 73) } WinTriggerArea = { CPos.New(16, 55), CPos.New(16, 56), CPos.New(16, 57), CPos.New(16, 58), CPos.New(16, 59), CPos.New(16, 60), CPos.New(16, 61), CPos.New(16, 62), CPos.New(16, 63), CPos.New(16, 64), CPos.New(16, 65), CPos.New(16, 66) } EngiDropTriggerArea = { CPos.New(66, 51), CPos.New(66, 52), CPos.New(66, 53), CPos.New(66, 54), CPos.New(66, 55), CPos.New(66, 67), CPos.New(66, 68), CPos.New(66, 69), CPos.New(66, 70) } ChinookAmbushTrigger = { CPos.New(54, 70), CPos.New(55, 70), CPos.New(56, 70), CPos.New(57, 70), CPos.New(58, 70), CPos.New(59, 70), CPos.New(60, 70), CPos.New(61, 70) } ChinookChalk = { "e3", "e3", "e3", "e3", "e3" } ChinookPath = { ChinookEntry.Location, ChinookLZ.Location } PatrolA = { "e1", "e1", "e1", "e1", "e1" } PatrolB = { "e1", "e1", "e1", "e3", "e3" } PatrolAPath = { PatrolA1.Location, PatrolA2.Location, PatrolA3.Location, PatrolA4.Location } PatrolBPath = { PatrolB1.Location, PatrolB2.Location, PatrolB3.Location, PatrolB4.Location } HunterTeam = { "1tnk", "1tnk", "1tnk", "1tnk", "2tnk", "2tnk", "arty" } StartTimer = false TimerColor = Player.GetPlayer("USSR").Color TimerTicks = DateTime.Minutes(9) Ticked = TimerTicks StartTimerDelay = { easy = DateTime.Minutes(5), normal = DateTime.Minutes(3), hard = DateTime.Minutes(1) } MissionStart = function() Utils.Do(StartingPlanes, function(a) a.ReturnToBase() end) Trigger.AfterDelay(DateTime.Seconds(1), function() StartTank.AttackMove(TrucksStop.Location) Reinforcements.Reinforce(USSR, ConvoyEscort, { ConvoyEntry.Location, TanksStop.Location }) Media.PlaySpeechNotification(USSR, "ConvoyApproaching") end) Trigger.AfterDelay(DateTime.Seconds(5), function() local patrol1 = Reinforcements.Reinforce(Greece, PatrolA, { SouthEntry.Location, PatrolA1.Location }) Utils.Do(patrol1, function(unit) Trigger.OnIdle(unit, function(patrols) patrols.Patrol(PatrolAPath, true, 80) end) end) end) Trigger.AfterDelay(DateTime.Seconds(5), function() local patrol2 = Reinforcements.Reinforce(Greece, PatrolB, { NorthEntry.Location, PatrolB1.Location }) Utils.Do(patrol2, function(unit) Trigger.OnIdle(unit, function(patrols) patrols.Patrol(PatrolBPath, true, 80) end) end) end) Trigger.AfterDelay(DateTime.Seconds(8), function() Trucks = Reinforcements.Reinforce(Turkey, ConvoyTrucks, { ConvoyEntry.Location, TrucksStop.Location }) TrucksFollow() Trigger.OnAnyKilled(Trucks, function() USSR.MarkFailedObjective(ProtectEveryTruck) end) end) end MissionSetup = function() Trigger.OnEnteredFootprint(WinTriggerArea, function(a, id) if a.Type == "truk" then -- Stop truck from looping through TrucksFollow a.AddTag("Exiting") a.Stop() Trigger.OnIdle(a, function() a.Move(Exit.Location) end) end end) Trigger.OnEnteredFootprint({ Exit.Location }, function(a, id) if a.Type == "truk" then a.Stop() a.Destroy() if not USSR.IsObjectiveCompleted(EscortTrucks) then USSR.MarkCompletedObjective(EscortTrucks) if not USSR.IsObjectiveFailed(ProtectEveryTruck) then USSR.MarkCompletedObjective(ProtectEveryTruck) end if not USSR.IsObjectiveFailed(SaveMigs) then USSR.MarkCompletedObjective(SaveMigs) end end end end) Utils.Do(Airfields, function(runway) Trigger.OnProduction(runway, function(a, b) if b.Type == "mig" then Trigger.OnKilled(b, function() USSR.MarkFailedObjective(SaveMigs) end) end end) end) Trigger.OnAnyKilled(Migs, function() USSR.MarkFailedObjective(SaveMigs) end) Trigger.OnKilled(AABarrel, function() if not AAGun.IsDead then AAGun.Kill() end end) Trigger.OnKilled(HealCrateBarrel, function() Actor.Create("healcrate", true, { Owner = USSR, Location = HealCrateBarrel.Location }) end) Trigger.OnEnteredFootprint(LightVehicleAttackTrigger, function(unit, id) if not AttackTriggered and unit.Type == "3tnk" then Trigger.RemoveFootprintTrigger(id) AttackTriggered = true Utils.Do(LightVehicleAttack, function(unit) if not unit.IsDead then IdleHunt(unit) end end) end end) Trigger.OnEnteredFootprint(EngiDropTriggerArea, function(unit, id) if unit.Owner == USSR and not DropTriggered then Trigger.RemoveFootprintTrigger(id) DropTriggered = true Media.PlaySpeechNotification(USSR, "SignalFlare") local engiFlare = Actor.Create("flare", true, { Owner = USSR, Location = EngiDropLZ.Location }) Trigger.AfterDelay(DateTime.Seconds(2), function() Engidrop.TargetParatroopers(EngiDropLZ.CenterPosition, Angle.NorthWest) end) Trigger.AfterDelay(DateTime.Seconds(20), function() engiFlare.Destroy() end) end end) Trigger.OnEnteredFootprint(ChinookAmbushTrigger, function(unit, id) if not ChinookTriggered and unit.Owner == USSR then Trigger.RemoveFootprintTrigger(id) ChinookTriggered = true local chalk = Reinforcements.ReinforceWithTransport(Greece, "tran", ChinookChalk , ChinookPath, { ChinookPath[1] })[2] Utils.Do(chalk, function(unit) Trigger.OnAddedToWorld(unit, IdleHunt) end) end end) end TrucksFollow = function() Utils.Do(Trucks, function(truck) if not truck.IsDead and not Running and not truck.HasTag("Exiting") then -- Trucks that are next to a guard don't need to move local guards = Map.ActorsInCircle(truck.CenterPosition, WDist.New(2048), IsValidConvoyGuard) if #guards == 0 then -- Search nearby area to find a new guard guards = Map.ActorsInCircle(truck.CenterPosition, WDist.New(6144), IsValidConvoyGuard) if #guards == 0 then -- Search the whole map as a last resort: this is slow! guards = Utils.Where(Map.ActorsInWorld, IsValidConvoyGuard) end if #guards == 0 then -- Runs on the first truck; returns to not loop over the rest. RunForIt() return end truck.Move(ClosestActorTo(guards, truck).Location, 3) end end end) Trigger.AfterDelay(DateTime.Seconds(2), TrucksFollow) end IsValidConvoyGuard = function(actor) -- Ground units (not aircraft) owned by USSR return actor.Owner == USSR and actor.HasProperty("Move") and not actor.HasProperty("Land") end ClosestActorTo = function(actors, target) local closestDistSq = 0 local closestActor = nil Utils.Do(actors, function(actor) local offset = actor.Location - target.Location local distSq = offset.X * offset.X + offset.Y * offset.Y if closestActor == nil or distSq < closestDistSq then closestDistSq = distSq closestActor = actor end end) return closestActor end RunForIt = function() Running = true Media.PlaySoundNotification(USSR, "AlertBleep") Media.DisplayMessage(UserInterface.Translate("run-for-it"), UserInterface.Translate("convoy-commander")) Utils.Do(Trucks, function(truck) if not truck.IsDead then truck.Stop() Trigger.OnIdle(truck, function(a) a.Move(Exit.Location) end) end end) end SendHunters = function() Media.PlaySpeechNotification(USSR, "AlliedForcesApproaching") local Hunters1 = Reinforcements.Reinforce(Greece, HunterTeam, { ConvoyEntry.Location, TrucksStop.Location }) local Hunters2 = Reinforcements.Reinforce(Greece, HunterTeam, { SouthEntry.Location, PatrolA1.Location }) local Hunters3 = Reinforcements.Reinforce(Greece, HunterTeam, { NorthEntry.Location, PatrolB1.Location }) local Hunters4 = Reinforcements.Reinforce(Greece, HunterTeam, { Exit.Location, PatrolB2.Location }) Utils.Do(Hunters1, IdleHunt) Utils.Do(Hunters2, IdleHunt) Utils.Do(Hunters3, IdleHunt) Utils.Do(Hunters4, IdleHunt) end StartTimerFunction = function() StartTimer = true Media.PlaySpeechNotification(USSR, "TimerStarted") Trigger.AfterDelay(DateTime.Minutes(5), function() Media.PlaySpeechNotification(USSR, "WarningFourMinutesRemaining") end) Trigger.AfterDelay(DateTime.Minutes(7), function() Media.PlaySpeechNotification(USSR, "WarningTwoMinutesRemaining") end) Trigger.AfterDelay(DateTime.Minutes(8), function() Media.PlaySpeechNotification(USSR, "WarningOneMinuteRemaining") end) end WeAreSurrounded = UserInterface.Translate("we-are-surrounded") FinishTimer = function() for i = 0, 5, 1 do local c = TimerColor if i % 2 == 0 then c = HSLColor.White end Trigger.AfterDelay(DateTime.Seconds(i), function() UserInterface.SetMissionText(WeAreSurrounded, c) end) end Trigger.AfterDelay(DateTime.Seconds(6), function() UserInterface.SetMissionText("") end) end Tick = function() if StartTimer then if Ticked > 0 then if (Ticked % DateTime.Seconds(1)) == 0 then Timer = UserInterface.Translate("corridor-closes-in", { ["time"] = Utils.FormatTime(Ticked) }) UserInterface.SetMissionText(Timer, TimerColor) end Ticked = Ticked - 1 elseif Ticked == 0 then FinishTimer() SendHunters() Ticked = Ticked - 1 end end if Turkey.UnitsLost == 3 then USSR.MarkFailedObjective(EscortTrucks) end end WorldLoaded = function() USSR = Player.GetPlayer("USSR") Greece = Player.GetPlayer("Greece") Turkey = Player.GetPlayer("Turkey") InitObjectives(USSR) EscortTrucks = AddPrimaryObjective(USSR, "escort-convoy-mountain-pass") ProtectEveryTruck = AddSecondaryObjective(USSR, "protect-every-truck") SaveMigs = AddSecondaryObjective(USSR, "save-migs") BeatUSSR = AddPrimaryObjective(Greece, "") ConvoyEscort = ConvoyEscort[Difficulty] StartTimerDelay = StartTimerDelay[Difficulty] MissionStart() MissionSetup() Trigger.AfterDelay(StartTimerDelay, StartTimerFunction) Engidrop = Actor.Create("engidrop", false, { Owner = USSR }) Camera.Position = DefaultCameraPosition.CenterPosition end