332 lines
11 KiB
Lua
332 lines
11 KiB
Lua
--[[
|
|
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
|