Merge pull request #6284 from obrakmann/gdi01-touchup

More Lua API enhancements
This commit is contained in:
Matthias Mailänder
2014-08-23 16:28:36 +02:00
25 changed files with 503 additions and 153 deletions

View File

@@ -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);
});

View File

@@ -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

View File

@@ -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);
});

View File

@@ -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));

View File

@@ -566,6 +566,7 @@
<Compile Include="Warheads\HealthPercentageDamageWarhead.cs" />
<Compile Include="Warheads\PerCellDamageWarhead.cs" />
<Compile Include="Warheads\SpreadDamageWarhead.cs" />
<Compile Include="Scripting\Global\ReinforcementsGlobal.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">

View File

@@ -79,8 +79,8 @@ namespace OpenRA.Mods.RA
objectives.Insert(newID, new MissionObjective(type, description));
ObjectiveAdded(player);
foreach (var imo in player.PlayerActor.TraitsImplementing<INotifyObjectivesUpdated>())
imo.OnObjectiveAdded(player, newID);
foreach (var inou in player.PlayerActor.TraitsImplementing<INotifyObjectivesUpdated>())
inou.OnObjectiveAdded(player, newID);
return newID;
}
@@ -90,60 +90,57 @@ namespace OpenRA.Mods.RA
if (objectiveID >= objectives.Count || objectives[objectiveID].State == ObjectiveState.Completed)
return;
var inous = player.PlayerActor.TraitsImplementing<INotifyObjectivesUpdated>();
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<INotifyObjectivesUpdated>())
if (playerWon)
{
imo.OnObjectiveCompleted(player, objectiveID);
foreach (var inou in inous)
inou.OnPlayerWon(player);
if (playerWon)
imo.OnPlayerWon(player);
}
if (playerWon)
CheckIfGameIsOver(player);
}
}
}
public void MarkFailed(Player player, int objectiveID)
{
if (objectiveID >= objectives.Count || objectives[objectiveID].State == ObjectiveState.Failed)
return;
var inous = player.PlayerActor.TraitsImplementing<INotifyObjectivesUpdated>();
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<INotifyObjectivesUpdated>())
if (playerLost)
{
imo.OnObjectiveFailed(player, objectiveID);
foreach (var inou in inous)
inou.OnPlayerLost(player);
if (playerLost)
imo.OnPlayerLost(player);
}
if (playerLost)
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();
@@ -151,51 +148,84 @@ namespace OpenRA.Mods.RA
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<MissionObjectives>().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<MissionObjectives>().ForceDefeat(p);
}
}
else
{
player.WinState = WinState.Won;
player.World.OnPlayerWinStateChanged(player);
if (info.EarlyGameOver)
foreach (var p in enemies)
p.PlayerActor.Trait<MissionObjectives>().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<MissionObjectives>().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<MissionObjectives>().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<Player> ObjectiveAdded = player => { };
@@ -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);
}
}

View File

@@ -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); }
}
}

View File

