--[[ 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. ]] ReinforceAllies = function() Media.PlaySpeechNotification(Greece, "ReinforcementsArrived") local proxy = SpawnMiscActor("paradrop.allies", GoodGuy, TanyaDrop.Location, DateTime.Seconds(1)) local planes = proxy.TargetParatroopers(TanyaDrop.CenterPosition, Angle.SouthEast) Utils.Do(planes, function(plane) Trigger.OnPassengerExited(plane, function(_, passenger) passenger.Owner = Greece end) Trigger.OnAllKilled(plane.Passengers, CheckAlliedDestruction) end) local demoTypes = { "2tnk", "2tnk", "2tnk", "dtrk" } local artilleryTypes = {"2tnk", "2tnk", "arty", "arty", "mech", "mech", "mech" } local groundPath = { WestRoadEntry.Location, WestRoadRally.Location } local demoTeam = Reinforcements.Reinforce(Greece, demoTypes, groundPath) Trigger.OnAllKilled(demoTeam, CheckAlliedDestruction) Trigger.AfterDelay(DateTime.Seconds(30), function() if AlliesDefeated then return end Media.PlaySpeechNotification(Greece, "ReinforcementsArrived") local artilleryTeam = Reinforcements.Reinforce(Greece, artilleryTypes, groundPath) Trigger.OnAllKilled(artilleryTeam, CheckAlliedDestruction) end) end ReinforceChronoTanks = function(locations) if ChronoTanksReinforced then return end ChronoTanksReinforced = true Media.PlaySoundNotification(Greece, "Chronoshift") Utils.Do(locations, function(location) local proxy = SpawnMiscActor("powerproxy.chronoshift", GoodGuy, location, DateTime.Seconds(1)) local tank = Actor.Create("ctnk", true, { Owner = Greece, Facing = Angle.NorthWest }) local payload = { [tank] = location } proxy.Chronoshift(payload) end) end ReinforceTanya = function() local path = { WestRoadEntry.Location, TanyaDrop.Location } local plane = Reinforcements.Reinforce(Greece, { "badr.tanya" }, path)[1] local passengers = { TanyaType } Trigger.OnAddedToWorld(plane, function() Utils.Do(passengers, function(type) local passenger = Actor.Create(type, false, { Owner = Greece }) plane.LoadPassenger(passenger) Trigger.OnKilled(passenger, OnTanyaKilled) Tanya = passenger AnnounceTanyaRules(passenger) end) plane.Paradrop(path[2]) end) end AnnounceTanyaRules = function(tanya) if Difficulty == "easy" then return end Trigger.OnAddedToWorld(tanya, function() Trigger.AfterDelay(DateTime.Seconds(1), function() Media.DisplayMessage(UserInterface.GetFluentMessage("tanya-rules-of-engagement"), tanya.TooltipName) Media.PlaySoundNotification(Greece, "AlertBleep") end) end) end ReinforceLongbows = function() if SoutheastTurret.IsDead then return end local goals = { LongbowGoal1, LongbowGoal2, LongbowGoal3 } local spawnOffset = 0 Utils.Do(goals, function(goal) local entry = { LongbowEntry.Location + CVec.New(spawnOffset, 15) } Reinforcements.Reinforce(GoodGuy, { "heli" }, entry, 0, function(helicopter) helicopter.Wait(DateTime.Seconds(1)) helicopter.AttackMove(goal.Location) -- Original behavior was to fire at the ground until out of ammo. Trigger.OnKilled(SoutheastTurret, function() helicopter.Stop() helicopter.Wait(10) helicopter.Move(LongbowExit.Location, 1) helicopter.Destroy() end) end) spawnOffset = spawnOffset + 5 end) end ReinforceBadGuy = function() -- If ForwardCommand dies early, the General will be busy hunting. if General.IsDead or ForwardCommand.IsDead then return end BadGuyReinforced = true Media.PlaySpeechNotification(Greece, "SignalFlareWest") local flare = SpawnFlare(NorthFlare.Location) Trigger.OnKilled(General, function() RemoveActor(flare, DateTime.Seconds(30)) end) local waterCamera = SpawnPlayerCamera(NorthWaterEntry.Location, -1) local boatPath = { NorthWaterEntry.Location, BuilderBeach.Location } local cargo = Reinforcements.ReinforceWithTransport(BadGuy, "lst", { "mcv" }, boatPath, { boatPath[1] })[2] Utils.Do(cargo, function(builder) Trigger.OnKilled(builder, CheckNewBase) Trigger.OnAddedToWorld(builder, function() local builderCamera = SpawnPlayerCamera(builder.Location, -1, "camera.small") builder.Move(BuilderRally.Location) builder.CallFunc(function() RemoveActor(waterCamera) RemoveActor(builderCamera) end) builder.Deploy() end) end) end ReinforceNorthChinook = function() local delay = DateTime.Seconds(4) local camera = SpawnPlayerCamera(NorthChinookUnload.Location, -1, "camera.small", delay + DateTime.Seconds(2)) Trigger.AfterDelay(delay, function() local transport = Actor.Create("tran.north", true, { Owner = USSR, Location = NorthChinookEntry.Location, Facing = Angle.South }) local passengers = transport.Passengers transport.UnloadPassengers(NorthChinookUnload.Location) transport.Move(SouthChinookExit.Location) transport.Destroy() Trigger.OnPassengerExited(transport, function() if transport.PassengerCount > 0 then return end -- Original unload is slow. Mimic the timing with a short pause. Trigger.AfterDelay(DateTime.Seconds(3), function() OrderNorthChinookSoldiers(passengers, camera) end) end) Trigger.OnAnyKilled(passengers, function() RemoveActor(camera, DateTime.Seconds(1)) end) end) end OrderNorthChinookSoldiers = function(soldiers, camera) local patrolPath = { VillageNortheast.Location, VillageNorthwest.Location, VillageSouthwest.Location, VillageSoutheast.Location } local liveSoldiers = Utils.Where(soldiers, IsAlive) Utils.Do(liveSoldiers, function(soldier) soldier.AttackMove(NorthChinookRally.Location) soldier.CallFunc(function() Trigger.AfterDelay(1, function() if not AreAllIdleOrDead(liveSoldiers) then return end RemoveActor(camera, DateTime.Seconds(1)) GroupAttackMove(liveSoldiers, VillageBridgeCenter.Location, 2) GroupTightPatrol(liveSoldiers, patrolPath, true) GroupHuntOnDamaged(liveSoldiers, Greece) end) end) end) end ReinforceVillagePatrol = function() if VillagePatrolSent then return end VillagePatrolSent = true local path = { VillageSoutheast.Location, VillageSouthwest.Location, VillageNorthwest.Location, VillageNortheast.Location } local types = { "e1", "e1", "e2", "dog" } local origin = { VillagePatrolEntry.Location } local group = Reinforcements.Reinforce(USSR, types, origin, 0) GroupTightPatrol(group, path, true) end ReinforceGuardHouse = function(intruderType) if GuardHouse.IsDead then return end local goal = GuideBarrelGoal.Location if intruderType == TanyaType then goal = PrisonReveal.Location end for i = 1, 4 do Trigger.AfterDelay(i * 5, function() local guard = Actor.Create("e1", true, { Owner = USSR, Location = GuardHouseSpawn.Location, SubCell = 4, Facing = Angle.SouthWest }) guard.Move(GuardHouseExit.Location) guard.AttackMove(goal) IdleHunt(guard) end) end end ReinforceHardTeams = function() if Difficulty ~= "hard" then return end ReinforceBaseDefenders() ReinforceNorthBeachGuards() ReinforceV2Team() ReinforceNorthEdgeGuards() PrepareSouthChinook() ReinforceHardDogs() end ReinforceBaseDefenders = function() local origin = BadGuyRally.Location + CVec.New(2, 2) local types = { "3tnk", "3tnk" } local defenders = Reinforcements.Reinforce(USSR, types, { origin }, 0, function(actor) actor.Wait(1) actor.Scatter() end) GroupHuntOnDamaged(defenders, Greece) local structures = { RoadTurretWest, RoadTurretEast, ForwardPower, ForwardCommand, ForwardTech } Trigger.OnAnyKilled(structures, function() GroupIdleHunt(defenders) end) end ReinforceNorthBeachGuards = function() local types = { "e1", "e1", "e2", "e1" } local patrolPath = { VillageNortheast.Location, VillageSouthwest.Location } local soldiers = Reinforcements.Reinforce(USSR, types, { NorthFlare.Location }, 0, function(actor) actor.Scatter() end) Trigger.OnEnteredFootprint({ GeneralRally.Location }, function(actor, id) if actor.Type ~= "gnrl" then return end Trigger.RemoveFootprintTrigger(id) GroupHuntOnDamaged(soldiers, Greece) Utils.Do(soldiers, function(soldier) if soldier.IsDead then return end soldier.AttackMove(BadGuyRally.Location) soldier.AttackMove(GuideBarrelGoal.Location) soldier.AttackMove(VillageNorthwest.Location) soldier.Patrol(patrolPath, true) end) end) end ReinforceV2Team = function() local rocket = Actor.Create("v2rl", true, { Owner = USSR, Facing = Angle.West, Location = SamRocketEntry.Location }) Reinforcements.Reinforce(USSR, { "e1" }, { SamFenceReveal.Location }, 0, function(actor) actor.Guard(rocket) end) end PrepareSouthChinook = function() local startPosition = SouthChinookEntry.CenterPosition + WVec.New(0, 0, Actor.CruiseAltitude("tran")) Trigger.OnEnteredProximityTrigger(SouthChinookProximity.CenterPosition, WDist.FromCells(8), function(actor, id) if actor.Type ~= TanyaType or not EscortFinished then return end Trigger.RemoveProximityTrigger(id) local transport = Actor.Create("tran.south", true, { Owner = USSR, CenterPosition = startPosition, Facing = Angle.East }) transport.UnloadPassengers(SouthChinookEntry.Location) transport.Move(SouthChinookExit.Location) transport.Destroy() Utils.Do(transport.Passengers, function(passenger) Trigger.OnAddedToWorld(passenger, function() passenger.Wait(DateTime.Seconds(1)) IdleHunt(passenger) end) end) end) end ReinforceNorthEdgeGuards = function() local types = { "e1", "e1", "e2" } local guards = Reinforcements.Reinforce(USSR, types, { NorthEdgeGuardEntry.Location }, 0, function(actor) actor.Scatter() end) GroupHuntOnDamaged(guards, Greece) end ReinforceStartSoldiers = function() local teams = { { entry = { WestRoadEntry.Location }, types = { "e2", "e2", "e1" } }, { entry = { RoadTurretRifle2.Location }, types = { "e1", "e2", "e4" } } } Utils.Do(teams, function(team) local soldiers = Reinforcements.Reinforce(USSR, team.types, team.entry, 15, function(actor) actor.AttackMove(TanyaDrop.Location) actor.Hunt() end) Trigger.OnAnyKilled(soldiers, function() IdleHunt(StartSoldier) end) end) end ReinforceHardDogs = function() local startDog = Actor.Create("dog", true, { Owner = USSR, Location = WestRoadEntry.Location, Facing = Angle.South }) startDog.Move(WestRoadRally.Location + CVec.New(-1, 0)) PrepareStartDogAttack(startDog) local turretDog = Actor.Create("dog.areaguard", true, { Owner = USSR, Location = LongbowGoal2.Location, Facing = Angle.SouthWest }) turretDog.AddTag("TurretDog") Trigger.OnKilled(turretDog, function() GroupIdleHunt(SoutheastTurretGuards) end) end PrepareStartDogAttack = function(dog) -- Tanya will take about 100 ticks/4 seconds to touch the ground. local delay = DateTime.Seconds(5) + 5 Trigger.OnEnteredFootprint({ TanyaDrop.Location }, function(actor, id) if actor.Type ~= TanyaType then return end Trigger.RemoveFootprintTrigger(id) Trigger.AfterDelay(delay, function() if dog.IsDead or actor.IsDead then return end dog.Attack(actor) end) end) end