Files
OpenRA/OpenRA.Mods.RA/Missions/Allies03Script.cs
2013-07-21 17:35:47 +12:00

460 lines
16 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2012 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Air;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.RA.Missions
{
class Allies03ScriptInfo : TraitInfo<Allies03Script>, Requires<SpawnMapActorsInfo> { }
class Allies03Script : IHasObjectives, IWorldLoaded, ITick
{
public event Action<bool> OnObjectivesUpdated = notify => { };
public IEnumerable<Objective> Objectives { get { return new[] { evacuateUnits, destroyAirbases, evacuateMgg }; } }
Objective evacuateUnits = new Objective(ObjectiveType.Primary, EvacuateUnitsText, ObjectiveStatus.InProgress);
Objective destroyAirbases = new Objective(ObjectiveType.Secondary, DestroyAirbasesText, ObjectiveStatus.InProgress);
Objective evacuateMgg = new Objective(ObjectiveType.Secondary, EvacuateMggText, ObjectiveStatus.InProgress);
const string EvacuateUnitsText = "Following the rescue of Einstein, the Allies are now being flanked from both sides."
+ " Evacuate {0} units before the remaining Allied forces in the area are wiped out.";
const string DestroyAirbasesText = "Destroy the nearby Soviet airbases.";
const string EvacuateMggText = "Einstein has recently developed a technology which allows us to obscure units from the enemy."
+ " Evacuate at least one prototype mobile gap generator intact.";
const string ShortEvacuateTemplate = "{0}/{1} units evacuated";
int unitsEvacuatedThreshold;
int unitsEvacuated;
InfoWidget evacuateWidget;
World world;
Player allies1;
Player allies2;
Player allies;
Player soviets;
Actor exit1TopLeft;
Actor exit1BottomRight;
Actor exit1ExitPoint;
Actor exit2TopLeft;
Actor exit2BottomRight;
Actor exit2ExitPoint;
Actor sovietEntryPoint1;
Actor sovietEntryPoint2;
Actor sovietEntryPoint3;
Actor sovietEntryPoint4;
Actor sovietEntryPoint5;
Actor sovietEntryPoint6;
CPos[] sovietEntryPoints;
Actor sovietRallyPoint1;
Actor sovietRallyPoint2;
Actor sovietRallyPoint3;
Actor sovietRallyPoint4;
Actor sovietRallyPoint5;
Actor sovietRallyPoint6;
CPos[] sovietRallyPoints;
Actor[] sovietAirfields;
Rectangle paradropBox;
const int ReinforcementsTicks1 = 1500 * 5;
static readonly string[] Reinforcements1 = { "mgg", "2tnk", "2tnk", "2tnk", "2tnk", "1tnk", "1tnk", "jeep", "jeep", "e1", "e1", "e1", "e1", "e3", "e3" };
int currentReinforcement1;
const int ReinforcementsTicks2 = 1500 * 10;
static readonly string[] Reinforcements2 = { "mgg", "2tnk", "2tnk", "2tnk", "2tnk", "truk", "truk", "truk", "truk", "truk", "truk", "1tnk", "1tnk", "jeep", "jeep" };
int currentReinforcement2;
static readonly string[] SovietUnits1 = { "3tnk", "3tnk", "3tnk", "3tnk", "3tnk", "3tnk", "v2rl", "v2rl", "ftrk", "apc", "e1", "e1", "e2", "e3", "e3", "e4" };
static readonly string[] SovietUnits2 = { "4tnk", "4tnk", "4tnk", "4tnk", "3tnk", "3tnk", "3tnk", "3tnk", "v2rl", "v2rl", "ftrk", "apc", "e1", "e1", "e2", "e3", "e3", "e4" };
int sovietUnits2Ticks;
const int SovietGroupSize = 5;
int sovietParadropTicks;
const int ParadropIncrement = 200;
static readonly string[] ParadropTerrainTypes = { "Clear", "Road", "Rough", "Beach", "Ore" };
static readonly string[] SovietParadroppers = { "e1", "e1", "e3", "e3", "e4" };
int sovietParadrops;
int maxSovietYaks;
int attackAtFrame;
int attackAtFrameIncrement;
int minAttackAtFrame;
Actor allies1EntryPoint;
Actor allies1MovePoint;
Actor allies2EntryPoint;
Actor allies2MovePoint;
const string McvName = "mcv";
const string YakName = "yak";
string difficulty;
void MissionFailed(string text)
{
MissionUtils.CoopMissionFailed(world, text, allies1, allies2);
}
void MissionAccomplished(string text)
{
MissionUtils.CoopMissionAccomplished(world, text, allies1, allies2);
}
public void Tick(Actor self)
{
if (allies1.WinState != WinState.Undefined) return;
if (world.FrameNumber == 1)
{
SpawnAlliedUnit(McvName);
evacuateWidget = new InfoWidget("");
Ui.Root.AddChild(evacuateWidget);
UpdateUnitsEvacuated();
}
if (world.FrameNumber == attackAtFrame)
{
SpawnSovietUnits();
attackAtFrame += attackAtFrameIncrement;
attackAtFrameIncrement = Math.Max(attackAtFrameIncrement - 5, minAttackAtFrame);
}
if (world.FrameNumber == ReinforcementsTicks1 || world.FrameNumber == ReinforcementsTicks2)
Sound.Play("reinfor1.aud");
if (world.FrameNumber % 25 == 0)
{
if (world.FrameNumber >= ReinforcementsTicks1 && currentReinforcement1 < Reinforcements1.Length)
SpawnAlliedUnit(Reinforcements1[currentReinforcement1++]);
if (world.FrameNumber >= ReinforcementsTicks2 && currentReinforcement2 < Reinforcements2.Length)
SpawnAlliedUnit(Reinforcements2[currentReinforcement2++]);
}
if (sovietParadrops > 0)
{
if (world.FrameNumber == sovietParadropTicks)
Sound.Play("sovfapp1.aud");
if (world.FrameNumber >= sovietParadropTicks && world.FrameNumber % ParadropIncrement == 0)
{
CPos lz;
CPos entry;
do
{
var x = world.SharedRandom.Next(paradropBox.X, paradropBox.X + paradropBox.Width);
var y = world.SharedRandom.Next(paradropBox.Y, paradropBox.Y + paradropBox.Height);
entry = new CPos(0, y);
lz = new CPos(x, y);
}
while (!ParadropTerrainTypes.Contains(world.GetTerrainType(lz)));
MissionUtils.Paradrop(world, soviets, SovietParadroppers, entry, lz);
sovietParadrops--;
}
}
if (world.FrameNumber % 25 == 0)
ManageSovietUnits();
if (destroyAirbases.Status != ObjectiveStatus.Completed)
{
if (world.FrameNumber % 25 == 0)
BuildSovietAircraft();
ManageSovietAircraft();
}
EvacuateAlliedUnits(exit1TopLeft.Location, exit1BottomRight.Location, exit1ExitPoint.Location);
EvacuateAlliedUnits(exit2TopLeft.Location, exit2BottomRight.Location, exit2ExitPoint.Location);
CheckSovietAirbases();
if (!world.Actors.Any(a => (a.Owner == allies1 || a.Owner == allies2) && a.IsInWorld && !a.IsDead()
&& ((a.HasTrait<Building>() && !a.HasTrait<Wall>()) || a.HasTrait<BaseBuilding>())))
{
evacuateUnits.Status = ObjectiveStatus.Failed;
OnObjectivesUpdated(true);
MissionFailed("The remaining Allied forces in the area have been wiped out.");
}
}
Actor FirstUnshroudedOrDefault(IEnumerable<Actor> actors, World world, int shroudRange)
{
return actors.FirstOrDefault(u => world.FindAliveCombatantActorsInCircle(u.CenterPosition, WRange.FromCells(shroudRange)).All(a => !a.HasTrait<CreatesShroud>()));
}
void ManageSovietAircraft()
{
var enemies = world.Actors
.Where(u => (u.Owner == allies1 || u.Owner == allies2)
&& ((u.HasTrait<Building>() && !u.HasTrait<Wall>()) || u.HasTrait<Mobile>()) && u.IsInWorld && !u.IsDead()
&& (!u.HasTrait<Spy>() || !u.Trait<Spy>().Disguised || (u.Trait<Spy>().Disguised && u.Trait<Spy>().disguisedAsPlayer != soviets)));
foreach (var aircraft in SovietAircraft())
{
var pos = aircraft.CenterPosition;
var plane = aircraft.Trait<Plane>();
var ammo = aircraft.Trait<LimitedAmmo>();
if ((pos.Z == 0 && ammo.FullAmmo()) || (pos.Z != 0 && ammo.HasAmmo()))
{
var enemy = FirstUnshroudedOrDefault(enemies.OrderBy(u => (aircraft.CenterPosition - u.CenterPosition).LengthSquared), world, 10);
if (enemy != null)
{
if (!aircraft.IsIdle && aircraft.GetCurrentActivity().GetType() != typeof(FlyAttack))
aircraft.CancelActivity();
if (pos.Z == 0)
plane.UnReserve();
aircraft.QueueActivity(new FlyAttack(Target.FromActor(enemy)));
}
}
else if (pos.Z != 0 && !LandIsQueued(aircraft))
{
aircraft.CancelActivity();
aircraft.QueueActivity(new ReturnToBase(aircraft, null));
aircraft.QueueActivity(new ResupplyAircraft());
}
}
}
bool LandIsQueued(Actor actor)
{
for (var a = actor.GetCurrentActivity(); a != null; a = a.NextActivity)
if (a is ReturnToBase || a is Land) return true;
return false;
}
void BuildSovietAircraft()
{
var queue = MissionUtils.FindQueues(world, soviets, "Plane").FirstOrDefault(q => q.CurrentItem() == null);
if (queue == null || SovietAircraft().Count() >= maxSovietYaks) return;
queue.ResolveOrder(queue.self, Order.StartProduction(queue.self, YakName, 1));
}
IEnumerable<Actor> SovietAircraft()
{
return world.Actors.Where(a => a.HasTrait<AttackPlane>() && a.Owner == soviets && a.IsInWorld && !a.IsDead());
}
void CheckSovietAirbases()
{
if (destroyAirbases.Status != ObjectiveStatus.Completed && sovietAirfields.All(a => a.IsDead() || a.Owner != soviets))
{
destroyAirbases.Status = ObjectiveStatus.Completed;
OnObjectivesUpdated(true);
}
}
void SpawnSovietUnits()
{
var route = world.SharedRandom.Next(sovietEntryPoints.Length);
var spawnPoint = sovietEntryPoints[route];
var rallyPoint = sovietRallyPoints[route];
IEnumerable<string> units;
if (world.FrameNumber >= sovietUnits2Ticks)
units = SovietUnits2;
else
units = SovietUnits1;
var unit = world.CreateActor(units.Random(world.SharedRandom),
new TypeDictionary
{
new LocationInit(spawnPoint),
new OwnerInit(soviets)
});
unit.QueueActivity(new AttackMove.AttackMoveActivity(unit, new Move.Move(rallyPoint, 3)));
}
void AttackNearestAlliedActor(Actor self)
{
var enemies = world.Actors.Where(u => u.AppearsHostileTo(self) && (u.Owner == allies1 || u.Owner == allies2)
&& ((u.HasTrait<Building>() && !u.HasTrait<Wall>()) || u.HasTrait<Mobile>()) && u.IsInWorld && !u.IsDead());
var enemy = FirstUnshroudedOrDefault(enemies.OrderBy(u => (self.CenterPosition - u.CenterPosition).LengthSquared), world, 10);
if (enemy != null)
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(enemy), WRange.FromCells(3))));
}
void ManageSovietUnits()
{
foreach (var rallyPoint in sovietRallyPoints)
{
var units = world.FindAliveCombatantActorsInCircle(rallyPoint.CenterPosition, WRange.FromCells(10))
.Where(u => u.IsIdle && u.HasTrait<Mobile>() && u.HasTrait<AttackBase>() && u.Owner == soviets);
if (units.Count() >= SovietGroupSize)
{
foreach (var unit in units)
AttackNearestAlliedActor(unit);
}
}
var scatteredUnits = world.Actors.Where(u => u.IsInWorld && !u.IsDead() && u.HasTrait<Mobile>() && u.IsIdle && u.Owner == soviets)
.Except(world.WorldActor.Trait<SpawnMapActors>().Actors.Values)
.Except(sovietRallyPoints.SelectMany(rp => world.FindAliveCombatantActorsInCircle(rp.CenterPosition, WRange.FromCells(10))));
foreach (var unit in scatteredUnits)
AttackNearestAlliedActor(unit);
}
void SpawnAlliedUnit(string actor)
{
SpawnAndMove(actor, allies1, allies1EntryPoint.Location, allies1MovePoint.Location);
if (allies1 != allies2)
SpawnAndMove(actor, allies2, allies2EntryPoint.Location, allies2MovePoint.Location);
}
Actor SpawnAndMove(string actor, Player owner, CPos entry, CPos to)
{
var unit = world.CreateActor(actor, new TypeDictionary
{
new OwnerInit(owner),
new LocationInit(entry),
new FacingInit(Util.GetFacing(to - entry, 0))
});
unit.QueueActivity(new Move.Move(to));
return unit;
}
void UpdateUnitsEvacuated()
{
evacuateWidget.Text = ShortEvacuateTemplate.F(unitsEvacuated, unitsEvacuatedThreshold);
if (evacuateUnits.Status == ObjectiveStatus.InProgress && unitsEvacuated >= unitsEvacuatedThreshold)
{
evacuateUnits.Status = ObjectiveStatus.Completed;
OnObjectivesUpdated(true);
MissionAccomplished("The remaining Allied forces in the area have evacuated.");
}
}
void EvacuateAlliedUnits(CPos tl, CPos br, CPos exit)
{
var units = world.FindAliveCombatantActorsInBox(tl, br)
.Where(u => u.HasTrait<Mobile>() && !u.HasTrait<Aircraft>() && (u.Owner == allies1 || u.Owner == allies2));
foreach (var unit in units)
{
unit.CancelActivity();
unit.ChangeOwner(allies);
unitsEvacuated++;
var createsShroud = unit.TraitOrDefault<CreatesShroud>();
if (createsShroud != null && evacuateMgg.Status == ObjectiveStatus.InProgress)
{
evacuateMgg.Status = ObjectiveStatus.Completed;
OnObjectivesUpdated(true);
}
var cargo = unit.TraitOrDefault<Cargo>();
if (cargo != null)
unitsEvacuated += cargo.Passengers.Count();
UpdateUnitsEvacuated();
unit.QueueActivity(new Move.Move(exit));
unit.QueueActivity(new RemoveSelf());
}
}
public void WorldLoaded(World w)
{
world = w;
difficulty = w.LobbyInfo.GlobalSettings.Difficulty;
Game.Debug("{0} difficulty selected".F(difficulty));
allies1 = w.Players.Single(p => p.InternalName == "Allies1");
allies2 = w.Players.SingleOrDefault(p => p.InternalName == "Allies2");
var res = allies1.PlayerActor.Trait<PlayerResources>();
if (allies2 == null)
{
res.Cash = 10000;
allies2 = allies1;
}
else
{
res.Cash = 5000;
res = allies2.PlayerActor.Trait<PlayerResources>();
res.Cash = 5000;
}
attackAtFrame = attackAtFrameIncrement = difficulty == "Hard" || difficulty == "Normal" ? 500 : 600;
minAttackAtFrame = difficulty == "Hard" || difficulty == "Normal" ? 100 : 150;
unitsEvacuatedThreshold = difficulty == "Hard" ? 200 : difficulty == "Normal" ? 100 : 50;
maxSovietYaks = difficulty == "Hard" ? 4 : difficulty == "Normal" ? 2 : 0;
sovietParadrops = difficulty == "Hard" ? 40 : difficulty == "Normal" ? 20 : 0;
sovietParadropTicks = difficulty == "Hard" ? 1500 * 17 : 1500 * 20;
sovietUnits2Ticks = difficulty == "Hard" ? 1500 * 12 : 1500 * 15;
evacuateUnits.Text = evacuateUnits.Text.F(unitsEvacuatedThreshold);
allies = w.Players.Single(p => p.InternalName == "Allies");
soviets = w.Players.Single(p => p.InternalName == "Soviets");
var actors = w.WorldActor.Trait<SpawnMapActors>().Actors;
exit1TopLeft = actors["Exit1TopLeft"];
exit1BottomRight = actors["Exit1BottomRight"];
exit1ExitPoint = actors["Exit1ExitPoint"];
exit2TopLeft = actors["Exit2TopLeft"];
exit2BottomRight = actors["Exit2BottomRight"];
exit2ExitPoint = actors["Exit2ExitPoint"];
allies1EntryPoint = actors["Allies1EntryPoint"];
allies1MovePoint = actors["Allies1MovePoint"];
allies2EntryPoint = actors["Allies2EntryPoint"];
allies2MovePoint = actors["Allies2MovePoint"];
sovietEntryPoint1 = actors["SovietEntryPoint1"];
sovietEntryPoint2 = actors["SovietEntryPoint2"];
sovietEntryPoint3 = actors["SovietEntryPoint3"];
sovietEntryPoint4 = actors["SovietEntryPoint4"];
sovietEntryPoint5 = actors["SovietEntryPoint5"];
sovietEntryPoint6 = actors["SovietEntryPoint6"];
sovietEntryPoints = new[] { sovietEntryPoint1, sovietEntryPoint2, sovietEntryPoint3, sovietEntryPoint4, sovietEntryPoint5, sovietEntryPoint6 }.Select(p => p.Location).ToArray();
sovietRallyPoint1 = actors["SovietRallyPoint1"];
sovietRallyPoint2 = actors["SovietRallyPoint2"];
sovietRallyPoint3 = actors["SovietRallyPoint3"];
sovietRallyPoint4 = actors["SovietRallyPoint4"];
sovietRallyPoint5 = actors["SovietRallyPoint5"];
sovietRallyPoint6 = actors["SovietRallyPoint6"];
sovietRallyPoints = new[] { sovietRallyPoint1, sovietRallyPoint2, sovietRallyPoint3, sovietRallyPoint4, sovietRallyPoint5, sovietRallyPoint6 }.Select(p => p.Location).ToArray();
sovietAirfields = actors.Values.Where(a => a.Owner == soviets && a.HasTrait<Production>() && a.Info.Traits.Get<ProductionInfo>().Produces.Contains("Plane")).ToArray();
var topLeft = actors["ParadropBoxTopLeft"];
var bottomRight = actors["ParadropBoxBottomRight"];
paradropBox = new Rectangle(topLeft.Location.X, topLeft.Location.Y, bottomRight.Location.X - topLeft.Location.X, bottomRight.Location.Y - topLeft.Location.Y);
if (w.LocalPlayer == null || w.LocalPlayer == allies1)
Game.MoveViewport(allies1EntryPoint.Location.ToFloat2());
else
Game.MoveViewport(allies2EntryPoint.Location.ToFloat2());
OnObjectivesUpdated(false);
MissionUtils.PlayMissionMusic();
}
}
}