diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index e2987960d46e..84ed13657c84 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -515,7 +515,7 @@ - + @@ -586,4 +586,4 @@ copy "FuzzyLogicLibrary.dll" "$(SolutionDir)" cd "$(SolutionDir)" - \ No newline at end of file + diff --git a/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs b/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs index 1a0dbd545252..242b29ef14a2 100644 --- a/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs +++ b/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs @@ -16,7 +16,7 @@ namespace OpenRA.Mods.RA { - [Desc("Attach this to the world actor (not a building!) to define a new shared build queue.", + [Desc("Attach this to the player actor (not a building!) to define a new shared build queue.", "Will only work together with the Production: trait on the actor that actually does the production.", "You will also want to add PrimaryBuildings: to let the user choose where new units should exit.")] public class ClassicProductionQueueInfo : ProductionQueueInfo, Requires, Requires, Requires diff --git a/OpenRA.Mods.RA/Scripting/Global/DateGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/DateTimeGlobal.cs similarity index 66% rename from OpenRA.Mods.RA/Scripting/Global/DateGlobal.cs rename to OpenRA.Mods.RA/Scripting/Global/DateTimeGlobal.cs index e93919bcf690..c87e7239d2d4 100644 --- a/OpenRA.Mods.RA/Scripting/Global/DateGlobal.cs +++ b/OpenRA.Mods.RA/Scripting/Global/DateTimeGlobal.cs @@ -17,7 +17,8 @@ namespace OpenRA.Mods.RA.Scripting [ScriptGlobal("Date")] public class DateGlobal : ScriptGlobal { - public DateGlobal(ScriptContext context) : base(context) { } + public DateGlobal(ScriptContext context) + : base(context) { } [Desc("True on the 31st of October.")] public bool IsHalloween @@ -25,4 +26,17 @@ public bool IsHalloween get { return DateTime.Today.Month == 10 && DateTime.Today.Day == 31; } } } + + [ScriptGlobal("Time")] + public class TimeGlobal : ScriptGlobal + { + public TimeGlobal(ScriptContext context) + : base(context) { } + + [Desc("Get the current game time (in ticks)")] + public int GameTime + { + get { return context.World.WorldTick; } + } + } } diff --git a/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs index 08a2b5be906b..4e8cdf98e2fe 100644 --- a/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs +++ b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs @@ -22,7 +22,7 @@ public class TriggerGlobal : ScriptGlobal { public TriggerGlobal(ScriptContext context) : base(context) { } - static ScriptTriggers GetScriptTriggers(Actor a) + public static ScriptTriggers GetScriptTriggers(Actor a) { var events = a.TraitOrDefault(); if (events == null) diff --git a/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs index 382599b41db1..6f864728e853 100644 --- a/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs +++ b/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs @@ -8,6 +8,9 @@ */ #endregion +using Eluant; +using System; +using System.Linq; using OpenRA.Mods.RA.Activities; using OpenRA.Scripting; using OpenRA.Traits; @@ -17,8 +20,13 @@ namespace OpenRA.Mods.RA.Scripting [ScriptPropertyGroup("Combat")] public class CombatProperties : ScriptActorProperties, Requires, Requires { + readonly IMove move; + public CombatProperties(ScriptContext context, Actor self) - : base(context, self) { } + : base(context, self) + { + move = self.Trait(); + } [ScriptActorPropertyActivity] [Desc("Seek out and attack nearby targets.")] @@ -33,11 +41,35 @@ public void Hunt() "close enough to complete the activity.")] public void AttackMove(CPos cell, int closeEnough = 0) { - var move = self.TraitOrDefault(); - if (move == null) - return; - self.QueueActivity(new AttackMove.AttackMoveActivity(self, move.MoveTo(cell, closeEnough))); } + + [ScriptActorPropertyActivity] + [Desc("Patrol along a set of given waypoints. The action is repeated by default, " + + "and the actor will wait for `wait` ticks at each waypoint.")] + public void Patrol(CPos[] waypoints, bool loop = true, int wait = 0) + { + foreach (var wpt in waypoints) + { + self.QueueActivity(new AttackMove.AttackMoveActivity(self, move.MoveTo(wpt, 2))); + self.QueueActivity(new Wait(wait)); + } + + if (loop) + self.QueueActivity(new CallFunc(() => Patrol(waypoints, loop, wait))); + } + + [ScriptActorPropertyActivity] + [Desc("Patrol along a set of given waypoints until a condition becomes true. " + + "The actor will wait for `wait` ticks at each waypoint.")] + public void PatrolUntil(CPos[] waypoints, LuaFunction func, int wait = 0) + { + Patrol(waypoints, false, wait); + + var repeat = func.Call(self.ToLuaValue(context)).First().ToBoolean(); + if (repeat) + using (var f = func.CopyReference() as LuaFunction) + self.QueueActivity(new CallFunc(() => PatrolUntil(waypoints, f, wait))); + } } } diff --git a/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs index c11867ff9e69..0143f3398342 100644 --- a/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs +++ b/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs @@ -8,6 +8,7 @@ */ #endregion +using OpenRA.Mods.RA.Buildings; using OpenRA.Scripting; using OpenRA.Traits; @@ -33,4 +34,34 @@ public int Health [Desc("Maximum health of the actor.")] public int MaxHealth { get { return health.MaxHP; } } } + + [ScriptPropertyGroup("General")] + public class RepairableBuildingProperties : ScriptActorProperties, Requires + { + readonly RepairableBuilding rb; + + public RepairableBuildingProperties(ScriptContext context, Actor self) + : base(context, self) + { + rb = self.Trait(); + } + + [Desc("Start repairs on this building. `repairer` can be an allied player.")] + public void StartBuildingRepairs(Player repairer = null) + { + repairer = repairer ?? self.Owner; + + if (!rb.Repairers.Contains(repairer)) + rb.RepairBuilding(self, repairer); + } + + [Desc("Stop repairs on this building. `repairer` can be an allied player.")] + public void StopBuildingRepairs(Player repairer = null) + { + repairer = repairer ?? self.Owner; + + if (rb.RepairActive && rb.Repairers.Contains(repairer)) + rb.RepairBuilding(self, repairer); + } + } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/PlaneProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/PlaneProperties.cs index 0624c42e4731..d56e8211ebaf 100644 --- a/OpenRA.Mods.RA/Scripting/Properties/PlaneProperties.cs +++ b/OpenRA.Mods.RA/Scripting/Properties/PlaneProperties.cs @@ -27,4 +27,17 @@ public void Move(CPos cell) self.QueueActivity(new Fly(self, Target.FromCell(self.World, cell))); } } + + [ScriptPropertyGroup("Combat")] + public class PlaneCombatProperties : ScriptActorProperties, Requires + { + public PlaneCombatProperties(ScriptContext context, Actor self) + : base(context, self) { } + + [Desc("Fly an attack against the target actor.")] + public void Attack(Actor target) + { + self.QueueActivity(new FlyAttack(Target.FromActor(target))); + } + } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs index 5e923fbe735a..ed47d59cfd9f 100644 --- a/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs +++ b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs @@ -9,6 +9,10 @@ #endregion using Eluant; +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Mods.Common; using OpenRA.Mods.RA.Activities; using OpenRA.Scripting; using OpenRA.Traits; @@ -37,4 +41,234 @@ public void Produce(string actorType, string raceVariant = null) self.QueueActivity(new WaitFor(() => p.Produce(self, actorInfo, raceVariant))); } } + + [ScriptPropertyGroup("Production")] + public class RallyPointProperties : ScriptActorProperties, Requires + { + readonly RallyPoint rp; + + public RallyPointProperties(ScriptContext context, Actor self) + : base(context, self) + { + rp = self.Trait(); + } + + [Desc("Query or set a factory's rally point")] + public CPos RallyPoint + { + get { return rp.Location; } + set { rp.Location = value; } + } + } + + [ScriptPropertyGroup("Production")] + public class PrimaryBuildingProperties : ScriptActorProperties, Requires + { + readonly PrimaryBuilding pb; + + public PrimaryBuildingProperties(ScriptContext context, Actor self) + : base(context, self) + { + pb = self.Trait(); + } + + [Desc("Query or set the factory's primary building status")] + public bool IsPrimaryBuilding + { + get { return pb.IsPrimary; } + set { pb.SetPrimaryProducer(self, value); } + } + } + + [ScriptPropertyGroup("Production")] + public class ProductionQueueProperties : ScriptActorProperties, Requires, Requires + { + readonly List queues; + readonly ScriptTriggers triggers; + + public ProductionQueueProperties(ScriptContext context, Actor self) + : base(context, self) + { + queues = self.TraitsImplementing().Where(q => q.Enabled).ToList(); + triggers = TriggerGlobal.GetScriptTriggers(self); + } + + [Desc("Build the specified set of actors using a TD-style (per building) production queue. " + + "The function will return true if production could be started, false otherwise. " + + "If an actionFunc is given, it will be called as actionFunc(Actor[] actors) once " + + "production of all actors has been completed. The actors array is guaranteed to " + + "only contain alive actors.")] + public bool Build(string[] actorTypes, LuaFunction actionFunc = null) + { + if (triggers.Triggers[Trigger.OnProduction].Any()) + return false; + + var queue = queues.Where(q => actorTypes.All(t => GetBuildableInfo(t).Queue.Contains(q.Info.Type))) + .FirstOrDefault(q => q.CurrentItem() == null); + + if (queue == null) + return false; + + if (actionFunc != null) + { + var playerIndex = self.Owner.ClientIndex; + var squadSize = actorTypes.Length; + var squad = new List(); + var func = actionFunc.CopyReference() as LuaFunction; + + Action productionHandler = (_, __) => { }; + productionHandler = (factory, unit) => + { + if (playerIndex != factory.Owner.ClientIndex) + { + triggers.OnProducedInternal -= productionHandler; + return; + } + + squad.Add(unit); + if (squad.Count >= squadSize) + { + using (func) + using (var luaSquad = squad.Where(u => !u.IsDead()).ToArray().ToLuaValue(context)) + func.Call(luaSquad).Dispose(); + + triggers.OnProducedInternal -= productionHandler; + } + }; + + triggers.OnProducedInternal += productionHandler; + } + + foreach (var actorType in actorTypes) + queue.ResolveOrder(self, Order.StartProduction(self, actorType, 1)); + + return true; + } + + [Desc("Checks whether the factory is currently producing anything on the queue that produces this type of actor.")] + public bool IsProducing(string actorType) + { + if (triggers.Triggers[Trigger.OnProduction].Any()) + return true; + + return queues.Where(q => GetBuildableInfo(actorType).Queue.Contains(q.Info.Type)) + .Any(q => q.CurrentItem() != null); + } + + BuildableInfo GetBuildableInfo(string actorType) + { + var ri = self.World.Map.Rules.Actors[actorType]; + var bi = ri.Traits.GetOrDefault(); + + if (bi == null) + throw new LuaException("Actor of type {0} cannot be produced".F(actorType)); + else + return bi; + } + } + + [ScriptPropertyGroup("Production")] + public class ClassicProductionQueueProperties : ScriptPlayerProperties, Requires, Requires + { + readonly Dictionary> productionHandlers; + readonly Dictionary queues; + + public ClassicProductionQueueProperties(ScriptContext context, Player player) + : base(context, player) + { + productionHandlers = new Dictionary>(); + + queues = new Dictionary(); + foreach (var q in player.PlayerActor.TraitsImplementing().Where(q => q.Enabled)) + queues.Add(q.Info.Type, q); + + Action globalProductionHandler = (factory, unit) => + { + if (factory.Owner != player) + return; + + var queue = GetBuildableInfo(unit.Info.Name).Queue.First(); + + if (productionHandlers.ContainsKey(queue)) + productionHandlers[queue](factory, unit); + }; + + var triggers = TriggerGlobal.GetScriptTriggers(player.PlayerActor); + triggers.OnOtherProducedInternal += globalProductionHandler; + } + + [Desc("Build the specified set of actors using classic (RA-style) production queues. " + + "The function will return true if production could be started, false otherwise. " + + "If an actionFunc is given, it will be called as actionFunc(Actor[] actors) once " + + "production of all actors has been completed. The actors array is guaranteed to " + + "only contain alive actors.")] + public bool Build(string[] actorTypes, LuaFunction actionFunc = null) + { + var typeToQueueMap = new Dictionary(); + foreach (var actorType in actorTypes.Distinct()) + typeToQueueMap.Add(actorType, GetBuildableInfo(actorType).Queue.First()); + + var queueTypes = typeToQueueMap.Values.Distinct(); + + if (queueTypes.Any(t => !queues.ContainsKey(t) || productionHandlers.ContainsKey(t))) + return false; + + if (queueTypes.Any(t => queues[t].CurrentItem() != null)) + return false; + + if (actionFunc != null) + { + var squadSize = actorTypes.Length; + var squad = new List(); + var func = actionFunc.CopyReference() as LuaFunction; + + Action productionHandler = (factory, unit) => + { + squad.Add(unit); + if (squad.Count >= squadSize) + { + using (func) + using (var luaSquad = squad.Where(u => !u.IsDead()).ToArray().ToLuaValue(context)) + func.Call(luaSquad).Dispose(); + + foreach (var q in queueTypes) + productionHandlers.Remove(q); + } + }; + + foreach (var q in queueTypes) + productionHandlers.Add(q, productionHandler); + } + + foreach (var actorType in actorTypes) + { + var queue = queues[typeToQueueMap[actorType]]; + queue.ResolveOrder(queue.Actor, Order.StartProduction(queue.Actor, actorType, 1)); + } + + return true; + } + + [Desc("Checks whether the player is currently producing anything on the queue that produces this type of actor.")] + public bool IsProducing(string actorType) + { + var queue = GetBuildableInfo(actorType).Queue.First(); + + if (!queues.ContainsKey(queue)) + return true; + + return productionHandlers.ContainsKey(queue) || queues[queue].CurrentItem() != null; + } + + BuildableInfo GetBuildableInfo(string actorType) + { + var ri = player.World.Map.Rules.Actors[actorType]; + var bi = ri.Traits.GetOrDefault(); + + if (bi == null) + throw new LuaException("Actor of type {0} cannot be produced".F(actorType)); + else + return bi; + } + } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs index aeee22ce1bec..3c8926d707ef 100644 --- a/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs +++ b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs @@ -18,22 +18,31 @@ namespace OpenRA.Mods.RA.Scripting { - public enum Trigger { OnIdle, OnDamaged, OnKilled, OnProduction, OnPlayerWon, OnPlayerLost, OnObjectiveAdded, - OnObjectiveCompleted, OnObjectiveFailed, OnCapture, OnAddedToWorld, OnRemovedFromWorld }; + public enum Trigger { OnIdle, OnDamaged, OnKilled, OnProduction, OnOtherProduction, OnPlayerWon, OnPlayerLost, + OnObjectiveAdded, OnObjectiveCompleted, OnObjectiveFailed, OnCapture, OnAddedToWorld, OnRemovedFromWorld }; [Desc("Allows map scripts to attach triggers to this actor via the Triggers global.")] - public class ScriptTriggersInfo : TraitInfo { } + public class ScriptTriggersInfo : ITraitInfo + { + public object Create(ActorInitializer init) { return new ScriptTriggers(init.world); } + } - public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyObjectivesUpdated, INotifyCapture, INotifyAddedToWorld, INotifyRemovedFromWorld, IDisposable + public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyOtherProduction, INotifyObjectivesUpdated, INotifyCapture, INotifyAddedToWorld, INotifyRemovedFromWorld, IDisposable { + readonly World world; + public event Action OnKilledInternal = _ => { }; public event Action OnRemovedInternal = _ => { }; + public event Action OnProducedInternal = (a, b) => { }; + public event Action OnOtherProducedInternal = (a, b) => { }; public Dictionary>> Triggers = new Dictionary>>(); - public ScriptTriggers() + public ScriptTriggers(World world) { - foreach (var t in Enum.GetValues(typeof(Trigger)).Cast()) + this.world = world; + + foreach (Trigger t in Enum.GetValues(typeof(Trigger))) Triggers.Add(t, new List>()); } @@ -101,6 +110,7 @@ public void Killed(Actor self, AttackInfo e) public void UnitProduced(Actor self, Actor other, CPos exit) { + // Run Lua callbacks foreach (var f in Triggers[Trigger.OnProduction]) { try @@ -115,6 +125,9 @@ public void UnitProduced(Actor self, Actor other, CPos exit) return; } } + + // Run any internally bound callbacks + OnProducedInternal(self, other); } public void OnPlayerWon(Player player) @@ -263,22 +276,46 @@ public void RemovedFromWorld(Actor self) OnRemovedInternal(self); } + public void UnitProducedByOther(Actor self, Actor producee, Actor produced) + { + // Run Lua callbacks + foreach (var f in Triggers[Trigger.OnOtherProduction]) + { + try + { + using (var a = producee.ToLuaValue(f.Second)) + using (var b = produced.ToLuaValue(f.Second)) + f.First.Call(a, b).Dispose(); + } + catch (Exception ex) + { + f.Second.FatalError(ex.Message); + return; + } + } + + // Run any internally bound callbacks + OnOtherProducedInternal(producee, produced); + } + public void Clear(Trigger trigger) { - Triggers[trigger].Clear(); + world.AddFrameEndTask(w => + { + Triggers[trigger].Select(p => p.First).Do(f => f.Dispose()); + Triggers[trigger].Clear(); + }); } public void ClearAll() { - foreach (var trigger in Triggers) - trigger.Value.Clear(); + foreach (Trigger t in Enum.GetValues(typeof(Trigger))) + Clear(t); } public void Dispose() { - var pairs = Triggers.Values; - pairs.SelectMany(l => l).Select(p => p.First).Do(f => f.Dispose()); - pairs.Do(l => l.Clear()); + ClearAll(); } } } diff --git a/OpenRA.sln b/OpenRA.sln index 38aba1ae18c3..84606d62db3c 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -62,6 +62,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Red Alert Lua scripts", "Re mods\ra\maps\allies-01-classic\allies01.lua = mods\ra\maps\allies-01-classic\allies01.lua mods\ra\maps\allies-02-classic\allies02.lua = mods\ra\maps\allies-02-classic\allies02.lua mods\ra\maps\desert-shellmap\desert-shellmap.lua = mods\ra\maps\desert-shellmap\desert-shellmap.lua + mods\ra\maps\intervention\intervention.lua = mods\ra\maps\intervention\intervention.lua mods\ra\maps\fort-lonestar\fort-lonestar.lua = mods\ra\maps\fort-lonestar\fort-lonestar.lua EndProjectSection EndProject diff --git a/mods/cnc/maps/gdi04a/gdi04a.lua b/mods/cnc/maps/gdi04a/gdi04a.lua index a9f733763e1a..2a867efe218e 100644 --- a/mods/cnc/maps/gdi04a/gdi04a.lua +++ b/mods/cnc/maps/gdi04a/gdi04a.lua @@ -1,137 +1,166 @@ -Nod1Template = { {HandOfNod, {"e1", "e1", "e3", "e3"}} } -Auto1Template = { {HandOfNod, {"e1", "e1", "e3"}} } +AutoTrigger = { CPos.New(51, 47), CPos.New(52, 47), CPos.New(53, 47), CPos.New(54, 47) } +GDIHeliTrigger = { CPos.New(27, 55), CPos.New(27, 56), CPos.New(28, 56), CPos.New(28, 57), CPos.New(28, 58), CPos.New(28, 59)} + +Nod1Units = { "e1", "e1", "e3", "e3" } +Auto1Units = { "e1", "e1", "e3" } KillsUntilReinforcements = 12 -HeliDelay = {83, 137, 211} +HeliDelay = { 83, 137, 211 } -GDIReinforcements = {"e2", "e2", "e2", "e2"} -GDIReinforcementsWaypoints = {GDIReinforcementsEntry, GDIReinforcementsWP1} +GDIReinforcements = { "e2", "e2", "e2", "e2", "e2" } +GDIReinforcementsWaypoints = { GDIReinforcementsEntry.Location, GDIReinforcementsWP1.Location } NodHelis = { - {Utils.Seconds(HeliDelay[1]), {NodHeliEntry, NodHeliLZ1}, {"e1", "e1", "e3"}}, - {Utils.Seconds(HeliDelay[2]), {NodHeliEntry, NodHeliLZ2}, {"e1", "e1", "e1", "e1"}}, - {Utils.Seconds(HeliDelay[3]), {NodHeliEntry, NodHeliLZ3}, {"e1", "e1", "e3"}} + { Utils.Seconds(HeliDelay[1]), { NodHeliEntry.Location, NodHeliLZ1.Location }, { "e1", "e1", "e3" } }, + { Utils.Seconds(HeliDelay[2]), { NodHeliEntry.Location, NodHeliLZ2.Location }, { "e1", "e1", "e1", "e1" } }, + { Utils.Seconds(HeliDelay[3]), { NodHeliEntry.Location, NodHeliLZ3.Location }, { "e1", "e1", "e3" } } } -SendHeli = function(heli, func) - Reinforcements.ReinforceWithCargo(nod, "tran", heli[2], heli[3], func) - OpenRA.RunAfterDelay(heli[1], function() SendHeli(heli, func) end) -end - -HeliAction = function(heliActor, team) - Actor.AfterMove(heliActor) - Actor.UnloadCargo(heliActor, true) - Actor.Wait(heliActor, Utils.Seconds(2)) - Actor.ScriptedMove(heliActor, NodHeliEntry.Location) - Actor.RemoveSelf(heliActor) - - Team.Do(team, function(actor) - Actor.Hunt(actor) - Actor.OnIdle(actor, Actor.Hunt) - Actor.OnKilled(actor, KillCounter) +SendHeli = function(heli) + units = Reinforcements.ReinforceWithTransport(nod, "tran", heli[3], heli[2], { heli[2][1] }) + Utils.Do(units[2], function(actor) + actor.Hunt() + Trigger.OnIdle(actor, actor.Hunt) + Trigger.OnKilled(actor, KillCounter) end) + Trigger.AfterDelay(heli[1], function() SendHeli(heli) end) end SendGDIReinforcements = function() - Reinforcements.ReinforceWithCargo(player, "apc", GDIReinforcementsWaypoints, GDIReinforcements, function(apc, team) - Team.Add(team, apc) - Actor.OnKilled(apc, SendGDIReinforcements) - Team.Do(team, function(unit) Actor.SetStance(unit, "Defend") end) + Media.PlaySpeechNotification(gdi, "Reinforce") + Reinforcements.ReinforceWithTransport(gdi, "apc", GDIReinforcements, GDIReinforcementsWaypoints, nil, function(apc, team) + table.insert(team, apc) + Trigger.OnAllKilled(team, function() Trigger.AfterDelay(Utils.Seconds(5), SendGDIReinforcements) end) + Utils.Do(team, function(unit) unit.Stance = "Defend" end) end) end BuildNod1 = function() - Production.BuildTeamFromTemplate(nod, Nod1Template, function(team) - Team.Do(team, function(actor) - Actor.OnIdle(actor, Actor.Hunt) - Actor.OnKilled(actor, KillCounter) + if HandOfNod.IsDead then + return + end + + local func = function(team) + Utils.Do(team, function(actor) + Trigger.OnIdle(actor, actor.Hunt) + Trigger.OnKilled(actor, KillCounter) end) - Team.AddEventHandler(team.OnAllKilled, BuildNod1) - end) + Trigger.OnAllKilled(team, BuildNod1) + end + + if not HandOfNod.Build(Nod1Units, func) then + Trigger.AfterDelay(Utils.Seconds(5), BuildNod1) + end end BuildAuto1 = function() - Production.BuildTeamFromTemplate(nod, Auto1Template, function(team) - Team.Do(team, function(actor) - Actor.OnIdle(actor, Actor.Hunt) - Actor.OnKilled(actor, KillCounter) + if HandOfNod.IsDead then + return + end + + local func = function(team) + Utils.Do(team, function(actor) + Trigger.OnIdle(actor, actor.Hunt) + Trigger.OnKilled(actor, KillCounter) end) - end) + end + + if not HandOfNod.IsDead and HandOfNod.Build(Auto1Units, func) then + Trigger.AfterDelay(Utils.Seconds(5), BuildAuto1) + end end kills = 0 KillCounter = function() kills = kills + 1 end -Auto1Triggered = false -GDIHeliTriggered = false ReinforcementsSent = false Tick = function() + nod.Cash = 1000 + if not ReinforcementsSent and kills >= KillsUntilReinforcements then ReinforcementsSent = true + gdi.MarkCompletedObjective(reinforcementsObjective) SendGDIReinforcements() end - if Mission.RequiredUnitsAreDestroyed(player) then - OpenRA.RunAfterDelay(Utils.Seconds(1), MissionFailed) - end - - if not Auto1Triggered then - -- FIXME: replace with cell trigger when available - local units = Map.FindUnitsInCircle(player, Auto1Trigger, 2) - if #units > 0 then - Auto1Triggered = true - BuildAuto1() - end - elseif not GDIHeliTriggered then - -- FIXME: replace with cell trigger when available - local units = Map.FindUnitsInCircle(player, GDIHeliLZ, 2) - if #units > 0 then - GDIHeliTriggered = true - Reinforcements.ReinforceWithCargo(player, "tran", {GDIHeliEntry, GDIHeliLZ}, nil, Actor.AfterMove) - end + if gdi.HasNoRequiredUnits() then + Trigger.AfterDelay(Utils.Seconds(1), function() gdi.MarkFailedObjective(gdiObjective) end) end end SetupWorld = function() - OpenRA.GiveCash(nod, 10000) - Production.EventHandlers.Setup(nod) - - Utils.Do(Mission.GetGroundAttackersOf(nod), function(unit) - Actor.OnKilled(unit, KillCounter) + Utils.Do(nod.GetGroundAttackers(nod), function(unit) + Trigger.OnKilled(unit, KillCounter) end) - Utils.Do(Mission.GetGroundAttackersOf(player), function(unit) - Actor.SetStance(unit, "Defend") + Utils.Do(gdi.GetGroundAttackers(), function(unit) + unit.Stance = "Defend" end) - Actor.Hunt(Hunter1) - Actor.Hunt(Hunter2) + Hunter1.Hunt() + Hunter2.Hunt() - Actor.OnRemovedFromWorld(crate, MissionAccomplished) + Trigger.OnRemovedFromWorld(crate, function() gdi.MarkCompletedObjective(gdiObjective) end) end WorldLoaded = function() - Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) - - player = OpenRA.GetPlayer("GDI") - nod = OpenRA.GetPlayer("Nod") + gdi = Player.GetPlayer("GDI") + nod = Player.GetPlayer("Nod") SetupWorld() - OpenRA.RunAfterDelay(1, BuildNod1) + Trigger.OnObjectiveAdded(gdi, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective") + end) + Trigger.OnObjectiveCompleted(gdi, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed") + end) + Trigger.OnObjectiveFailed(gdi, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed") + end) + + Trigger.OnPlayerWon(gdi, function() + Media.PlaySpeechNotification(gdi, "Win") + Trigger.AfterDelay(Utils.Seconds(1), function() + Media.PlayMovieFullscreen("burdet1.vqa") + end) + end) + + Trigger.OnPlayerLost(gdi, function() + Media.PlaySpeechNotification(gdi, "Lose") + Trigger.AfterDelay(Utils.Seconds(1), function() + Media.PlayMovieFullscreen("gameover.vqa") + end) + end) + + gdiObjective = gdi.AddPrimaryObjective("Retrieve the crate with the stolen rods.") + reinforcementsObjective = gdi.AddSecondaryObjective("Eliminate " .. KillsUntilReinforcements .. " Nod units for reinforcements.") + nod.AddPrimaryObjective("Defend against the GDI forces.") + + BuildNod1() Utils.Do(NodHelis, function(heli) - OpenRA.RunAfterDelay(heli[1], function() SendHeli(heli, HeliAction) end) + Trigger.AfterDelay(heli[1], function() SendHeli(heli) end) end) - OpenRA.SetViewportCenterPosition(Actor56.CenterPosition) -end + autoTrigger = false + Trigger.OnEnteredFootprint(AutoTrigger, function(a, id) + if not autoTrigger and a.Owner == gdi then + autoTrigger = true + Trigger.RemoveFootprintTrigger(id) + BuildAuto1() + end + end) -MissionAccomplished = function() - Mission.MissionOver({ player }, nil, true) - Media.PlayMovieFullscreen("burdet1.vqa") -end + gdiHeliTrigger = false + Trigger.OnEnteredFootprint(GDIHeliTrigger, function(a, id) + if not gdiHeliTrigger and a.Owner == gdi then + gdiHeliTrigger = true + Trigger.RemoveFootprintTrigger(id) + Reinforcements.ReinforceWithTransport(gdi, "tran", nil, { GDIHeliEntry.Location, GDIHeliLZ.Location }) + end + end) -MissionFailed = function() - Mission.MissionOver(nil, { player }, true) - Media.PlayMovieFullscreen("gameover.vqa") + Camera.Position = Actor56.CenterPosition + + Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) end diff --git a/mods/cnc/maps/gdi04a/map.yaml b/mods/cnc/maps/gdi04a/map.yaml index dc998841522a..f22a87fd5205 100644 --- a/mods/cnc/maps/gdi04a/map.yaml +++ b/mods/cnc/maps/gdi04a/map.yaml @@ -326,6 +326,12 @@ Actors: Health: 1 Facing: 0 SubCell: 3 + Actor710: e1 + Location: 12,54 + Owner: GDI + Health: 1 + Facing: 0 + SubCell: 5 Actor71: e1 Location: 12,54 Owner: GDI @@ -416,6 +422,12 @@ Actors: Health: 1 Facing: 64 SubCell: 4 + Actor860: e2 + Location: 14,54 + Owner: GDI + Health: 1 + Facing: 0 + SubCell: 5 Actor86: e2 Location: 14,54 Owner: GDI @@ -524,9 +536,6 @@ Actors: GDIReinforcementsEntry: waypoint Location: 7,50 Owner: Neutral - Auto1Trigger: waypoint - Location: 52,47 - Owner: Neutral NodHeliEntry: waypoint Location: 41,23 Owner: Neutral @@ -547,8 +556,8 @@ Rules: -SpawnMPUnits: -MPStartLocations: -CrateSpawner: - LuaScriptInterface: - LuaScripts: gdi04a.lua + LuaScript: + Scripts: gdi04a.lua ObjectivesPanel: PanelName: MISSION_OBJECTIVES Player: @@ -595,18 +604,16 @@ Rules: GenericVisibility: Enemy, Ally, Neutral GenericStancePrefix: false ShowOwnerRow: false - HARV: - -MustBeDestroyed: CRATE: Crate: Lifetime: 9999 - LuaScriptEvents: HealUnitsCrateAction: -RevealMapCrateAction: -GiveMcvCrateAction: -GiveCashCrateAction: -ExplodeCrateAction@fire: -GrantUpgradeCrateAction@cloak: + -DuplicateUnitCrateAction: ScriptTriggers: Sequences: @@ -615,7 +622,7 @@ VoxelSequences: Weapons: Tiberium: - Warhead: SpreadDamage + Warhead@1Dam: SpreadDamage Damage: 6 Voices: diff --git a/mods/cnc/maps/gdi04b/gdi04b.lua b/mods/cnc/maps/gdi04b/gdi04b.lua index 8f539d2073ec..bafbfd97d923 100644 --- a/mods/cnc/maps/gdi04b/gdi04b.lua +++ b/mods/cnc/maps/gdi04b/gdi04b.lua @@ -1,172 +1,198 @@ -NodxTemplate = { {HandOfNod, {"e1", "e1", "e3", "e3"}} } -AutoTemplate = { {HandOfNod, {"e1", "e1", "e1", "e3", "e3"}} } +BhndTrigger = { CPos.New(39, 21), CPos.New(40, 21), CPos.New(41, 21) } +Atk1Trigger = { CPos.New(35, 37) } +Atk2Trigger = { CPos.New(9, 44), CPos.New(10, 44), CPos.New(11, 44), CPos.New(12, 44), CPos.New(13, 44) } +AutoTrigger = { CPos.New(5, 30), CPos.New(6, 30), CPos.New(7, 30), CPos.New(8, 30), CPos.New(9, 30), CPos.New(10, 30), CPos.New(11, 30), CPos.New(12, 30), CPos.New(13, 30) } +GDIHeliTrigger = { CPos.New(11, 11), CPos.New(11, 12), CPos.New(11, 13), CPos.New(11, 14), CPos.New(11, 15), CPos.New(12, 15), CPos.New(13, 15), CPos.New(14, 15), CPos.New(15, 15), CPos.New(16, 15) } -KillsUntilReinforcements = 12 -kills = 0 -KillCounter = function() kills = kills + 1 end +Hunters = { Hunter1, Hunter2, Hunter3, Hunter4, Hunter5 } +NodxUnits = { "e1", "e1", "e3", "e3" } +AutoUnits = { "e1", "e1", "e1", "e3", "e3" } -GDIReinforcements = {"e2", "e2", "e2", "e2"} -GDIReinforcementsWaypoints = {GDIReinforcementsEntry, GDIReinforcementsWP1} +KillsUntilReinforcements = 12 -NodHeli = {{HeliEntry, NodHeliLZ}, {"e1", "e1", "e3", "e3"}} +GDIReinforcements = { "e2", "e2", "e2", "e2", "e2" } +GDIReinforcementsWaypoints = { GDIReinforcementsEntry.Location, GDIReinforcementsWP1.Location } -SendHeli = function(heli, func) - Reinforcements.ReinforceWithCargo(nod, "tran", heli[1], heli[2], func) -end +NodHeli = { { HeliEntry.Location, NodHeliLZ.Location }, { "e1", "e1", "e3", "e3" } } -HeliAction = function(heliActor, team) - Actor.AfterMove(heliActor) - Actor.UnloadCargo(heliActor, true) - Actor.Wait(heliActor, Utils.Seconds(2)) - Actor.ScriptedMove(heliActor, HeliEntry.Location) - Actor.RemoveSelf(heliActor) - - Team.Do(team, function(actor) - Actor.Hunt(actor) - Actor.OnIdle(actor, Actor.Hunt) - Actor.OnKilled(actor, KillCounter) +SendHeli = function(heli) + units = Reinforcements.ReinforceWithTransport(nod, "tran", heli[2], heli[1], { heli[1][1] }) + Utils.Do(units[2], function(actor) + actor.Hunt() + Trigger.OnIdle(actor, actor.Hunt) + Trigger.OnKilled(actor, KillCounter) end) end SendGDIReinforcements = function() - Reinforcements.ReinforceWithCargo(player, "apc", GDIReinforcementsWaypoints, GDIReinforcements, function(apc, team) - Team.Add(team, apc) - Actor.OnKilled(apc, SendGDIReinforcements) - Team.Do(team, function(unit) Actor.SetStance(unit, "Defend") end) + Media.PlaySpeechNotification(gdi, "Reinforce") + Reinforcements.ReinforceWithTransport(gdi, "apc", GDIReinforcements, GDIReinforcementsWaypoints, nil, function(apc, team) + table.insert(team, apc) + Trigger.OnAllKilled(team, function() Trigger.AfterDelay(Utils.Seconds(5), SendGDIReinforcements) end) + Utils.Do(team, function(unit) unit.Stance = "Defend" end) end) end -Build = function(template, repeats, func) - Production.BuildTeamFromTemplate(nod, template, function(team) - Team.Do(team, func) +Build = function(unitTypes, repeats, func) + if HandOfNod.IsDead then + return + end + + local innerFunc = function(units) + Utils.Do(units, func) if repeats then - Team.AddEventHandler(team.OnAllKilled, function() - Build(template, repeats, func) + Trigger.OnAllKilled(units, function() + Build(unitTypes, repeats, func) end) end - end) + end + if not HandOfNod.Build(unitTypes, innerFunc) then + Trigger.AfterDelay(Utils.Seconds(5), function() + Build(unitTypes, repeats, func) + end) + end end BuildNod1 = function() - Build(NodxTemplate, false, function(actor) - Actor.OnKilled(actor, KillCounter) - Actor.Patrol(actor, {waypoint1, waypoint2, waypoint3, waypoint4}, 0, false) - Actor.OnIdle(actor, Actor.Hunt) + Build(NodxUnits, false, function(actor) + Trigger.OnKilled(actor, KillCounter) + actor.Patrol({ waypoint1.Location, waypoint2.Location, waypoint3.Location, waypoint4.Location }, false) + Trigger.OnIdle(actor, actor.Hunt) end) end BuildNod2 = function() - Build(NodxTemplate, false, function(actor) - Actor.OnKilled(actor, KillCounter) - Actor.Patrol(actor, {waypoint1, waypoint2}, 0, false) - Actor.OnIdle(actor, Actor.Hunt) + Build(NodxUnits, false, function(actor) + Trigger.OnKilled(actor, KillCounter) + actor.Patrol({ waypoint1.Location, waypoint2.Location }, false) + Trigger.OnIdle(actor, actor.Hunt) end) end BuildAuto = function() - Build(AutoTemplate, true, function(actor) - Actor.OnKilled(actor, KillCounter) - Actor.OnIdle(actor, Actor.Hunt) + Build(AutoUnits, true, function(actor) + Trigger.OnKilled(actor, KillCounter) + Trigger.OnIdle(actor, actor.Hunt) end) end --- FIXME: replace with real cell trigger when available -CellTrigger = function(player, trigger, radius, func) - local units = Map.FindUnitsInCircle(player, trigger, radius) - if #units > 0 then - func() - end -end - -BhndTriggered = false -Atk1Triggered = false -Atk2Triggered = false -AutoTriggered = false -GDIHeliTriggered = false ReinforcementsSent = false - +kills = 0 +KillCounter = function() kills = kills + 1 end Tick = function() + nod.Cash = 1000 + if not ReinforcementsSent and kills >= KillsUntilReinforcements then ReinforcementsSent = true + gdi.MarkCompletedObjective(reinforcementsObjective) SendGDIReinforcements() end - if Mission.RequiredUnitsAreDestroyed(player) then - OpenRA.RunAfterDelay(Utils.Seconds(1), MissionFailed) - end - - if not BhndTriggered then - CellTrigger(player, BhndTrigger, 2, function() - BhndTriggered = true - SendHeli(NodHeli, HeliAction) - end) - end - - if not Atk1Triggered then - CellTrigger(player, Atk1Trigger, 2, function() - Atk1Triggered = true - BuildNod1() - end) - elseif not Atk2Triggered then - CellTrigger(player, Atk2Trigger, 2, function() - Atk2Triggered = true - BuildNod2() - end) - elseif not AutoTriggered then - CellTrigger(player, AutoTrigger, 2, function() - AutoTriggered = true - BuildAuto() - OpenRA.RunAfterDelay(Utils.Seconds(5), function() - Actor.Hunt(tank) - end) - end) - elseif not GDIHeliTriggered then - CellTrigger(player, HeliTrigger, 2, function() - GDIHeliTriggered = true - Reinforcements.ReinforceWithCargo(player, "tran", {HeliEntry, GDIHeliLZ}, nil, Actor.AfterMove) + if gdi.HasNoRequiredUnits() then + Trigger.AfterDelay(Utils.Seconds(1), function() + gdi.MarkFailedObjective(gdiObjective) end) end end SetupWorld = function() - OpenRA.GiveCash(nod, 10000) - Production.EventHandlers.Setup(nod) - - Utils.Do(Mission.GetGroundAttackersOf(nod), function(unit) - Actor.OnKilled(unit, KillCounter) + Utils.Do(nod.GetGroundAttackers(), function(unit) + Trigger.OnKilled(unit, KillCounter) end) - Utils.Do(Mission.GetGroundAttackersOf(player), function(unit) - Actor.SetStance(unit, "Defend") + Utils.Do(gdi.GetGroundAttackers(), function(unit) + unit.Stance = "Defend" end) - hunters1 = Team.New({Hunter1, Hunter2}) - hunters2 = Team.New({Hunter3, Hunter4, Hunter5}) - - OpenRA.RunAfterDelay(1, function() Team.Do(hunters1, Actor.Hunt) end) - OpenRA.RunAfterDelay(1, function() Team.Do(hunters2, Actor.Hunt) end) + Utils.Do(Hunters, function(actor) actor.Hunt() end) - Actor.OnRemovedFromWorld(crate, MissionAccomplished) + Trigger.OnRemovedFromWorld(crate, function() gdi.MarkCompletedObjective(gdiObjective) end) end WorldLoaded = function() - Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) + gdi = Player.GetPlayer("GDI") + nod = Player.GetPlayer("Nod") + + Trigger.OnObjectiveAdded(gdi, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective") + end) + Trigger.OnObjectiveCompleted(gdi, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed") + end) + Trigger.OnObjectiveFailed(gdi, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed") + end) + + Trigger.OnPlayerWon(gdi, function() + Media.PlaySpeechNotification(gdi, "Win") + Trigger.AfterDelay(Utils.Seconds(1), function() + Media.PlayMovieFullscreen("burdet1.vqa") + end) + end) - player = OpenRA.GetPlayer("GDI") - nod = OpenRA.GetPlayer("Nod") + Trigger.OnPlayerLost(gdi, function() + Media.PlaySpeechNotification(gdi, "Lose") + Trigger.AfterDelay(Utils.Seconds(1), function() + Media.PlayMovieFullscreen("gameover.vqa") + end) + end) + + gdiObjective = gdi.AddPrimaryObjective("Retrieve the crate with the stolen rods.") + reinforcementsObjective = gdi.AddSecondaryObjective("Eliminate " .. KillsUntilReinforcements .. " Nod units for reinforcements.") + nod.AddPrimaryObjective("Defend against the GDI forces.") SetupWorld() - OpenRA.SetViewportCenterPosition(GDIReinforcementsWP1.CenterPosition) -end + bhndTrigger = false + Trigger.OnExitedFootprint(BhndTrigger, function(a, id) + if not bhndTrigger and a.Owner == gdi then + bhndTrigger = true + Trigger.RemoveFootprintTrigger(id) + SendHeli(NodHeli) + end + end) -MissionAccomplished = function() - Mission.MissionOver({ player }, nil, true) - Media.PlayMovieFullscreen("burdet1.vqa") -end + atk1Trigger = false + Trigger.OnExitedFootprint(Atk1Trigger, function(a, id) + if not atk1Trigger and a.Owner == gdi then + atk1Trigger = true + Trigger.RemoveFootprintTrigger(id) + BuildNod1() + end + end) + + atk2Trigger = false + Trigger.OnEnteredFootprint(Atk2Trigger, function(a, id) + if not atk2Trigger and a.Owner == gdi then + atk2Trigger = true + Trigger.RemoveFootprintTrigger(id) + BuildNod2() + end + end) + + autoTrigger = false + Trigger.OnEnteredFootprint(AutoTrigger, function(a, id) + if not autoTrigger and a.Owner == gdi then + autoTrigger = true + Trigger.RemoveFootprintTrigger(id) + BuildAuto() + Trigger.AfterDelay(Utils.Seconds(4), function() + tank.Hunt() + end) + end + end) + + gdiHeliTrigger = false + Trigger.OnEnteredFootprint(GDIHeliTrigger, function(a, id) + if not gdiHeliTrigger and a.Owner == gdi then + gdiHeliTrigger = true + Trigger.RemoveFootprintTrigger(id) + Reinforcements.ReinforceWithTransport(gdi, "tran", nil, { HeliEntry.Location, GDIHeliLZ.Location }) + end + end) -MissionFailed = function() - Mission.MissionOver(nil, { player }, true) - Media.PlayMovieFullscreen("gameover.vqa") + Camera.Position = GDIReinforcementsWP1.CenterPosition + + Media.PlayMovieFullscreen("bkground.vqa", function() Media.PlayMovieFullscreen("nitejump.vqa") end) end diff --git a/mods/cnc/maps/gdi04b/map.png b/mods/cnc/maps/gdi04b/map.png index 7884cb32643c..4c734350ee48 100644 Binary files a/mods/cnc/maps/gdi04b/map.png and b/mods/cnc/maps/gdi04b/map.png differ diff --git a/mods/cnc/maps/gdi04b/map.yaml b/mods/cnc/maps/gdi04b/map.yaml index 6041dc7d15e8..88b73b290b77 100644 --- a/mods/cnc/maps/gdi04b/map.yaml +++ b/mods/cnc/maps/gdi04b/map.yaml @@ -597,21 +597,6 @@ Actors: crate: CRATE Location: 14,13 Owner: Neutral - BhndTrigger: waypoint - Location: 40,21 - Owner: Neutral - Atk1Trigger: waypoint - Location: 35,37 - Owner: Neutral - Atk2Trigger: waypoint - Location: 11,44 - Owner: Neutral - AutoTrigger: waypoint - Location: 12,30 - Owner: Neutral - HeliTrigger: waypoint - Location: 13,15 - Owner: Neutral HeliEntry: waypoint Location: 4,26 Owner: Neutral @@ -626,8 +611,8 @@ Rules: -SpawnMPUnits: -MPStartLocations: -CrateSpawner: - LuaScriptInterface: - LuaScripts: gdi04b.lua + LuaScript: + Scripts: gdi04b.lua ObjectivesPanel: PanelName: MISSION_OBJECTIVES Player: @@ -674,21 +659,20 @@ Rules: GenericVisibility: Enemy, Ally, Neutral GenericStancePrefix: false ShowOwnerRow: false - HARV: - -MustBeDestroyed: E3: AutoTarget: ScanRadius: 5 CRATE: Crate: Lifetime: 9999 - LuaScriptEvents: HealUnitsCrateAction: -RevealMapCrateAction: -GiveMcvCrateAction: -GiveCashCrateAction: -ExplodeCrateAction@fire: -GrantUpgradeCrateAction@cloak: + -DuplicateUnitCrateAction: + ScriptTriggers: Sequences: @@ -696,7 +680,7 @@ VoxelSequences: Weapons: Tiberium: - Warhead: SpreadDamage + Warhead@1Dam: SpreadDamage Damage: 4 Voices: diff --git a/mods/cnc/maps/gdi04c/map.png b/mods/cnc/maps/gdi04c/map.png index 4c734350ee48..7884cb32643c 100644 Binary files a/mods/cnc/maps/gdi04c/map.png and b/mods/cnc/maps/gdi04c/map.png differ diff --git a/mods/ra/maps/intervention/intervention.lua b/mods/ra/maps/intervention/intervention.lua new file mode 100644 index 000000000000..4aa893ab01bf --- /dev/null +++ b/mods/ra/maps/intervention/intervention.lua @@ -0,0 +1,279 @@ +BeachheadTrigger = +{ + CPos.New(120, 90), CPos.New(120, 89), CPos.New(120, 88), CPos.New(121, 88), CPos.New(122, 88), CPos.New(123, 88), CPos.New(124, 88), + CPos.New(125, 88), CPos.New(126, 88), CPos.New(126, 89), CPos.New(127, 89), CPos.New(128, 89), CPos.New(128, 90), CPos.New(129, 90), + CPos.New(130, 90), CPos.New(130, 91), CPos.New(131, 91), CPos.New(132, 91), CPos.New(133, 91), CPos.New(134, 91), CPos.New(134, 92), + CPos.New(135, 92), CPos.New(136, 92), CPos.New(137, 92), CPos.New(137, 93), CPos.New(138, 93), CPos.New(139, 93), CPos.New(140, 93), + CPos.New(140, 94), CPos.New(140, 95), CPos.New(140, 96), CPos.New(140, 97), CPos.New(140, 98), CPos.New(140, 99), CPos.New(140, 100), + CPos.New(139, 100), CPos.New(139, 101), CPos.New(139, 102), CPos.New(138, 102), CPos.New(138, 103), CPos.New(138, 104), + CPos.New(137, 104), CPos.New(137, 105), CPos.New(137, 106), CPos.New(136, 106), CPos.New(136, 107) +} + +BaseRaidInterval = Utils.Minutes(3) +BaseFrontAttackInterval = Utils.Minutes(3) + Utils.Seconds(30) +BaseRearAttackInterval = Utils.Minutes(8) +UBoatPatrolDelay = Utils.Minutes(2) + Utils.Seconds(30) +BaseFrontAttackWpts = { PatrolWpt1.Location, BaseRaidWpt1.Location } + +Village = { FarmHouse1, FarmHouse2, FarmHouse3, FarmHouse4, FarmHouse5, FarmHouse6, FarmHouse7, FarmHouse8, FarmHouse9, Church } +VillageRaidInterval = Utils.Minutes(3) +VillageRaidAircraft = { "mig", "mig" } +VillageRaidWpts = { VillageRaidEntrypoint.Location, VillageRaidWpt1.Location, VillageRaidWpt2.Location } + +BaseRaidAircraft = { "mig", "mig" } +BaseRaidWpts = { BaseRaidEntrypoint.Location, UboatPatrolWpt1.Location, BaseRaidWpt2.Location } + +BaseFrontAttackUnits = { "e3", "e3", "e1", "e1", "e1", "3tnk", "3tnk", "apc" } +BaseRearAttackUnits = { "e3", "e3", "e1", "e1", "3tnk", "3tnk", "v2rl" } +BaseRearAttackWpts = { GroundAttackWpt1.Location, BaseRearAttackWpt1.Location, BaseRearAttackWpt2.Location, BaseRearAttackWpt3.Location } + +SovietHarvesters = { Harvester1, Harvester2, Harvester3 } +HarvesterGuard = { HarvGuard1, HarvGuard2, HarvGuard3 } + +UBoats = { Uboat1, Uboat2, Uboat3, Uboat4, Uboat5, Uboat6 } +UboatPatrolWpts1 = { UboatPatrolWpt1.Location, UboatPatrolWpt2.Location, UboatPatrolWpt3.Location, UboatPatrolWpt4.Location } +UboatPatrolWpts2 = { UboatPatrolWpt4.Location, UboatPatrolWpt2.Location, UboatPatrolWpt1.Location } +UBoatPatrolUnits = { "ss" } + +HunterSubs = { "ss", "ss" } + +GroundPatrolWpts = { PatrolWpt1.Location, PatrolWpt2.Location } +GroundPatrolUnits = +{ + { "e1", "e1", "e1", "e3", "e3", "dog" }, + { "apc", "apc", "ftrk" }, + { "3tnk", "3tnk" } +} +Paratroopers = { "e1", "e1", "e1", "e3", "e3" } + +ParadropSovietUnits = function() + local start = BaseRaidEntrypoint.CenterPosition + WVec.New(0, 0, Actor.CruiseAltitude("badr")) + local transport = Actor.Create("badr", true, { CenterPosition = start, Owner = soviets, Facing = (Utils.CenterOfCell(MCVDeployLocation.Location) - start).Facing }) + + Utils.Do(Paratroopers, function(type) + local a = Actor.Create(type, false, { Owner = soviets }) + transport.LoadPassenger(a) + Trigger.OnIdle(a, function(b) b.Hunt() end) + end) + + transport.Paradrop(MCVDeployLocation.Location) +end + +AirRaid = function(planeTypes, ingress, egress, target) + if target == nil then + return + end + + for i = 1, #planeTypes do + Trigger.AfterDelay((i - 1) * Utils.Seconds(1), function() + local start = Utils.CenterOfCell(ingress[1]) + WVec.New(0, 0, Actor.CruiseAltitude(planeTypes[i])) + local plane = Actor.Create(planeTypes[i], true, { CenterPosition = start, Owner = soviets, Facing = (Utils.CenterOfCell(ingress[2]) - start).Facing }) + + Utils.Do(ingress, function(wpt) plane.Move(wpt) end) + plane.Attack(target) + Utils.Do(egress, function(wpt) plane.Move(wpt) end) + plane.Destroy() + end) + end +end + +BaseRaid = function() + local targets = Map.ActorsInBox(AlliedAreaTopLeft.CenterPosition, AlliedAreaBottomRight.CenterPosition, function(actor) + return actor.Owner == player and actor.HasProperty("StartBuildingRepairs") + end) + + if #targets == 0 then + return + end + + local target = Utils.Random(targets) + + AirRaid(BaseRaidAircraft, BaseRaidWpts, { VillageRaidEntrypoint.Location }, target) + + Trigger.AfterDelay(BaseRaidInterval, BaseRaid) +end + +VillageRaid = function() + local target = nil + Utils.Do(Village, function(tgt) + if target == nil and not tgt.IsDead then + target = tgt + return + end + end) + + if target == nil then + return + end + + AirRaid(VillageRaidAircraft, VillageRaidWpts, { BaseRaidEntrypoint.Location }, target) + + Trigger.AfterDelay(VillageRaidInterval, VillageRaid) +end + +SendUboatPatrol = function(team) + Trigger.AfterDelay(UBoatPatrolDelay, function() + Utils.Do(team, function(uboat) + if not uboat.IsDead then + uboat.PatrolUntil(UboatPatrolWpts1, function() + return Time.GameTime > Utils.Minutes(2) + UBoatPatrolDelay + end) + uboat.Patrol(UboatPatrolWpts2) + end + end) + end) +end + +SendGroundPatrol = function(team) + Utils.Do(team, function(unit) unit.Patrol(GroundPatrolWpts, true, Utils.Seconds(3)) end) + Utils.Do(team, function(unit) + Trigger.OnIdle(unit, function(actor) actor.Hunt() end) + end) + Trigger.OnAllKilled(team, function() + Build(Utils.Random(GroundPatrolUnits), SendGroundPatrol) + end) +end + +BaseFrontAttack = function(team) + Utils.Do(team, function(unit) unit.Patrol(BaseFrontAttackWpts, false) end) + Utils.Do(team, function(unit) + Trigger.OnIdle(unit, function(actor) actor.Hunt() end) + end) + Trigger.AfterDelay(BaseFrontAttackInterval, function() Build(BaseFrontAttackUnits, BaseFrontAttack) end) +end + +BaseRearAttack = function(team) + Utils.Do(team, function(unit) unit.Patrol(BaseRearAttackWpts, false) end) + Utils.Do(team, function(unit) + Trigger.OnIdle(unit, function(actor) actor.Hunt() end) + end) + Trigger.AfterDelay(BaseRearAttackInterval, function() Build(BaseRearAttackUnits, BaseRearAttack) end) +end + +Build = function(units, action) + if not soviets.Build(units, action) then + Trigger.AfterDelay(Utils.Seconds(15), function() + Build(units, action) + end) + end +end + +SetupWorld = function() + Utils.Do(SovietHarvesters, function(a) a.FindResources() end) + + Utils.Do(SovietHarvesters, function(harvester) + Trigger.OnDamaged(harvester, function(h) + Utils.Do(HarvesterGuard, function(g) + if not g.IsDead then + g.Stop() + g.AttackMove(h.Location, 3) + end + end) + end) + end) + + Utils.Do(UBoats, function(a) a.Stance = "Defend" end) + + Utils.Do(Map.NamedActors, function(actor) + if actor.Owner == soviets and actor.HasProperty("StartBuildingRepairs") then + Trigger.OnDamaged(actor, function(building) + if building.Owner == soviets then + building.StartBuildingRepairs() + end + end) + end + end) + + WarFactory.RallyPoint = Rallypoint.Location + WarFactory.IsPrimaryBuilding = true + Barracks.IsPrimaryBuilding = true + SubPen.IsPrimaryBuilding = true +end + +Tick = function() + if soviets.Resources > soviets.ResourceCapacity * 0.75 then + soviets.Resources = soviets.Resources - ((soviets.ResourceCapacity * 0.01) / 25) + end + + if player.HasNoRequiredUnits() then + player.MarkFailedObjective(villageObjective) + end +end + +WorldLoaded = function() + player = Player.GetPlayer("Allies") + soviets = Player.GetPlayer("Soviets") + + Trigger.OnObjectiveAdded(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective") + end) + Trigger.OnObjectiveCompleted(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed") + end) + Trigger.OnObjectiveFailed(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed") + end) + + Trigger.OnPlayerWon(player, function() + Media.PlaySpeechNotification(player, "Win") + end) + + Trigger.OnPlayerLost(player, function() + Media.PlaySpeechNotification(player, "Lose") + end) + + sovietObjective = soviets.AddPrimaryObjective("Destroy the village.") + villageObjective = player.AddPrimaryObjective("Save the village.") + beachheadObjective = player.AddSecondaryObjective("Get your MCV to the main island.") + + beachheadTrigger = false + Trigger.OnExitedFootprint(BeachheadTrigger, function(a, id) + if not beachheadTrigger and a.Owner == player and a.Type == "mcv" then + beachheadTrigger = true + Trigger.RemoveFootprintTrigger(id) + player.MarkCompletedObjective(beachheadObjective) + + captureObjective = player.AddPrimaryObjective("Locate and capture the enemy's Air Force HQ.") + Trigger.OnCapture(AirForceHQ, function() + Trigger.AfterDelay(Utils.Seconds(3), function() + player.MarkCompletedObjective(captureObjective) + player.MarkCompletedObjective(villageObjective) + end) + end) + Trigger.OnKilled(AirForceHQ, function() player.MarkFailedObjective(captureObjective) end) + + Trigger.AfterDelay(BaseFrontAttackInterval, function() + Build(BaseFrontAttackUnits, BaseFrontAttack) + ParadropSovietUnits() + end) + Trigger.AfterDelay(BaseRearAttackInterval, function() + Build(BaseRearAttackUnits, BaseRearAttack) + end) + Trigger.AfterDelay(BaseRaidInterval, BaseRaid) + + Trigger.AfterDelay(UBoatPatrolDelay, function() + Build(HunterSubs, function(subs) + Utils.Do(subs, function(sub) + Trigger.OnIdle(sub, function(s) s.Hunt() end) + end) + end) + end) + end + end) + + Trigger.OnAllKilled(Village, function() player.MarkFailedObjective(villageObjective) end) + + SetupWorld() + + Trigger.AfterDelay(VillageRaidInterval, VillageRaid) + + Trigger.AfterDelay(1, function() Build(UBoatPatrolUnits, SendUboatPatrol) end) + Trigger.AfterDelay(1, function() Build(Utils.Random(GroundPatrolUnits), SendGroundPatrol) end) + + Reinforcements.Reinforce(player, { "mcv" }, { MCVInsertLocation.Location, MCVDeployLocation.Location }, 0, function(mcv) + mcv.Deploy() + end) + + Camera.Position = CameraSpot.CenterPosition + Trigger.AfterDelay(Utils.Seconds(5), function() CameraSpot.Destroy() end) +end diff --git a/mods/ra/maps/intervention/map.yaml b/mods/ra/maps/intervention/map.yaml index ad2515229918..1629c1702737 100644 --- a/mods/ra/maps/intervention/map.yaml +++ b/mods/ra/maps/intervention/map.yaml @@ -2205,7 +2205,7 @@ Actors: Actor14: wood Location: 23,57 Owner: Neutral - Camera: CAMERA + CameraSpot: CAMERA Location: 79,137 Owner: Allies @@ -2220,8 +2220,8 @@ Rules: -CrateSpawner: -SpawnMPUnits: -MPStartLocations: - LuaScriptInterface: - LuaScripts: mission.lua + LuaScript: + Scripts: intervention.lua ObjectivesPanel: PanelName: MISSION_OBJECTIVES CAMERA: @@ -2229,17 +2229,26 @@ Rules: Range: 18c0 MISS: Tooltip: - Name: Air Force HQ + Name: Soviet Air Force HQ Capturable: Type: building AllowAllies: False AllowNeutral: False AllowEnemies: True CaptureThreshold: 1.0 - E6: + E6.MOD: + Inherits: E6 + Buildable: + Prerequisites: ~barracks + -RepairsBridges: Captures: CaptureTypes: building Sabotage: False + RenderInfantry: + Image: e6 + E6: + Buildable: + Prerequisites: ~disabled HPAD: ProvidesCustomPrerequisite: Prerequisite: givefix @@ -2254,12 +2263,12 @@ Rules: Name: Weapons Factory or Helipad MIG: Buildable: - Prerequisites: afld + Prerequisites: ~afld LimitedAmmo: Ammo: 2 HELI: Buildable: - Prerequisites: hpad + Prerequisites: ~hpad Valued: Cost: 1500 SAM: @@ -2270,19 +2279,49 @@ Rules: TSLA: Power: Amount: -50 - ^Vehicles: + ^Building: + Tooltip: + GenericVisibility: Enemy + ShowOwnerRow: false + ^Vehicle: MustBeDestroyed: + Tooltip: + GenericVisibility: Enemy + ShowOwnerRow: false ^Tank: MustBeDestroyed: + Tooltip: + GenericVisibility: Enemy + ShowOwnerRow: false ^Infantry: MustBeDestroyed: + Tooltip: + GenericVisibility: Enemy + ShowOwnerRow: false ^Plane: MustBeDestroyed: + Tooltip: + GenericVisibility: Enemy + ShowOwnerRow: false ^Ship: MustBeDestroyed: + Tooltip: + GenericVisibility: Enemy + ShowOwnerRow: false + ^Wall: + Tooltip: + ShowOwnerRow: false + ^Husk: + Tooltip: + GenericVisibility: Enemy, Ally, Neutral + GenericStancePrefix: false + ShowOwnerRow: false ATEK: Buildable: Prerequisites: ~disabled + STEK: + Buildable: + Prerequisites: ~disabled GAP: Buildable: Prerequisites: ~disabled @@ -2292,9 +2331,24 @@ Rules: PDOX: Buildable: Prerequisites: ~disabled + E4: + Buildable: + Prerequisites: ~disabled E7: Buildable: Prerequisites: ~disabled + THF: + Buildable: + Prerequisites: ~disabled + HIJACKER: + Buildable: + Prerequisites: ~disabled + SHOK: + Buildable: + Prerequisites: ~disabled + SNIPER: + Buildable: + Prerequisites: ~disabled 2TNK: Buildable: Prerequisites: ~disabled @@ -2310,6 +2364,9 @@ Rules: MNLY.AT: Buildable: Prerequisites: ~disabled + MNLY.AP: + Buildable: + Prerequisites: ~disabled MRJ: Buildable: Prerequisites: ~disabled @@ -2319,6 +2376,9 @@ Rules: HIND: Buildable: Prerequisites: ~disabled + YAK: + Buildable: + Prerequisites: ~disabled CA: Buildable: Prerequisites: ~disabled diff --git a/mods/ra/maps/intervention/mission.lua b/mods/ra/maps/intervention/mission.lua deleted file mode 100644 index d23a0531cbfe..000000000000 --- a/mods/ra/maps/intervention/mission.lua +++ /dev/null @@ -1,272 +0,0 @@ -difficulty = OpenRA.GetDifficulty() - -if difficulty == "Medium" then - BaseRaidInterval = Utils.Minutes(3) - BaseFrontAttackInterval = Utils.Minutes(3) + Utils.Seconds(30) - BaseRearAttackInterval = Utils.Minutes(8) - UBoatPatrolDelay = Utils.Minutes(2) + Utils.Seconds(30) - BaseFrontAttackWpts = { PatrolWpt1, BaseRaidWpt1 } -else - BaseRaidInterval = Utils.Minutes(2) + Utils.Seconds(30) - BaseFrontAttackInterval = Utils.Minutes(2) - BaseRearAttackInterval = Utils.Minutes(5) - UBoatPatrolDelay = Utils.Minutes(2) - BaseFrontAttackWpts = { PatrolWpt1 } -end - -Village = { FarmHouse1, FarmHouse2, FarmHouse3, FarmHouse4, FarmHouse5, FarmHouse6, FarmHouse7, FarmHouse8, FarmHouse9, Church } -VillageRaidInterval = Utils.Minutes(3) -VillageRaidAircraft = { "mig", "mig" } -VillageRaidWpts = { VillageRaidWpt1, VillageRaidWpt2 } - -BaseRaidAircraft = { "mig", "mig" } -BaseRaidWpts = { UboatPatrolWpt1, BaseRaidWpt2 } - -BaseFrontAttackUnits = { - { Barracks, {"e3", "e3", "e1", "e1", "e1"} }, - { WarFactory, {"3tnk", "3tnk", "apc"} } - } - -BaseRearAttackUnits = { - { Barracks, {"e3", "e3", "e1", "e1"} }, - { WarFactory, {"3tnk", "3tnk", "v2rl"} } - } -BaseRearAttackWpts = { GroundAttackWpt1, BaseRearAttackWpt1, BaseRearAttackWpt2, BaseRearAttackWpt3 } - -SovietHarvesters = { Harvester1, Harvester2, Harvester3 } -HarvesterGuard = { HarvGuard1, HarvGuard2, HarvGuard3 } - -UBoats = { Uboat1, Uboat2, Uboat3, Uboat4, Uboat5, Uboat6 } -UboatPatrolWpts1 = { UboatPatrolWpt1, UboatPatrolWpt2, UboatPatrolWpt3, UboatPatrolWpt4 } -UboatPatrolWpts2 = { UboatPatrolWpt4, UboatPatrolWpt2, UboatPatrolWpt1 } -UBoatPatrolUnits = { { SubPen, {"ss"} } } - -HunterSubs = { { SubPen, {"ss", "ss"} } } - -GroundPatrolWpts = { PatrolWpt1, PatrolWpt2 } -GroundPatrolUnits = { - { { Barracks, {"e1", "e1", "e1", "e3", "e3"} }, { Kennel, {"dog"} } }, - { { WarFactory, {"apc", "apc", "ftrk"} } }, - { { WarFactory, {"3tnk", "3tnk"} } } - } - -Reinforcements.ReinforceAir = function(owner, planeNames, entrypoint, rallypoint, interval, onCreateFunc) - local facing = { Map.GetFacing(CPos.op_Subtraction(rallypoint.Location, entrypoint.Location), 0), "Int32" } - local flight = { } - - for i, planeName in ipairs(planeNames) do - local enterPosition = WPos.op_Addition(entrypoint.CenterPosition, WVec.New(0, 0, Rules.InitialAltitude(planeName))) - local plane = Actor.Create(planeName, { AddToWorld = false, Location = entrypoint.Location, CenterPosition = enterPosition, Owner = owner, Facing = facing }) - flight[i] = plane - OpenRA.RunAfterDelay((i - 1) * interval, function() - World:Add(plane) - Actor.Fly(plane, rallypoint.CenterPosition) - if onCreateFunc ~= nil then - onCreateFunc(plane) - end - end) - end - return flight -end - -FollowWaypoints = function(team, waypoints) - Utils.Do(waypoints, function(wpt) - Team.Do(team, function(a) Actor.Fly(a, wpt.CenterPosition) end) - end) -end - -PlaneExitMap = function(actor, exitPoint) - Actor.Fly(actor, exitPoint.CenterPosition) - Actor.FlyOffMap(actor) - Actor.RemoveSelf(actor) -end - -BaseRaid = function() - local base = Map.FindStructuresInBox(player, AlliedAreaTopLeft, AlliedAreaBottomRight) - if #base == 0 then - return - end - - local target = base[OpenRA.GetRandomInteger(1, #base + 1)] - - local flight = Team.New(Reinforcements.ReinforceAir(soviets, BaseRaidAircraft, BaseRaidEntrypoint, BaseRaidWpts[1], Utils.Seconds(1))) - FollowWaypoints(flight, BaseRaidWpts) - - Team.Do(flight, function(plane) - Actor.FlyAttackActor(plane, target) - PlaneExitMap(plane, VillageRaidEntrypoint) - end) - - OpenRA.RunAfterDelay(BaseRaidInterval, BaseRaid) -end - -VillageRaid = function() - local target = nil - Utils.Do(Village, function(tgt) - if target == nil and not Actor.IsDead(tgt) then - target = tgt - return - end - end) - - if target == nil then - return - end - - local flight = Team.New(Reinforcements.ReinforceAir(soviets, VillageRaidAircraft, VillageRaidEntrypoint, VillageRaidWpts[1], Utils.Seconds(1))) - FollowWaypoints(flight, VillageRaidWpts) - - Team.Do(flight, function(plane) - Actor.FlyAttackActor(plane, target) - PlaneExitMap(plane, BaseRaidEntrypoint) - end) - - OpenRA.RunAfterDelay(VillageRaidInterval, VillageRaid) -end - -SendUboatPatrol = function(team) - OpenRA.RunAfterDelay(UBoatPatrolDelay, function() - if difficulty == "Medium" then - Team.Patrol(team, UboatPatrolWpts1, 0, false) - else - Team.Do(team, Actor.Hunt) - end - OpenRA.RunAfterDelay(Utils.Minutes(2), function() - Team.Do(team, Actor.Stop) - Team.Patrol(team, UboatPatrolWpts2) - end) - end) -end - -SendGroundPatrol = function(team) - Team.Patrol(team, GroundPatrolWpts, Utils.Seconds(3)) - Team.Do(team, function(actor) Actor.OnIdle(actor, Actor.Hunt) end) - - Team.AddEventHandler(team.OnAllKilled, function() - Production.BuildTeamFromTemplate(soviets, GroundPatrolUnits[OpenRA.GetRandomInteger(1, #GroundPatrolUnits + 1)], SendGroundPatrol) - end) -end - -BaseFrontAttack = function(team) - Team.Patrol(team, BaseFrontAttackWpts, 0, false) - Team.Do(team, function(actor) Actor.OnIdle(actor, Actor.Hunt) end) - OpenRA.RunAfterDelay(BaseFrontAttackInterval, function() Production.BuildTeamFromTemplate(soviets, BaseFrontAttackUnits, BaseFrontAttack) end) -end - -BaseRearAttack = function(team) - Team.Patrol(team, BaseRearAttackWpts, 0, false) - Team.Do(team, function(actor) Actor.OnIdle(actor, Actor.Hunt) end) - OpenRA.RunAfterDelay(BaseRearAttackInterval, function() Production.BuildTeamFromTemplate(soviets, BaseRearAttackUnits, BaseRearAttack) end) -end - -InsertMCV = function () - local mcv = Actor.Create("mcv", { Owner = player, Location = MCVInsertLocation.Location, Facing = Facing.North }) - Actor.Move(mcv, MCVDeployLocation.Location) - Actor.DeployTransform(mcv) -end - -SetupWorld = function() - if difficulty ~= "Medium" then - Actor.RemoveSelf(EasyMine) - end - - Utils.Do(SovietHarvesters, Actor.Harvest) - - harvesterGuard = Team.New(HarvesterGuard) - Utils.Do(SovietHarvesters, function(harvester) - Actor.OnDamaged(harvester, function(h) - Team.Do(harvesterGuard, function(g) - Actor.Stop(g) - Actor.AttackMove(g, h.Location, 3) - end) - end) - end) - - Utils.Do(UBoats, function(a) Actor.SetStance(a, "Defend") end) - - Utils.Do(Actor.ActorsWithTrait("RepairableBuilding"), function(building) - if Actor.Owner(building) == soviets then - Actor.OnDamaged(building, function(b) - if Actor.Owner(b) == soviets then - Actor.RepairBuilding(b) - end - end) - end - end) - --- Production.SetRallyPoint(WarFactory, Rallypoint) - Production.EventHandlers.Setup(soviets) - - -- RunAfterDelay is used so that the 'Building captured' and 'Mission accomplished' sounds don't play at the same time - Actor.OnCaptured(AirForceHQ, function() OpenRA.RunAfterDelay(Utils.Seconds(3), MissionAccomplished) end) - Actor.OnKilled(AirForceHQ, MissionFailed) - - village = Team.New(Village) - Team.AddEventHandler(village.OnAllKilled, MissionFailed) -end - -tick = 0 -alliedBaseEstablished = false -Tick = function() - tick = tick + 1 - - if OpenRA.GetOre(soviets) > (OpenRA.GetOreCapacity(soviets) * 0.75) then - Mission.TickTakeOre(soviets) - end - - if Mission.RequiredUnitsAreDestroyed(player) then - OpenRA.RunAfterDelay(Utils.Seconds(1), MissionFailed) - end - - if not alliedBaseEstablished and tick > Utils.Minutes(5) and tick % Utils.Seconds(10) == 0 then - -- FIXME: replace with cell trigger when available - local base = Map.FindStructuresInBox(player, AlliedAreaTopLeft, AlliedAreaBottomRight) - if #base > 0 then - alliedBaseEstablished = true - - OpenRA.RunAfterDelay(BaseFrontAttackInterval, function() - Production.BuildTeamFromTemplate(soviets, BaseFrontAttackUnits, BaseFrontAttack) - - local plane, paratroopers = SupportPowers.Paradrop(soviets, "badr", {"e1", "e1", "e1", "e3", "e3"}, BaseRaidEntrypoint.Location, MCVDeployLocation.Location) - Utils.Do(paratroopers, function(actor) Actor.OnIdle(actor, Actor.Hunt) end) - end) - - OpenRA.RunAfterDelay(BaseRearAttackInterval, function() - Production.BuildTeamFromTemplate(soviets, BaseRearAttackUnits, BaseRearAttack) - end) - - Production.BuildTeamFromTemplate(soviets, HunterSubs, function(team) - Team.Do(team, function(actor) Actor.OnIdle(actor, Actor.Hunt) end) - end) - - OpenRA.RunAfterDelay(BaseRaidInterval, BaseRaid) - end - end -end - -WorldLoaded = function() - player = OpenRA.GetPlayer("Allies") - soviets = OpenRA.GetPlayer("Soviets") - civvies = OpenRA.GetPlayer("Civilians") - - SetupWorld() - - OpenRA.RunAfterDelay(1, function() - Production.BuildTeamFromTemplate(soviets, UBoatPatrolUnits, SendUboatPatrol) - Production.BuildTeamFromTemplate(soviets, GroundPatrolUnits[OpenRA.GetRandomInteger(1, #GroundPatrolUnits + 1)], SendGroundPatrol) - end) - OpenRA.RunAfterDelay(VillageRaidInterval, VillageRaid) - - InsertMCV() - - OpenRA.SetViewportCenterPosition(Camera.CenterPosition) - OpenRA.RunAfterDelay(Utils.Seconds(5), function() Actor.RemoveSelf(Camera) end) -end - -MissionFailed = function() - Mission.MissionOver(nil, { player }, true) -end - -MissionAccomplished = function() - Mission.MissionOver({ player }, nil, true) -end