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