diff --git a/OpenRA.Game/Scripting/ScriptActorInterface.cs b/OpenRA.Game/Scripting/ScriptActorInterface.cs
index 047f903849..c750850fa8 100644
--- a/OpenRA.Game/Scripting/ScriptActorInterface.cs
+++ b/OpenRA.Game/Scripting/ScriptActorInterface.cs
@@ -25,10 +25,10 @@ namespace OpenRA.Scripting
{
this.actor = actor;
- var args = new [] { actor };
+ var args = new object[] { context, actor };
var objects = context.ActorCommands[actor.Info].Select(cg =>
{
- var groupCtor = cg.GetConstructor(new Type[] { typeof(Actor) });
+ var groupCtor = cg.GetConstructor(new Type[] { typeof(ScriptContext), typeof(Actor) });
return groupCtor.Invoke(args);
});
diff --git a/OpenRA.Game/Scripting/ScriptContext.cs b/OpenRA.Game/Scripting/ScriptContext.cs
index b947f2237f..d737adfb02 100644
--- a/OpenRA.Game/Scripting/ScriptContext.cs
+++ b/OpenRA.Game/Scripting/ScriptContext.cs
@@ -44,13 +44,23 @@ namespace OpenRA.Scripting
public abstract class ScriptActorProperties
{
protected readonly Actor self;
- public ScriptActorProperties(Actor self) { this.self = self; }
+ protected readonly ScriptContext context;
+ public ScriptActorProperties(ScriptContext context, Actor self)
+ {
+ this.self = self;
+ this.context = context;
+ }
}
public abstract class ScriptPlayerProperties
{
protected readonly Player player;
- public ScriptPlayerProperties(Player player) { this.player = player; }
+ protected readonly ScriptContext context;
+ public ScriptPlayerProperties(ScriptContext context, Player player)
+ {
+ this.player = player;
+ this.context = context;
+ }
}
// For global-level bindings
diff --git a/OpenRA.Game/Scripting/ScriptPlayerInterface.cs b/OpenRA.Game/Scripting/ScriptPlayerInterface.cs
index 4cc418ec12..8593b12262 100644
--- a/OpenRA.Game/Scripting/ScriptPlayerInterface.cs
+++ b/OpenRA.Game/Scripting/ScriptPlayerInterface.cs
@@ -25,10 +25,10 @@ namespace OpenRA.Scripting
{
this.player = player;
- var args = new [] { player };
+ var args = new object[] { context, player };
var objects = context.PlayerCommands.Select(cg =>
{
- var groupCtor = cg.GetConstructor(new Type[] { typeof(Player) });
+ var groupCtor = cg.GetConstructor(new Type[] { typeof(ScriptContext), typeof(Player) });
return groupCtor.Invoke(args);
});
diff --git a/OpenRA.Mods.RA/ConquestVictoryConditions.cs b/OpenRA.Mods.RA/ConquestVictoryConditions.cs
index 8e37a578ea..00c871ecaf 100644
--- a/OpenRA.Mods.RA/ConquestVictoryConditions.cs
+++ b/OpenRA.Mods.RA/ConquestVictoryConditions.cs
@@ -52,12 +52,6 @@ namespace OpenRA.Mods.RA
mo.MarkCompleted(self.Owner, objectiveID);
}
- public void ResolveOrder(Actor self, Order order)
- {
- if (order.OrderString == "Surrender")
- mo.MarkFailed(self.Owner, objectiveID);
- }
-
public void OnPlayerLost(Player player)
{
Game.Debug("{0} is defeated.".F(player.PlayerName));
diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index f5ff3d3d64..188bd7823e 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -566,6 +566,7 @@
+
@@ -611,4 +612,4 @@ copy "FuzzyLogicLibrary.dll" "$(SolutionDir)"
cd "$(SolutionDir)"
-
\ No newline at end of file
+
diff --git a/OpenRA.Mods.RA/Player/MissionObjectives.cs b/OpenRA.Mods.RA/Player/MissionObjectives.cs
index 8c907495eb..bdaf011718 100644
--- a/OpenRA.Mods.RA/Player/MissionObjectives.cs
+++ b/OpenRA.Mods.RA/Player/MissionObjectives.cs
@@ -79,8 +79,8 @@ namespace OpenRA.Mods.RA
objectives.Insert(newID, new MissionObjective(type, description));
ObjectiveAdded(player);
- foreach (var imo in player.PlayerActor.TraitsImplementing())
- imo.OnObjectiveAdded(player, newID);
+ foreach (var inou in player.PlayerActor.TraitsImplementing())
+ inou.OnObjectiveAdded(player, newID);
return newID;
}
@@ -90,22 +90,23 @@ namespace OpenRA.Mods.RA
if (objectiveID >= objectives.Count || objectives[objectiveID].State == ObjectiveState.Completed)
return;
+ var inous = player.PlayerActor.TraitsImplementing();
+
objectives[objectiveID].State = ObjectiveState.Completed;
+ foreach (var inou in inous)
+ inou.OnObjectiveCompleted(player, objectiveID);
if (objectives[objectiveID].Type == ObjectiveType.Primary)
{
var playerWon = objectives.Where(o => o.Type == ObjectiveType.Primary).All(o => o.State == ObjectiveState.Completed);
- foreach (var imo in player.PlayerActor.TraitsImplementing())
- {
- imo.OnObjectiveCompleted(player, objectiveID);
-
- if (playerWon)
- imo.OnPlayerWon(player);
- }
-
if (playerWon)
+ {
+ foreach (var inou in inous)
+ inou.OnPlayerWon(player);
+
CheckIfGameIsOver(player);
+ }
}
}
@@ -114,89 +115,118 @@ namespace OpenRA.Mods.RA
if (objectiveID >= objectives.Count || objectives[objectiveID].State == ObjectiveState.Failed)
return;
+ var inous = player.PlayerActor.TraitsImplementing();
+
objectives[objectiveID].State = ObjectiveState.Failed;
+ foreach (var inou in inous)
+ inou.OnObjectiveFailed(player, objectiveID);
if (objectives[objectiveID].Type == ObjectiveType.Primary)
{
var playerLost = objectives.Where(o => o.Type == ObjectiveType.Primary).Any(o => o.State == ObjectiveState.Failed);
- foreach (var imo in player.PlayerActor.TraitsImplementing())
- {
- imo.OnObjectiveFailed(player, objectiveID);
-
- if (playerLost)
- imo.OnPlayerLost(player);
- }
-
if (playerLost)
+ {
+ foreach (var inou in inous)
+ inou.OnPlayerLost(player);
+
CheckIfGameIsOver(player);
+ }
}
}
void CheckIfGameIsOver(Player player)
{
var players = player.World.Players.Where(p => !p.NonCombatant);
- var allies = players.Where(p => p.IsAlliedWith(player));
-
- var gameOver = ((info.EarlyGameOver && !info.Cooperative && player.WinState != WinState.Undefined) ||
- (info.EarlyGameOver && info.Cooperative && allies.All(p => p.WinState != WinState.Undefined)) ||
- players.All(p => p.WinState != WinState.Undefined));
+ var gameOver = players.All(p => p.WinState != WinState.Undefined);
if (gameOver)
- {
Game.RunAfterDelay(info.GameOverDelay, () =>
{
player.World.EndGame();
player.World.SetPauseState(true);
player.World.PauseStateLocked = true;
});
- }
}
public void OnPlayerWon(Player player)
{
+ var players = player.World.Players.Where(p => !p.NonCombatant);
+ var enemies = players.Where(p => !p.IsAlliedWith(player));
+
if (info.Cooperative)
{
WinStateCooperative = WinState.Won;
- var players = player.World.Players.Where(p => !p.NonCombatant);
var allies = players.Where(p => p.IsAlliedWith(player));
if (allies.All(p => p.PlayerActor.Trait().WinStateCooperative == WinState.Won))
+ {
foreach (var p in allies)
{
p.WinState = WinState.Won;
p.World.OnPlayerWinStateChanged(p);
}
+
+ if (info.EarlyGameOver)
+ foreach (var p in enemies)
+ p.PlayerActor.Trait().ForceDefeat(p);
+ }
}
else
{
player.WinState = WinState.Won;
player.World.OnPlayerWinStateChanged(player);
+
+ if (info.EarlyGameOver)
+ foreach (var p in enemies)
+ p.PlayerActor.Trait().ForceDefeat(p);
}
}
public void OnPlayerLost(Player player)
{
+ var players = player.World.Players.Where(p => !p.NonCombatant);
+ var enemies = players.Where(p => !p.IsAlliedWith(player));
+
if (info.Cooperative)
{
WinStateCooperative = WinState.Lost;
- var players = player.World.Players.Where(p => !p.NonCombatant);
var allies = players.Where(p => p.IsAlliedWith(player));
if (allies.Any(p => p.PlayerActor.Trait().WinStateCooperative == WinState.Lost))
+ {
foreach (var p in allies)
{
p.WinState = WinState.Lost;
p.World.OnPlayerWinStateChanged(p);
}
+
+ if (info.EarlyGameOver)
+ foreach (var p in enemies)
+ p.PlayerActor.Trait().ForceDefeat(p);
+ }
}
else
{
player.WinState = WinState.Lost;
player.World.OnPlayerWinStateChanged(player);
+
+ if (info.EarlyGameOver)
+ foreach (var p in enemies)
+ {
+ p.WinState = WinState.Won;
+ p.World.OnPlayerWinStateChanged(p);
+ }
}
}
+ public void ForceDefeat(Player player)
+ {
+ for (var id = 0; id < Objectives.Count; id++)
+ if (Objectives[id].State == ObjectiveState.Incomplete)
+ MarkFailed(player, id);
+ }
+
public event Action ObjectiveAdded = player => { };
public void OnObjectiveAdded(Player player, int id) {}
@@ -206,8 +236,7 @@ namespace OpenRA.Mods.RA
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Surrender")
- for (var id = 0; id < objectives.Count; id++)
- MarkFailed(self.Owner, id);
+ ForceDefeat(self.Owner);
}
}
diff --git a/OpenRA.Mods.RA/Scripting/Global/CoordinateGlobals.cs b/OpenRA.Mods.RA/Scripting/Global/CoordinateGlobals.cs
index dcc1763c22..f4c1775456 100644
--- a/OpenRA.Mods.RA/Scripting/Global/CoordinateGlobals.cs
+++ b/OpenRA.Mods.RA/Scripting/Global/CoordinateGlobals.cs
@@ -65,5 +65,8 @@ namespace OpenRA.Scripting
[Desc("Create a new WRange.")]
public WRange New(int r) { return new WRange(r); }
+
+ [Desc("Create a new WRange by cell distance")]
+ public WRange FromCells(int numCells) { return WRange.FromCells(numCells); }
}
}
diff --git a/OpenRA.Mods.RA/Scripting/Global/MapGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/MapGlobal.cs
index 34c61859f0..d7d45366f7 100644
--- a/OpenRA.Mods.RA/Scripting/Global/MapGlobal.cs
+++ b/OpenRA.Mods.RA/Scripting/Global/MapGlobal.cs
@@ -8,6 +8,7 @@
*/
#endregion
+using System;
using System.Linq;
using Eluant;
using OpenRA.Scripting;
@@ -43,6 +44,34 @@ namespace OpenRA.Mods.RA.Scripting
return actors.ToLuaTable(context);
}
+ [Desc("Returns a table of all actors within the requested rectangle, filtered using the specified function.")]
+ public LuaTable ActorsInBox(WPos topLeft, WPos bottomRight, LuaFunction filter = null)
+ {
+ var actors = context.World.ActorMap.ActorsInBox(topLeft, bottomRight)
+ .Select(a => a.ToLuaValue(context));
+
+ if (filter != null)
+ actors = actors.Where(a =>
+ {
+ using (var f = filter.Call(a))
+ return f.First().ToBoolean();
+ });
+
+ return actors.ToLuaTable(context);
+ }
+
+ [Desc("Returns the location of the top-left corner of the map.")]
+ public WPos TopLeft
+ {
+ get { return new WPos(context.World.Map.Bounds.Left * 1024, context.World.Map.Bounds.Top * 1024, 0); }
+ }
+
+ [Desc("Returns the location of the bottom-right corner of the map.")]
+ public WPos BottomRight
+ {
+ get { return new WPos(context.World.Map.Bounds.Right * 1024, context.World.Map.Bounds.Bottom * 1024, 0); }
+ }
+
[Desc("Returns a random cell inside the visible region of the map.")]
public CPos RandomCell()
{
diff --git a/OpenRA.Mods.RA/Scripting/Global/ReinforcementsGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/ReinforcementsGlobal.cs
new file mode 100644
index 0000000000..c21b66a007
--- /dev/null
+++ b/OpenRA.Mods.RA/Scripting/Global/ReinforcementsGlobal.cs
@@ -0,0 +1,220 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation. For more information,
+ * see COPYING.
+ */
+#endregion
+
+using Eluant;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using OpenRA;
+using OpenRA.Primitives;
+using OpenRA.Traits;
+using OpenRA.Scripting;
+using OpenRA.Effects;
+using OpenRA.Mods.RA.Activities;
+using OpenRA.Mods.RA.Air;
+
+namespace OpenRA.Mods.RA.Scripting
+{
+ [ScriptGlobal("Reinforcements")]
+ public class ReinforcementsGlobal : ScriptGlobal
+ {
+ public ReinforcementsGlobal(ScriptContext context) : base(context) { }
+
+ Actor CreateActor(Player owner, string actorType, bool addToWorld, CPos? entryLocation = null, CPos? nextLocation = null)
+ {
+ ActorInfo ai;
+ if (!context.World.Map.Rules.Actors.TryGetValue(actorType, out ai))
+ throw new LuaException("Unknown actor type '{0}'".F(actorType));
+
+ var initDict = new TypeDictionary();
+
+ initDict.Add(new OwnerInit(owner));
+
+ if (entryLocation.HasValue)
+ {
+ var pi = ai.Traits.GetOrDefault();
+ initDict.Add(new CenterPositionInit(owner.World.Map.CenterOfCell(entryLocation.Value) + new WVec(0, 0, pi != null ? pi.CruiseAltitude.Range : 0)));
+ initDict.Add(new LocationInit(entryLocation.Value));
+ }
+
+ if (entryLocation.HasValue && nextLocation.HasValue)
+ initDict.Add(new FacingInit(context.World.Map.FacingBetween(CPos.Zero, CPos.Zero + (nextLocation.Value - entryLocation.Value), 0)));
+
+ var actor = context.World.CreateActor(addToWorld, actorType, initDict);
+
+ return actor;
+ }
+
+ void Move(Actor actor, CPos dest)
+ {
+ if (actor.HasTrait())
+ {
+ if (actor.HasTrait())
+ actor.QueueActivity(new HeliFly(actor, Target.FromCell(actor.World, dest)));
+ else
+ actor.QueueActivity(new Fly(actor, Target.FromCell(actor.World, dest)));
+ }
+ else
+ {
+ actor.QueueActivity(new Move.Move(dest, 2));
+ }
+ }
+
+ [Desc("Send reinforcements consisting of multiple units. Supports ground-based, naval and air units. " +
+ "The first member of the 'entryPath' array will be the units' spawnpoint, " +
+ "while the last one will be their destination. If 'actionFunc' is given, " +
+ "it will be executed once a unit has reached its destination. 'actionFunc' " +
+ "will be called as 'actionFunc(Actor actor)'")]
+ public LuaTable Reinforce(Player owner, LuaTable actorTypes, LuaTable entryPath, int interval = 25, LuaFunction actionFunc = null)
+ {
+ var actors = new List();
+ for (var i = 1; i <= actorTypes.Count; i++)
+ {
+ string actorType;
+ if (!(actorTypes[i].TryGetClrValue(out actorType)))
+ throw new LuaException("Invalid data in actorTypes array");
+
+ CPos entry, next = new CPos();
+ if (!(entryPath[1].TryGetClrValue(out entry)
+ && (entryPath.Count < 2 || entryPath[2].TryGetClrValue(out next))))
+ throw new LuaException("Invalid data in entryPath array");
+
+ var actor = CreateActor(owner, actorType, false, entry, entryPath.Count > 1 ? next : (CPos?)null);
+ actors.Add(actor);
+
+ var ep = entryPath.CopyReference() as LuaTable;
+ var af = actionFunc != null ? actionFunc.CopyReference() as LuaFunction : null;
+
+ var actionDelay = (i - 1) * interval;
+ Action actorAction = () =>
+ {
+ context.World.Add(actor);
+ for (var j = 2; j <= ep.Count; j++)
+ {
+ CPos wpt;
+ if (!(ep[j].TryGetClrValue(out wpt)))
+ throw new LuaException("Invalid data in entryPath array");
+
+ Move(actor, wpt);
+ }
+ ep.Dispose();
+
+ if (af != null)
+ actor.QueueActivity(new CallFunc(() =>
+ {
+ af.Call(actor.ToLuaValue(context));
+ af.Dispose();
+ }));
+ };
+
+ context.World.AddFrameEndTask(w => w.Add(new DelayedAction(actionDelay, actorAction)));
+ }
+ return actors.Select(a => a.ToLuaValue(context)).ToLuaTable(context);
+ }
+
+ [Desc("Send reinforcements in a transport. A transport can be a ground unit (APC etc.), ships and aircraft. " +
+ "The first member of the 'entryPath' array will be the spawnpoint for the transport, " +
+ "while the last one will be its destination. The last member of the 'exitPath' array " +
+ "is be the place where the transport will be removed from the game. When the transport " +
+ "has reached the destination, it will unload its cargo unless a custom 'actionFunc' has " +
+ "been supplied. Afterwards, the transport will follow the 'exitPath' and leave the map, " +
+ "unless a custom 'exitFunc' has been supplied. 'actionFunc' will be called as " +
+ "'actionFunc(Actor transport, Actor[] cargo). 'exitFunc' will be called as 'exitFunc(Actor transport)'.")]
+ public LuaTable ReinforceWithTransport(Player owner, string actorType, LuaTable cargoTypes, LuaTable entryPath, LuaTable exitPath = null,
+ LuaFunction actionFunc = null, LuaFunction exitFunc = null)
+ {
+ CPos entry, next = new CPos();
+ if (!(entryPath[1].TryGetClrValue(out entry)
+ && (entryPath.Count < 2 || entryPath[2].TryGetClrValue(out next))))
+ throw new LuaException("Invalid data in entryPath array");
+
+ var transport = CreateActor(owner, actorType, true, entry, entryPath.Count > 1 ? next : (CPos?)null);
+ var cargo = transport.TraitOrDefault();
+
+ var passengers = context.CreateTable();
+
+ if (cargo != null && cargoTypes != null)
+ {
+ for (var i = 1; i <= cargoTypes.Count; i++)
+ {
+ string cargoType;
+ if (!(cargoTypes [i].TryGetClrValue(out cargoType)))
+ throw new LuaException("Invalid data in cargoTypes array");
+
+ var passenger = CreateActor(owner, cargoType, false);
+ passengers.Add(passengers.Count + 1, passenger.ToLuaValue(context));
+ cargo.Load(transport, passenger);
+ }
+ }
+
+ for (var i = 2; i <= entryPath.Count; i++)
+ {
+ CPos wpt;
+ if (!(entryPath[i].TryGetClrValue(out wpt)))
+ throw new LuaException("Invalid data in entryPath array");
+
+ Move(transport, wpt);
+ }
+
+ if (actionFunc != null)
+ {
+ var af = actionFunc.CopyReference() as LuaFunction;
+ transport.QueueActivity(new CallFunc(() =>
+ {
+ af.Call(transport.ToLuaValue(context), passengers);
+ af.Dispose();
+ }));
+ }
+ else
+ {
+ var heli = transport.TraitOrDefault();
+ if (heli != null)
+ {
+ transport.QueueActivity(new Turn(heli.Info.InitialFacing));
+ transport.QueueActivity(new HeliLand(true));
+ transport.QueueActivity(new Wait(15));
+ }
+ if (cargo != null)
+ {
+ transport.QueueActivity(new UnloadCargo(transport, true));
+ transport.QueueActivity(new WaitFor(() => cargo.IsEmpty(transport)));
+ }
+ transport.QueueActivity(new Wait(heli != null ? 50 : 25));
+ }
+
+ if (exitFunc != null)
+ {
+ var ef = exitFunc.CopyReference() as LuaFunction;
+ transport.QueueActivity(new CallFunc(() =>
+ {
+ ef.Call(transport.ToLuaValue(context));
+ ef.Dispose();
+ }));
+ }
+ else if (exitPath != null)
+ {
+ for (var i = 1; i <= exitPath.Count; i++)
+ {
+ CPos wpt;
+ if (!(exitPath[i].TryGetClrValue(out wpt)))
+ throw new LuaException("Invalid data in exitPath array.");
+
+ Move(transport, wpt);
+ }
+ transport.QueueActivity(new RemoveSelf());
+ }
+
+ var ret = context.CreateTable();
+ ret.Add(1, transport.ToLuaValue(context));
+ ret.Add(2, passengers);
+ return ret;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs
index a2dca58344..4fd96055d1 100644
--- a/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs
+++ b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs
@@ -144,6 +144,27 @@ namespace OpenRA.Mods.RA.Scripting
GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnObjectiveFailed, func, context);
}
+ [Desc("Call a function when this actor is added to the world. " +
+ "The callback function will be called as func(Actor self).")]
+ public void OnAddedToWorld(Actor a, LuaFunction func)
+ {
+ GetScriptTriggers(a).RegisterCallback(Trigger.OnAddedToWorld, func, context);
+ }
+
+ [Desc("Call a function when this actor is removed from the world. " +
+ "The callback function will be called as func(Actor self).")]
+ public void OnRemovedFromWorld(Actor a, LuaFunction func)
+ {
+ GetScriptTriggers(a).RegisterCallback(Trigger.OnRemovedFromWorld, func, context);
+ }
+
+ [Desc("Call a function when this actor is captured. The callback function " +
+ "will be called as func(Actor self, Actor captor, Player oldOwner, Player newOwner).")]
+ public void OnCapture(Actor a, LuaFunction func)
+ {
+ GetScriptTriggers(a).RegisterCallback(Trigger.OnCapture, func, context);
+ }
+
[Desc("Removes all triggers from this actor")]
public void ClearAll(Actor a)
{
diff --git a/OpenRA.Mods.RA/Scripting/Global/UtilsGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/UtilsGlobal.cs
index cb330e43cb..95b9c089e3 100644
--- a/OpenRA.Mods.RA/Scripting/Global/UtilsGlobal.cs
+++ b/OpenRA.Mods.RA/Scripting/Global/UtilsGlobal.cs
@@ -58,6 +58,17 @@ namespace OpenRA.Mods.RA.Scripting
return true;
}
+ [Desc("Skips over the first numElements members of the array and returns the rest")]
+ public LuaTable Skip(LuaTable table, int numElements)
+ {
+ var t = context.CreateTable();
+
+ for (var i = numElements; i <= table.Count; i++)
+ t.Add(t.Count + 1, table[i]);
+
+ return t;
+ }
+
[Desc("Returns a random value from table.")]
public LuaValue Random(LuaTable table)
{
@@ -94,5 +105,17 @@ namespace OpenRA.Mods.RA.Scripting
{
return context.World.Map.CenterOfCell(cell);
}
+
+ [Desc("Converts the number of seconds into game time (ticks).")]
+ public int Seconds(int seconds)
+ {
+ return seconds * 25;
+ }
+
+ [Desc("Converts the number of minutes into game time (ticks).")]
+ public int Minutes(int minutes)
+ {
+ return Seconds(minutes * 60);
+ }
}
}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs
index f0257dd320..66b46aa92e 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs
@@ -17,8 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
[ScriptPropertyGroup("Support Powers")]
public class ChronsphereProperties : ScriptActorProperties, Requires
{
- public ChronsphereProperties(Actor self)
- : base(self) { }
+ public ChronsphereProperties(ScriptContext context, Actor self)
+ : base(context, self) { }
[Desc("Chronoshift a group of actors. A duration of 0 will teleport the actors permanently.")]
public void Chronoshift(LuaTable unitLocationPairs, int duration = 0, bool killCargo = false)
diff --git a/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs
index 46a678bb88..20b9fad366 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs
@@ -17,7 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
[ScriptPropertyGroup("Combat")]
public class CombatProperties : ScriptActorProperties, Requires, Requires
{
- public CombatProperties(Actor self) : base(self) { }
+ public CombatProperties(ScriptContext context, Actor self)
+ : base(context, self) { }
[ScriptActorPropertyActivity]
[Desc("Seek out and attack nearby targets.")]
@@ -35,4 +36,4 @@ namespace OpenRA.Mods.RA.Scripting
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Move.Move(cell, WRange.FromCells(closeEnough))));
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/GeneralProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/GeneralProperties.cs
index 0ab242684b..ca49084cc2 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/GeneralProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/GeneralProperties.cs
@@ -21,7 +21,8 @@ namespace OpenRA.Mods.RA.Scripting
readonly IFacing facing;
readonly AutoTarget autotarget;
- public GeneralProperties(Actor self) : base(self)
+ public GeneralProperties(ScriptContext context, Actor self)
+ : base(context, self)
{
facing = self.TraitOrDefault();
autotarget = self.TraitOrDefault();
@@ -135,4 +136,4 @@ namespace OpenRA.Mods.RA.Scripting
return self.HasScriptProperty(name);
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/GuardProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/GuardProperties.cs
index 30f6c8b6dd..fd1e744b98 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/GuardProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/GuardProperties.cs
@@ -17,8 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
public class GuardProperties : ScriptActorProperties, Requires, Requires
{
Guard guard;
- public GuardProperties(Actor self)
- : base(self)
+ public GuardProperties(ScriptContext context, Actor self)
+ : base(context, self)
{
guard = self.Trait();
}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs
index 27620af598..fc80b5762e 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs
@@ -17,8 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
public class HealthProperties : ScriptActorProperties, Requires
{
Health health;
- public HealthProperties(Actor self)
- : base(self)
+ public HealthProperties(ScriptContext context, Actor self)
+ : base(context, self)
{
health = self.Trait();
}
@@ -41,8 +41,8 @@ namespace OpenRA.Mods.RA.Scripting
public class InvulnerableProperties : ScriptActorProperties, Requires
{
ScriptInvulnerable invulnerable;
- public InvulnerableProperties(Actor self)
- : base(self)
+ public InvulnerableProperties(ScriptContext context, Actor self)
+ : base(context, self)
{
invulnerable = self.Trait();
}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/MissionObjectiveProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/MissionObjectiveProperties.cs
index b11a6b6af4..89a14e9b14 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/MissionObjectiveProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/MissionObjectiveProperties.cs
@@ -20,8 +20,8 @@ namespace OpenRA.Mods.RA.Scripting
{
readonly MissionObjectives mo;
- public MissionObjectiveProperties(Player player)
- : base(player)
+ public MissionObjectiveProperties(ScriptContext context, Player player)
+ : base(context, player)
{
mo = player.PlayerActor.Trait();
}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/MobileProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/MobileProperties.cs
index fd990dec55..511aad91ff 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/MobileProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/MobileProperties.cs
@@ -17,7 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
[ScriptPropertyGroup("Movement")]
public class MobileProperties : ScriptActorProperties, Requires
{
- public MobileProperties(Actor self) : base(self) { }
+ public MobileProperties(ScriptContext context, Actor self)
+ : base(context, self) { }
[ScriptActorPropertyActivity]
[Desc("Moves within the cell grid. closeEnough defines an optional range " +
diff --git a/OpenRA.Mods.RA/Scripting/Properties/PlayerProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/PlayerProperties.cs
index 538b611c1f..ee1e52a064 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/PlayerProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/PlayerProperties.cs
@@ -9,22 +9,28 @@
#endregion
using System;
+using System.Linq;
+using Eluant;
using OpenRA.Scripting;
+using OpenRA.Mods.RA.Move;
namespace OpenRA.Mods.RA.Scripting
{
[ScriptPropertyGroup("Player")]
public class PlayerProperties : ScriptPlayerProperties
{
- readonly Player p;
-
- public PlayerProperties(Player player)
- : base(player)
- {
- p = player;
- }
+ public PlayerProperties(ScriptContext context, Player player)
+ : base(context, player) { }
[Desc("The player's name.")]
- public string PlayerName { get { return p.PlayerName; } }
+ public string Name { get { return player.PlayerName; } }
+
+ [Desc("Returns an array of actors representing all ground attack units of this player.")]
+ public LuaTable GetGroundAttackers()
+ {
+ return player.World.ActorsWithTrait().Select(a => a.Actor)
+ .Where(a => a.Owner == player && !a.IsDead() && a.IsInWorld && a.HasTrait())
+ .Select(a => a.ToLuaValue(context)).ToLuaTable(context);
+ }
}
}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs
index d51c43bdd8..6b8596b6a1 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs
@@ -20,8 +20,8 @@ namespace OpenRA.Mods.RA.Scripting
{
readonly Production p;
- public ProductionProperties(Actor self)
- : base(self)
+ public ProductionProperties(ScriptContext context, Actor self)
+ : base(context, self)
{
p = self.Trait();
}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/ResourceProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ResourceProperties.cs
index bfe54bda15..377d0bce17 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/ResourceProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/ResourceProperties.cs
@@ -19,8 +19,8 @@ namespace OpenRA.Mods.RA.Scripting
{
readonly PlayerResources pr;
- public ResourceProperties(Player player)
- : base(player)
+ public ResourceProperties(ScriptContext context, Player player)
+ : base(context, player)
{
pr = player.PlayerActor.Trait();
}
diff --git a/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs
index 58d87dc5bc..ff826ed717 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs
@@ -21,8 +21,8 @@ namespace OpenRA.Mods.RA.Scripting
{
readonly Cargo cargo;
- public TransportProperties(Actor self)
- : base(self)
+ public TransportProperties(ScriptContext context, Actor self)
+ : base(context, self)
{
cargo = self.Trait();
}
@@ -46,8 +46,8 @@ namespace OpenRA.Mods.RA.Scripting
{
readonly ParaDrop paradrop;
- public ParadropPowers(Actor self)
- : base(self)
+ public ParadropPowers(ScriptContext context, Actor self)
+ : base(context, self)
{
paradrop = self.Trait();
}
diff --git a/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs
index 9c2a8e597b..c507a09616 100644
--- a/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs
+++ b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs
@@ -18,12 +18,13 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA.Scripting
{
- public enum Trigger { OnIdle, OnDamaged, OnKilled, OnProduction, OnPlayerWon, OnPlayerLost, OnObjectiveAdded, OnObjectiveCompleted, OnObjectiveFailed };
+ public enum Trigger { OnIdle, OnDamaged, OnKilled, OnProduction, 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 sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyObjectivesUpdated, IDisposable
+ public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyObjectivesUpdated, INotifyCapture, INotifyAddedToWorld, INotifyRemovedFromWorld, IDisposable
{
public event Action OnKilledInternal = _ => {};
@@ -43,36 +44,25 @@ namespace OpenRA.Mods.RA.Scripting
public void TickIdle(Actor self)
{
foreach (var f in Triggers[Trigger.OnIdle])
- {
- var a = self.ToLuaValue(f.Second);
- f.First.Call(a).Dispose();
- a.Dispose();
- }
+ using (var a = self.ToLuaValue(f.Second))
+ f.First.Call(a).Dispose();
}
public void Damaged(Actor self, AttackInfo e)
{
foreach (var f in Triggers[Trigger.OnDamaged])
- {
- var a = self.ToLuaValue(f.Second);
- var b = e.Attacker.ToLuaValue(f.Second);
- f.First.Call(a, b).Dispose();
- a.Dispose();
- b.Dispose();
- }
+ using (var a = self.ToLuaValue(f.Second))
+ using (var b = e.Attacker.ToLuaValue(f.Second))
+ f.First.Call(a, b).Dispose();
}
public void Killed(Actor self, AttackInfo e)
{
// Run lua callbacks
foreach (var f in Triggers[Trigger.OnKilled])
- {
- var a = self.ToLuaValue(f.Second);
- var b = e.Attacker.ToLuaValue(f.Second);
- f.First.Call(a, b).Dispose();
- a.Dispose();
- b.Dispose();
- }
+ using (var a = self.ToLuaValue(f.Second))
+ using (var b = e.Attacker.ToLuaValue(f.Second))
+ f.First.Call(a, b).Dispose();
// Run any internally bound callbacks
OnKilledInternal(self);
@@ -81,69 +71,71 @@ namespace OpenRA.Mods.RA.Scripting
public void UnitProduced(Actor self, Actor other, CPos exit)
{
foreach (var f in Triggers[Trigger.OnProduction])
- {
- var a = self.ToLuaValue(f.Second);
- var b = other.ToLuaValue(f.Second);
- f.First.Call(a, b).Dispose();
- a.Dispose();
- b.Dispose();
- }
+ using (var a = self.ToLuaValue(f.Second))
+ using (var b = other.ToLuaValue(f.Second))
+ f.First.Call(a, b).Dispose();
}
public void OnPlayerWon(Player player)
{
foreach (var f in Triggers[Trigger.OnPlayerWon])
- {
- var a = player.ToLuaValue(f.Second);
- f.First.Call(a).Dispose();
- a.Dispose();
- }
+ using (var a = player.ToLuaValue(f.Second))
+ f.First.Call(a).Dispose();
}
public void OnPlayerLost(Player player)
{
foreach (var f in Triggers[Trigger.OnPlayerLost])
- {
- var a = player.ToLuaValue(f.Second);
- f.First.Call(a).Dispose();
- a.Dispose();
- }
+ using (var a = player.ToLuaValue(f.Second))
+ f.First.Call(a).Dispose();
}
public void OnObjectiveAdded(Player player, int id)
{
foreach (var f in Triggers[Trigger.OnObjectiveAdded])
- {
- var a = player.ToLuaValue(f.Second);
- var b = id.ToLuaValue(f.Second);
- f.First.Call(a, b).Dispose();
- a.Dispose();
- b.Dispose();
- }
+ using (var a = player.ToLuaValue(f.Second))
+ using (var b = id.ToLuaValue(f.Second))
+ f.First.Call(a, b).Dispose();
}
public void OnObjectiveCompleted(Player player, int id)
{
foreach (var f in Triggers[Trigger.OnObjectiveCompleted])
- {
- var a = player.ToLuaValue(f.Second);
- var b = id.ToLuaValue(f.Second);
- f.First.Call(a, b).Dispose();
- a.Dispose();
- b.Dispose();
- }
+ using (var a = player.ToLuaValue(f.Second))
+ using (var b = id.ToLuaValue(f.Second))
+ f.First.Call(a, b).Dispose();
}
public void OnObjectiveFailed(Player player, int id)
{
foreach (var f in Triggers[Trigger.OnObjectiveFailed])
- {
- var a = player.ToLuaValue(f.Second);
- var b = id.ToLuaValue(f.Second);
- f.First.Call(a, b).Dispose();
- a.Dispose();
- b.Dispose();
- }
+ using (var a = player.ToLuaValue(f.Second))
+ using (var b = id.ToLuaValue(f.Second))
+ f.First.Call(a, b).Dispose();
+ }
+
+ public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
+ {
+ foreach (var f in Triggers[Trigger.OnCapture])
+ using (var a = self.ToLuaValue(f.Second))
+ using (var b = captor.ToLuaValue(f.Second))
+ using (var c = oldOwner.ToLuaValue(f.Second))
+ using (var d = newOwner.ToLuaValue(f.Second))
+ f.First.Call(a, b, c, d).Dispose();
+ }
+
+ public void AddedToWorld(Actor self)
+ {
+ foreach (var f in Triggers[Trigger.OnAddedToWorld])
+ using (var a = self.ToLuaValue(f.Second))
+ f.First.Call(a).Dispose();
+ }
+
+ public void RemovedFromWorld(Actor self)
+ {
+ foreach (var f in Triggers[Trigger.OnRemovedFromWorld])
+ using (var a = self.ToLuaValue(f.Second))
+ f.First.Call(a).Dispose();
}
public void Clear(Trigger trigger)
diff --git a/mods/cnc/maps/gdi01/gdi01.lua b/mods/cnc/maps/gdi01/gdi01.lua
index b54c5f8e98..3c08f00494 100644
--- a/mods/cnc/maps/gdi01/gdi01.lua
+++ b/mods/cnc/maps/gdi01/gdi01.lua
@@ -3,9 +3,7 @@ VehicleReinforcements = { "jeep" }
NodPatrol = { "e1", "e1" }
SendNodPatrol = function()
- Utils.Do(NodPatrol, function(type)
- local soldier = Actor.Create(type, true, { Location = nod0.Location, Owner = enemy })
- soldier.Move(nod1.Location)
+ Reinforcements.Reinforce(enemy, NodPatrol, { nod0.Location, nod1.Location }, 15, function(soldier)
soldier.AttackMove(nod2.Location)
soldier.Move(nod3.Location)
soldier.Hunt()
@@ -17,21 +15,27 @@ SetGunboatPath = function(gunboat)
gunboat.AttackMove(gunboatRight.Location)
end
-ReinforceFromSea = function(passengers)
- local transport = Actor.Create("oldlst", true, { Location = lstStart.Location, Owner = player })
+Reinforce = function(units)
+ Media.PlaySpeechNotification(player, "Reinforce")
+ Reinforcements.ReinforceWithTransport(player, "oldlst", units, { lstStart.Location, lstEnd.Location }, { lstStart.Location })
+end
- Utils.Do(passengers, function(actorType)
- local passenger = Actor.Create(actorType, false, { Owner = player })
- transport.LoadPassenger(passenger)
+triggerAdded = false
+CheckForBase = function()
+ baseBuildings = Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(actor)
+ return actor.Type == "fact" or actor.Type == "pyle" or actor.Type == "nuke"
end)
- transport.Move(lstEnd.Location)
- transport.UnloadPassengers()
- transport.Wait(50)
- transport.Move(lstStart.Location)
- transport.Destroy()
+ Utils.Do(baseBuildings, function(building)
+ if not triggerAdded and building.Type == "fact" then
+ Trigger.OnRemovedFromWorld(building, function()
+ player.MarkFailedObjective(gdiObjective2)
+ end)
+ triggerAdded = true
+ end
+ end)
- Media.PlaySpeechNotification(player, "Reinforce")
+ return #baseBuildings >= 3
end
WorldLoaded = function()
@@ -40,7 +44,12 @@ WorldLoaded = function()
player = Player.GetPlayer("GDI")
enemy = Player.GetPlayer("Nod")
- gdiObjective = player.AddPrimaryObjective("Destroy all Nod forces in the area!")
+ nodObjective = enemy.AddPrimaryObjective("Destroy all GDI troops")
+ gdiObjective1 = player.AddPrimaryObjective("Eliminate all Nod forces in the area")
+ gdiObjective2 = player.AddSecondaryObjective("Establish a beachhead")
+
+ Trigger.OnObjectiveCompleted(player, function() Media.DisplayMessage("Objective completed") end)
+ Trigger.OnObjectiveFailed(player, function() Media.DisplayMessage("Objective failed") end)
Trigger.OnPlayerWon(player, function()
Media.PlaySpeechNotification(player, "Win")
@@ -60,18 +69,26 @@ WorldLoaded = function()
SendNodPatrol()
- Trigger.AfterDelay(25 * 5, function() ReinforceFromSea(InfantryReinforcements) end)
- Trigger.AfterDelay(25 * 15, function() ReinforceFromSea(InfantryReinforcements) end)
- Trigger.AfterDelay(25 * 30, function() ReinforceFromSea(VehicleReinforcements) end)
- Trigger.AfterDelay(25 * 60, function() ReinforceFromSea(VehicleReinforcements) end)
+ Trigger.AfterDelay(Utils.Seconds(5), function() Reinforce(InfantryReinforcements) end)
+ Trigger.AfterDelay(Utils.Seconds(15), function() Reinforce(InfantryReinforcements) end)
+ Trigger.AfterDelay(Utils.Seconds(30), function() Reinforce(VehicleReinforcements) end)
+ Trigger.AfterDelay(Utils.Seconds(60), function() Reinforce(VehicleReinforcements) end)
end
+tick = 0
+baseEstablished = false
Tick = function()
+ tick = tick + 1
if enemy.HasNoRequiredUnits() then
- player.MarkCompletedObjective(gdiObjective)
+ player.MarkCompletedObjective(gdiObjective1)
end
if player.HasNoRequiredUnits() then
- player.MarkFailedObjective(gdiObjective)
+ enemy.MarkCompletedObjective(nodObjective)
+ end
+
+ if not baseEstablished and tick % Utils.Seconds(1) == 0 and CheckForBase() then
+ baseEstablished = true
+ player.MarkCompletedObjective(gdiObjective2)
end
end
diff --git a/mods/cnc/maps/gdi01/map.yaml b/mods/cnc/maps/gdi01/map.yaml
index 8948d0b9f6..df4e1b7c72 100644
--- a/mods/cnc/maps/gdi01/map.yaml
+++ b/mods/cnc/maps/gdi01/map.yaml
@@ -467,6 +467,8 @@ Rules:
EarlyGameOver: true
^Infantry:
MustBeDestroyed:
+ ^Vehicle:
+ MustBeDestroyed:
PROC:
Buildable:
Prerequisites: ~disabled