@@ -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()
{

View File

@@ -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<AircraftInfo>();
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<Aircraft>())
{
if (actor.HasTrait<Helicopter>())
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<Actor>();
for (var i = 1; i <= actorTypes.Count; i++)
{
string actorType;
if (!(actorTypes[i].TryGetClrValue<String>(out actorType)))
throw new LuaException("Invalid data in actorTypes array");
CPos entry, next = new CPos();
if (!(entryPath[1].TryGetClrValue<CPos>(out entry)
&& (entryPath.Count < 2 || entryPath[2].TryGetClrValue<CPos>(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<CPos>(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<CPos>(out entry)
&& (entryPath.Count < 2 || entryPath[2].TryGetClrValue<CPos>(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<Cargo>();
var passengers = context.CreateTable();
if (cargo != null && cargoTypes != null)
{
for (var i = 1; i <= cargoTypes.Count; i++)
{
string cargoType;
if (!(cargoTypes [i].TryGetClrValue<String>(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<CPos>(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<Helicopter>();
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<CPos>(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;
}
}
}

View File

@@ -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)
{

View File

@@ -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);
}
}
}

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
[ScriptPropertyGroup("Support Powers")]
public class ChronsphereProperties : ScriptActorProperties, Requires<ChronoshiftPowerInfo>
{
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)

View File

@@ -17,7 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
[ScriptPropertyGroup("Combat")]
public class CombatProperties : ScriptActorProperties, Requires<AttackBaseInfo>, Requires<IMoveInfo>
{
public CombatProperties(Actor self) : base(self) { }
public CombatProperties(ScriptContext context, Actor self)
: base(context, self) { }
[ScriptActorPropertyActivity]
[Desc("Seek out and attack nearby targets.")]

View File

@@ -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<IFacing>();
autotarget = self.TraitOrDefault<AutoTarget>();

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
public class GuardProperties : ScriptActorProperties, Requires<GuardInfo>, Requires<IMoveInfo>
{
Guard guard;
public GuardProperties(Actor self)
: base(self)
public GuardProperties(ScriptContext context, Actor self)
: base(context, self)
{
guard = self.Trait<Guard>();
}

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
public class HealthProperties : ScriptActorProperties, Requires<HealthInfo>
{
Health health;
public HealthProperties(Actor self)
: base(self)
public HealthProperties(ScriptContext context, Actor self)
: base(context, self)
{
health = self.Trait<Health>();
}
@@ -41,8 +41,8 @@ namespace OpenRA.Mods.RA.Scripting
public class InvulnerableProperties : ScriptActorProperties, Requires<ScriptInvulnerableInfo>
{
ScriptInvulnerable invulnerable;
public InvulnerableProperties(Actor self)
: base(self)
public InvulnerableProperties(ScriptContext context, Actor self)
: base(context, self)
{
invulnerable = self.Trait<ScriptInvulnerable>();
}

View File

@@ -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<MissionObjectives>();
}

View File

@@ -17,7 +17,8 @@ namespace OpenRA.Mods.RA.Scripting
[ScriptPropertyGroup("Movement")]
public class MobileProperties : ScriptActorProperties, Requires<MobileInfo>
{
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 " +

View File

@@ -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<AttackBase>().Select(a => a.Actor)
.Where(a => a.Owner == player && !a.IsDead() && a.IsInWorld && a.HasTrait<Mobile>())
.Select(a => a.ToLuaValue(context)).ToLuaTable(context);
}
}
}

View File

@@ -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<Production>();
}

View File

@@ -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<PlayerResources>();
}

View File

@@ -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<Cargo>();
}
@@ -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<ParaDrop>();
}

View File

@@ -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<ScriptTriggers> { }
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<Actor> 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);
using (var a = self.ToLuaValue(f.Second))
f.First.Call(a).Dispose();
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);
using (var a = self.ToLuaValue(f.Second))
using (var b = e.Attacker.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose();
a.Dispose();
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);
using (var a = self.ToLuaValue(f.Second))
using (var b = e.Attacker.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose();
a.Dispose();
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);
using (var a = self.ToLuaValue(f.Second))
using (var b = other.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose();
a.Dispose();
b.Dispose();
}
}
public void OnPlayerWon(Player player)
{
foreach (var f in Triggers[Trigger.OnPlayerWon])
{
var a = player.ToLuaValue(f.Second);
using (var a = player.ToLuaValue(f.Second))
f.First.Call(a).Dispose();
a.Dispose();
}
}
public void OnPlayerLost(Player player)
{
foreach (var f in Triggers[Trigger.OnPlayerLost])
{
var a = player.ToLuaValue(f.Second);
using (var a = player.ToLuaValue(f.Second))
f.First.Call(a).Dispose();
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);
using (var a = player.ToLuaValue(f.Second))
using (var b = id.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose();
a.Dispose();
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);
using (var a = player.ToLuaValue(f.Second))
using (var b = id.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose();
a.Dispose();
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);
using (var a = player.ToLuaValue(f.Second))
using (var b = id.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose();
a.Dispose();
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)

View File

@@ -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

View File

@@ -467,6 +467,8 @@ Rules:
EarlyGameOver: true
^Infantry:
MustBeDestroyed:
^Vehicle:
MustBeDestroyed:
PROC:
Buildable:
Prerequisites: ~disabled