429 lines
15 KiB
Lua
429 lines
15 KiB
Lua
--[[
|
|
Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
|
|
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.
|
|
]]
|
|
|
|
if Difficulty == "easy" then
|
|
AttackAtFrameIncrement = DateTime.Seconds(22)
|
|
AttackAtFrameIncrementInf = DateTime.Seconds(16)
|
|
TimerTicks = DateTime.Minutes(15)
|
|
IncrementTurningPoint = TimerTicks / 2
|
|
DamageModifier = 0.5
|
|
LongBowReinforcements = { "heli", "heli" }
|
|
ParadropArtillery = true
|
|
elseif Difficulty == "normal" then
|
|
AttackAtFrameIncrement = DateTime.Seconds(18)
|
|
AttackAtFrameIncrementInf = DateTime.Seconds(12)
|
|
TimerTicks = DateTime.Minutes(20)
|
|
IncrementTurningPoint = TimerTicks / 2
|
|
MoreParas = true
|
|
DamageModifier = 0.75
|
|
LongBowReinforcements = { "heli", "heli" }
|
|
else --Difficulty == "hard"
|
|
AttackAtFrameIncrement = DateTime.Seconds(14)
|
|
AttackAtFrameIncrementInf = DateTime.Seconds(8)
|
|
TimerTicks = DateTime.Minutes(25)
|
|
IncrementTurningPoint = DateTime.Minutes(10)
|
|
MoreParas = true
|
|
AttackAtFrameNaval = DateTime.Minutes(3) + DateTime.Seconds(45)
|
|
SpawnNavalUnits = true
|
|
DamageModifier = 1
|
|
LongBowReinforcements = { "heli" }
|
|
end
|
|
|
|
AlliedAirReinforcementsWaypoints =
|
|
{
|
|
{ AirReinforcementsEntry1.Location, AirReinforcementsRally1.Location },
|
|
{ AirReinforcementsEntry2.Location, AirReinforcementsRally2.Location }
|
|
}
|
|
FrenchReinforcements = { "2tnk", "2tnk", "2tnk", "2tnk", "2tnk", "1tnk", "1tnk", "1tnk", "arty", "arty", "arty", "jeep", "jeep" }
|
|
|
|
SpawningSovietUnits = true
|
|
SpawningInfantry = true
|
|
AttackAtFrameInf = DateTime.Seconds(12)
|
|
AttackAtFrame = DateTime.Seconds(18)
|
|
SovietAttackGroupSize = 5
|
|
SovietInfantryGroupSize = 7
|
|
FactoryClearRange = 10
|
|
ParadropTicks = DateTime.Seconds(30)
|
|
ParadropWaypoints =
|
|
{
|
|
{ Angle.East + Angle.New(16), ParaDrop1},
|
|
{ Angle.East - Angle.New(16), ParaDrop2},
|
|
{ Angle.East + Angle.New(16), Alliesbase2},
|
|
{ Angle.East - Angle.New(16), Alliesbase1}
|
|
}
|
|
NavalTransportPassengers = { "e1", "e1", "e2", "e4", "e4" }
|
|
NavalReinforcementsWaypoints = { NavalWaypoint1, NavalWaypoint2, NavalWaypoint2, NavalWaypoint3 }
|
|
Squad1 = { "e1", "e1" }
|
|
Squad2 = { "e2", "e2" }
|
|
SovietVehicles = { "3tnk", "3tnk", "3tnk", "3tnk", "3tnk", "3tnk", "v2rl", "v2rl", "ftrk", "ftrk", "ftrk", "apc", "apc" }
|
|
SovietInfantry = { "e1", "e1", "e1", "e1", "e2", "e2", "e2", "e4", "e4", "e3" }
|
|
SovietEntryPoints = { SovietEntryPoint1, SovietEntryPoint2, SovietEntryPoint3, SovietEntryPoint4, SovietEntryPoint5 }
|
|
SovietRallyPoints = { SovietRallyPoint1, SovietRallyPoint2, SovietRallyPoint3, SovietRallyPoint4, SovietRallyPoint5 }
|
|
SovietGateRallyPoints = { AlliesBaseGate2, AlliesBaseGate2, AlliesBaseGate1, AlliesBaseGate1, AlliesBaseGate1 }
|
|
|
|
Airfields = { SovietAirfield1, SovietAirfield2, SovietAirfield3 }
|
|
SovietBuildings = { Barrack1, SubPen, RadarDome, AdvancedPowerPlant1, AdvancedPowerPlant2, AdvancedPowerPlant3, WarFactory, Refinery, Silo1, Silo2, FlameTower1, FlameTower2, FlameTower3, Sam1, Sam2, Sam3, Sam4, SovietAirfield1, SovietAirfield2, SovietAirfield3 }
|
|
|
|
IdleTrigger = function(units, dest)
|
|
Utils.Do(units, function(unit)
|
|
|
|
if not unit.IsDead then
|
|
Trigger.OnIdle(unit, function()
|
|
local bool = Utils.All(units, function(unit) return unit.IsIdle end)
|
|
if bool then
|
|
SetupHuntTrigger(units)
|
|
end
|
|
end)
|
|
|
|
Trigger.OnDamaged(unit, function()
|
|
SetupHuntTrigger(units)
|
|
end)
|
|
|
|
Trigger.OnCapture(unit, function()
|
|
Trigger.ClearAll(unit)
|
|
end)
|
|
end
|
|
end)
|
|
end
|
|
|
|
SetupHuntTrigger = function(units)
|
|
Utils.Do(units, function(unit)
|
|
if not unit.IsDead then
|
|
Trigger.ClearAll(unit)
|
|
Trigger.AfterDelay(0, function()
|
|
if not unit.IsDead then
|
|
Trigger.OnIdle(unit, unit.Hunt)
|
|
Trigger.OnCapture(unit, function()
|
|
Trigger.ClearAll(unit)
|
|
end)
|
|
end
|
|
end)
|
|
end
|
|
end)
|
|
end
|
|
|
|
ticked = TimerTicks
|
|
Tick = function()
|
|
if KillObj and soviets.HasNoRequiredUnits() then
|
|
allies.MarkCompletedObjective(KillObj)
|
|
end
|
|
|
|
if allies.HasNoRequiredUnits() then
|
|
soviets.MarkCompletedObjective(SovietObj)
|
|
end
|
|
|
|
if soviets.Resources > soviets.ResourceCapacity / 2 then
|
|
soviets.Resources = soviets.ResourceCapacity / 2
|
|
end
|
|
|
|
if ticked > 0 then
|
|
if DateTime.Minutes(20) == ticked then
|
|
Media.PlaySpeechNotification(allies, "TwentyMinutesRemaining")
|
|
|
|
elseif DateTime.Minutes(10) == ticked then
|
|
Media.PlaySpeechNotification(allies, "TenMinutesRemaining")
|
|
|
|
elseif DateTime.Minutes(5) == ticked then
|
|
Media.PlaySpeechNotification(allies, "WarningFiveMinutesRemaining")
|
|
|
|
elseif DateTime.Minutes(4) == ticked then
|
|
Media.PlaySpeechNotification(allies, "WarningFourMinutesRemaining")
|
|
|
|
Trigger.AfterDelay(ParadropTicks, function()
|
|
SendSovietParadrops(ParadropWaypoints[3])
|
|
SendSovietParadrops(ParadropWaypoints[2])
|
|
end)
|
|
Trigger.AfterDelay(ParadropTicks * 2, function()
|
|
SendSovietParadrops(ParadropWaypoints[4])
|
|
SendSovietParadrops(ParadropWaypoints[1])
|
|
end)
|
|
|
|
elseif DateTime.Minutes(3) == ticked then
|
|
Media.PlaySpeechNotification(allies, "WarningThreeMinutesRemaining")
|
|
|
|
elseif DateTime.Minutes(2) == ticked then
|
|
Media.PlaySpeechNotification(allies, "WarningTwoMinutesRemaining")
|
|
|
|
AttackAtFrameIncrement = DateTime.Seconds(4)
|
|
AttackAtFrameIncrementInf = DateTime.Seconds(4)
|
|
|
|
elseif DateTime.Minutes(1) == ticked then
|
|
Media.PlaySpeechNotification(allies, "WarningOneMinuteRemaining")
|
|
|
|
elseif DateTime.Seconds(45) == ticked then
|
|
Media.PlaySpeechNotification(allies, "AlliedForcesApproaching")
|
|
end
|
|
|
|
UserInterface.SetMissionText("French reinforcements arrive in " .. Utils.FormatTime(ticked), TimerColor)
|
|
ticked = ticked - 1
|
|
elseif ticked == 0 then
|
|
FinishTimer()
|
|
TimerExpired()
|
|
ticked = ticked - 1
|
|
end
|
|
end
|
|
|
|
FinishTimer = function()
|
|
for i = 0, 9, 1 do
|
|
local c = TimerColor
|
|
if i % 2 == 0 then
|
|
c = HSLColor.White
|
|
end
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(i), function() UserInterface.SetMissionText("Our french allies have arrived!", c) end)
|
|
end
|
|
Trigger.AfterDelay(DateTime.Seconds(10), function() UserInterface.SetMissionText("") end)
|
|
end
|
|
|
|
SendSovietParadrops = function(table)
|
|
local aircraft = powerproxy.TargetParatroopers(table[2].CenterPosition, table[1])
|
|
Utils.Do(aircraft, function(a)
|
|
Trigger.OnPassengerExited(a, function(t, p)
|
|
IdleHunt(p)
|
|
end)
|
|
end)
|
|
end
|
|
|
|
SendSovietNavalReinforcements = function()
|
|
if SpawnNavalUnits then
|
|
local entry = NavalEntryPoint.Location
|
|
local units = Reinforcements.ReinforceWithTransport(soviets, "lst", NavalTransportPassengers, { entry, Utils.Random(NavalReinforcementsWaypoints).Location }, { entry })[2]
|
|
Utils.Do(units, function(unit)
|
|
Trigger.OnIdle(unit, unit.Hunt)
|
|
end)
|
|
|
|
local delay = Utils.RandomInteger(AttackAtFrameNaval, AttackAtFrameNaval + DateTime.Minutes(2))
|
|
|
|
Trigger.AfterDelay(delay, SendSovietNavalReinforcements)
|
|
end
|
|
end
|
|
|
|
SpawnSovietInfantry = function()
|
|
local units = { }
|
|
for i = 0, SovietInfantryGroupSize - 1, 1 do
|
|
local type = Utils.Random(SovietInfantry)
|
|
units[i] = type
|
|
end
|
|
|
|
soviets.Build(units, function(soldiers)
|
|
Trigger.AfterDelay(25, function() IdleTrigger(soldiers) end)
|
|
end)
|
|
end
|
|
|
|
SpawnSovietUnits = function()
|
|
local units = { }
|
|
for i = 0, SovietAttackGroupSize - 1, 1 do
|
|
local type = Utils.Random(SovietVehicles)
|
|
units[i] = type
|
|
end
|
|
|
|
local route = Utils.RandomInteger(1, #SovietEntryPoints + 1)
|
|
local attackers = Reinforcements.Reinforce(soviets, units, { SovietEntryPoints[route].Location, SovietRallyPoints[route].Location })
|
|
Trigger.AfterDelay(25, function()
|
|
IdleTrigger(attackers, SovietGateRallyPoints[route].Location)
|
|
end)
|
|
end
|
|
|
|
SendInfantryWave = function()
|
|
if SpawningInfantry then
|
|
SpawnSovietInfantry()
|
|
|
|
if DateTime.GameTime < IncrementTurningPoint then
|
|
AttackAtFrameIncrementInf = AttackAtFrameIncrementInf + Utils.RandomInteger(DateTime.Seconds(2), DateTime.Seconds(3))
|
|
elseif not (AttackAtFrameIncrementInf <= DateTime.Seconds(4)) then
|
|
AttackAtFrameIncrementInf = AttackAtFrameIncrementInf - Utils.RandomInteger(DateTime.Seconds(2), DateTime.Seconds(3))
|
|
end
|
|
|
|
Trigger.AfterDelay(AttackAtFrameInf + AttackAtFrameIncrementInf, SendInfantryWave)
|
|
end
|
|
end
|
|
|
|
SendVehicleWave = function()
|
|
if SpawningSovietUnits then
|
|
SpawnSovietUnits()
|
|
|
|
if DateTime.GameTime < IncrementTurningPoint then
|
|
AttackAtFrameIncrement = AttackAtFrameIncrement + Utils.RandomInteger(DateTime.Seconds(4), DateTime.Seconds(6))
|
|
elseif not (AttackAtFrameIncrement <= DateTime.Seconds(4)) then
|
|
AttackAtFrameIncrement = AttackAtFrameIncrement - Utils.RandomInteger(DateTime.Seconds(4), DateTime.Seconds(6))
|
|
end
|
|
|
|
Trigger.AfterDelay(AttackAtFrame + AttackAtFrameIncrement, SendVehicleWave)
|
|
end
|
|
end
|
|
|
|
TimerExpired = function()
|
|
SpawningSovietUnits = false
|
|
SpawningInfantry = false
|
|
SpawnNavalUnits = false
|
|
|
|
Beacon.New(allies, SovietEntryPoint7.CenterPosition - WVec.New(3 * 1024, 0, 0))
|
|
Media.PlaySpeechNotification(allies, "AlliedReinforcementsArrived")
|
|
Reinforcements.Reinforce(allies, FrenchReinforcements, { SovietEntryPoint7.Location, Alliesbase.Location })
|
|
|
|
if DestroyObj then
|
|
KillObj = allies.AddObjective("Take control of French reinforcements and\nkill all remaining Soviet forces.")
|
|
else
|
|
DestroyObj = allies.AddObjective("Take control of French reinforcements and\ndismantle the nearby Soviet base.")
|
|
end
|
|
|
|
allies.MarkCompletedObjective(SurviveObj)
|
|
if not allies.IsObjectiveCompleted(KillSams) then
|
|
allies.MarkFailedObjective(KillSams)
|
|
end
|
|
end
|
|
|
|
DropAlliedArtillery = function(facing, dropzone)
|
|
local proxy = Actor.Create("powerproxy.allied", true, { Owner = allies })
|
|
proxy.TargetParatroopers(dropzone, facing)
|
|
proxy.Destroy()
|
|
end
|
|
|
|
SendLongBowReinforcements = function()
|
|
Media.PlaySpeechNotification(allies, "AlliedReinforcementsArrived")
|
|
Reinforcements.Reinforce(allies, LongBowReinforcements, AlliedAirReinforcementsWaypoints[1])
|
|
Reinforcements.Reinforce(allies, LongBowReinforcements, AlliedAirReinforcementsWaypoints[2])
|
|
|
|
if ParadropArtillery then
|
|
local facing = Angle.New(Utils.RandomInteger(128, 384))
|
|
DropAlliedArtillery(facing, Alliesbase.CenterPosition)
|
|
end
|
|
end
|
|
|
|
InitObjectives = function()
|
|
Trigger.OnObjectiveAdded(allies, function(p, id)
|
|
Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective")
|
|
end)
|
|
|
|
SurviveObj = allies.AddObjective("Enforce your position and hold-out the onslaught\nuntil reinforcements arrive.")
|
|
KillSams = allies.AddObjective("Destroy the two SAM sites before reinforcements\narrive.", "Secondary", false)
|
|
Media.DisplayMessage("The Soviets are blocking our GPS. We need to investigate their new technology.")
|
|
CaptureAirfields = allies.AddObjective("Capture and hold the Soviet airbase\nin the northeast.", "Secondary", false)
|
|
SovietObj = soviets.AddObjective("Eliminate all Allied forces.")
|
|
|
|
Trigger.OnObjectiveCompleted(allies, function(p, id)
|
|
Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed")
|
|
end)
|
|
Trigger.OnObjectiveFailed(allies, function(p, id)
|
|
Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed")
|
|
end)
|
|
|
|
Trigger.OnPlayerLost(allies, function()
|
|
Media.PlaySpeechNotification(allies, "MissionFailed")
|
|
end)
|
|
Trigger.OnPlayerWon(allies, function()
|
|
Media.PlaySpeechNotification(allies, "MissionAccomplished")
|
|
Media.DisplayMessage("The French forces have survived and dismantled the Soviet presence in the area!")
|
|
end)
|
|
end
|
|
|
|
InitMission = function()
|
|
Camera.Position = Alliesbase.CenterPosition
|
|
camera1 = Actor.Create("camera.sam", true, { Owner = allies, Location = Sam1.Location })
|
|
camera2 = Actor.Create("camera.sam", true, { Owner = allies, Location = Sam2.Location })
|
|
Trigger.OnKilled(Sam1, function()
|
|
if camera1.IsInWorld then camera1.Destroy() end
|
|
end)
|
|
Trigger.OnKilled(Sam2, function()
|
|
if camera2.IsInWorld then camera2.Destroy() end
|
|
end)
|
|
Trigger.OnAllKilledOrCaptured({ Sam1, Sam2 }, function()
|
|
if not allies.IsObjectiveFailed(KillSams) then
|
|
allies.MarkCompletedObjective(KillSams)
|
|
SendLongBowReinforcements()
|
|
end
|
|
end)
|
|
|
|
local count = 0
|
|
Utils.Do(Airfields, function(field)
|
|
Trigger.OnCapture(field, function()
|
|
count = count + 1
|
|
if count == #Airfields then
|
|
allies.MarkCompletedObjective(CaptureAirfields)
|
|
local atek = Actor.Create("atek.mission", true, { Owner = allies, Location = HiddenATEK.Location })
|
|
Trigger.AfterDelay(DateTime.Seconds(5), atek.Destroy)
|
|
end
|
|
end)
|
|
Trigger.OnKilled(field, function()
|
|
allies.MarkFailedObjective(CaptureAirfields)
|
|
end)
|
|
end)
|
|
|
|
Trigger.OnAllKilledOrCaptured(SovietBuildings, function()
|
|
if DestroyObj then
|
|
if not soviets.HasNoRequiredUnits() then
|
|
KillObj = allies.AddObjective("Kill all remaining Soviet forces.")
|
|
end
|
|
allies.MarkCompletedObjective(DestroyObj)
|
|
else
|
|
DestroyObj = allies.AddObjective("Dismantle the nearby Soviet base.")
|
|
allies.MarkCompletedObjective(DestroyObj)
|
|
end
|
|
end)
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(1), function() Media.PlaySpeechNotification(allies, "MissionTimerInitialised") end)
|
|
TimerColor = allies.Color
|
|
end
|
|
|
|
SetupSoviets = function()
|
|
Barrack1.IsPrimaryBuilding = true
|
|
Barrack1.RallyPoint = SovietInfantryRally1.Location
|
|
Trigger.OnKilledOrCaptured(Barrack1, function()
|
|
SpawningInfantry = false
|
|
end)
|
|
|
|
Trigger.AfterDelay(0, function()
|
|
local buildings = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == soviets and self.HasProperty("StartBuildingRepairs") end)
|
|
Utils.Do(buildings, function(actor)
|
|
Trigger.OnDamaged(actor, function(building)
|
|
if building.Owner == soviets and building.Health < building.MaxHealth * DamageModifier then
|
|
building.StartBuildingRepairs()
|
|
end
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
Reinforcements.Reinforce(soviets, Squad1, { AlliesBaseGate1.Location, Alliesbase1.Location })
|
|
Reinforcements.Reinforce(soviets, Squad2, { AlliesBaseGate2.Location, Alliesbase2.Location })
|
|
|
|
powerproxy = Actor.Create("powerproxy.paratroopers", false, { Owner = soviets })
|
|
Trigger.AfterDelay(ParadropTicks, function()
|
|
SendSovietParadrops(ParadropWaypoints[1])
|
|
SendSovietParadrops(ParadropWaypoints[2])
|
|
end)
|
|
Trigger.AfterDelay(ParadropTicks * 2, function()
|
|
SendSovietParadrops(ParadropWaypoints[3])
|
|
SendSovietParadrops(ParadropWaypoints[4])
|
|
end)
|
|
|
|
Trigger.AfterDelay(AttackAtFrame, SendVehicleWave)
|
|
Trigger.AfterDelay(AttackAtFrameInf, SendInfantryWave)
|
|
|
|
if MoreParas then
|
|
local delay = Utils.RandomInteger(TimerTicks/3, TimerTicks*2/3)
|
|
Trigger.AfterDelay(delay, function()
|
|
SendSovietParadrops(ParadropWaypoints[Utils.RandomInteger(1,3)])
|
|
SendSovietParadrops(ParadropWaypoints[Utils.RandomInteger(3,5)])
|
|
end)
|
|
end
|
|
if SpawnNavalUnits then
|
|
Trigger.AfterDelay(AttackAtFrameNaval, SendSovietNavalReinforcements)
|
|
end
|
|
end
|
|
|
|
WorldLoaded = function()
|
|
|
|
allies = Player.GetPlayer("Allies")
|
|
soviets = Player.GetPlayer("Soviets")
|
|
|
|
InitObjectives()
|
|
InitMission()
|
|
SetupSoviets()
|
|
end
|