diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index c213a501b9..206ed65fbb 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -502,6 +502,24 @@ + + + + + + + + + + + + + + + + + + @@ -547,4 +565,8 @@ copy "FuzzyLogicLibrary.dll" "$(SolutionDir)" cd "$(SolutionDir)" + + + + \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/CallLuaFunc.cs b/OpenRA.Mods.RA/Scripting/CallLuaFunc.cs new file mode 100644 index 0000000000..276652cce1 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/CallLuaFunc.cs @@ -0,0 +1,57 @@ +#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 System; +using Eluant; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Activities +{ + public class CallLuaFunc : Activity + { + LuaFunction function; + public CallLuaFunc(LuaFunction func) + { + function = func.CopyReference() as LuaFunction; + } + + public override Activity Tick(Actor self) + { + if (function != null) + function.Call().Dispose(); + + Dispose(); + return NextActivity; + } + + public override void Cancel(Actor self) + { + Dispose(); + base.Cancel(self); + } + + public void Dispose() + { + if (function == null) + return; + + GC.SuppressFinalize(this); + function.Dispose(); + function = null; + } + + ~CallLuaFunc() + { + if (function != null) + Game.RunAfterTick(Dispose); + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Global/ActorGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/ActorGlobal.cs new file mode 100644 index 0000000000..86c7a40e11 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Global/ActorGlobal.cs @@ -0,0 +1,87 @@ +#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 System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Eluant; +using OpenRA.FileFormats; +using OpenRA.Mods.RA.Buildings; +using OpenRA.Mods.RA.Air; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Scripting +{ + [ScriptGlobal("Actor")] + public class ActorGlobal : ScriptGlobal + { + public ActorGlobal(ScriptContext context) : base(context) { } + + [Desc("Create a new actor. initTable specifies a list of key-value pairs that definite initial parameters for the actor's traits.")] + public Actor Create(string type, bool addToWorld, LuaTable initTable) + { + var initDict = new TypeDictionary(); + + // Convert table entries into ActorInits + foreach (var kv in initTable) + { + // Find the requested type + var typeName = kv.Key.ToString(); + var initType = Game.modData.ObjectCreator.FindType(typeName + "Init"); + if (initType == null) + throw new LuaException("Unknown initializer type '{0}'".F(typeName)); + + // Cast it up to an IActorInit + var genericType = initType.GetInterfaces() + .First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IActorInit<>)); + var innerType = genericType.GetGenericArguments().First(); + + // Try and coerce the table value to the required type + object value; + if (!kv.Value.TryGetClrValue(innerType, out value)) + throw new LuaException("Invalid data type for '{0}' (expected '{1}')".F(typeName, innerType.Name)); + + // Construct the ActorInit. Phew! + var test = initType.GetConstructor(new[] { innerType }).Invoke(new[] { value }); + initDict.Add(test); + } + + // The actor must be added to the world at the end of the tick + var a = context.World.CreateActor(false, type, initDict); + if (addToWorld) + context.World.AddFrameEndTask(w => w.Add(a)); + + return a; + } + + [Desc("Returns the build time (in ticks) of the requested unit type")] + public int BuildTime(string type) + { + ActorInfo ai; + if (!Rules.Info.TryGetValue(type, out ai)) + throw new LuaException("Unknown actor type '{0}'".F(type)); + + return ai.GetBuildTime(); + } + + [Desc("Returns the cruise altitude of the requested unit type (zero if it ground-based).")] + public int CruiseAltitude(string type) + { + ActorInfo ai; + if (!Rules.Info.TryGetValue(type, out ai)) + throw new LuaException("Unknown actor type '{0}'".F(type)); + + var pi = ai.Traits.GetOrDefault(); + return pi != null ? pi.CruiseAltitude.Range : 0; + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Global/CameraGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/CameraGlobal.cs new file mode 100644 index 0000000000..16b243c8de --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Global/CameraGlobal.cs @@ -0,0 +1,33 @@ +#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 System; +using System.Collections.Generic; +using System.Linq; +using Eluant; +using OpenRA.Effects; +using OpenRA.Scripting; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptGlobal("Camera")] + public class CameraGlobal : ScriptGlobal + { + public CameraGlobal(ScriptContext context) + : base(context) { } + + [Desc("The center of the visible viewport.")] + public WPos Position + { + get { return context.WorldRenderer.Viewport.CenterPosition; } + set { context.WorldRenderer.Viewport.Center(value); } + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Global/CoordinateGlobals.cs b/OpenRA.Mods.RA/Scripting/Global/CoordinateGlobals.cs new file mode 100644 index 0000000000..8fd2ca3794 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Global/CoordinateGlobals.cs @@ -0,0 +1,60 @@ +#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 + +namespace OpenRA.Scripting +{ + [ScriptGlobal("CPos")] + public class CPosGlobal : ScriptGlobal + { + public CPosGlobal(ScriptContext context) : base(context) { } + + [Desc("Create a new CPos with the specified coordinates.")] + public CPos New(int x, int y) { return new CPos(x, y); } + + [Desc("The cell coordinate origin.")] + public CPos Zero { get { return CPos.Zero; } } + } + + [ScriptGlobal("CVec")] + public class CVecGlobal : ScriptGlobal + { + public CVecGlobal(ScriptContext context) : base(context) { } + + [Desc("Create a new CVec with the specified coordinates.")] + public CVec New(int x, int y) { return new CVec(x, y); } + + [Desc("The cell zero-vector.")] + public CVec Zero { get { return CVec.Zero; } } + } + + [ScriptGlobal("WPos")] + public class WPosGlobal : ScriptGlobal + { + public WPosGlobal(ScriptContext context) : base(context) { } + + [Desc("Create a new WPos with the specified coordinates.")] + public WPos New(int x, int y, int z) { return new WPos(x, y, z); } + + [Desc("The world coordinate origin.")] + public WPos Zero { get { return WPos.Zero; } } + } + + [ScriptGlobal("WVec")] + public class WVecGlobal : ScriptGlobal + { + public WVecGlobal(ScriptContext context) : base(context) { } + + [Desc("Create a new WVec with the specified coordinates.")] + public WVec New(int x, int y, int z) { return new WVec(x, y, z); } + + [Desc("The world zero-vector.")] + public WVec Zero { get { return WVec.Zero; } } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Global/MapGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/MapGlobal.cs new file mode 100644 index 0000000000..58a050bb72 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Global/MapGlobal.cs @@ -0,0 +1,90 @@ +#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 System; +using System.Collections.Generic; +using System.Linq; +using Eluant; +using OpenRA.Effects; +using OpenRA.Scripting; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptGlobal("Map")] + public class MapGlobal : ScriptGlobal + { + SpawnMapActors sma; + public MapGlobal(ScriptContext context) : base(context) + { + sma = context.World.WorldActor.Trait(); + + // Register map actors as globals (yuck!) + foreach (var kv in sma.Actors) + context.RegisterMapActor(kv.Key, kv.Value); + } + + [Desc("Returns a table of all actors within the requested region, filtered using the specified function.")] + public LuaTable ActorsInCircle(WPos location, WRange radius, LuaFunction filter) + { + var actors = context.World.FindActorsInCircle(location, radius) + .Select(a => a.ToLuaValue(context)) + .Where(a => + { + using (var f = filter.Call(a)) + return f.First().ToBoolean(); + }); + return actors.ToLuaTable(context); + } + + [Desc("Returns a random cell inside the visible region of the map.")] + public CPos RandomCell() + { + return context.World.ChooseRandomCell(context.World.SharedRandom); + } + + [Desc("Returns a random cell on the visible border of the map.")] + public CPos RandomEdgeCell() + { + return context.World.ChooseRandomEdgeCell(); + } + + [Desc("Returns true if there is only one human player.")] + public bool IsSinglePlayer { get { return context.World.LobbyInfo.IsSinglePlayer; } } + + [Desc("Returns the difficulty selected by the player before starting the mission.")] + public string Difficulty { get { return context.World.LobbyInfo.GlobalSettings.Difficulty; } } + + [Desc("Returns a table of all the actors that were specified in the map file.")] + public LuaTable NamedActors + { + get + { + return sma.Actors.Values.ToLuaTable(context); + } + } + + [Desc("Returns the actor that was specified with a given name in " + + "the map file (or nil, if the actor is dead or not found")] + public Actor NamedActor(string actorName) + { + Actor ret; + if (!sma.Actors.TryGetValue(actorName, out ret)) + return null; + + return ret; + } + + [Desc("Returns true if actor was originally specified in the map file.")] + public bool IsNamedActor(Actor actor) + { + return actor.ActorID <= sma.LastMapActorID && actor.ActorID > sma.LastMapActorID - sma.Actors.Count; + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Global/PlayerGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/PlayerGlobal.cs new file mode 100644 index 0000000000..f55211e0d5 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Global/PlayerGlobal.cs @@ -0,0 +1,46 @@ +#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 System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Eluant; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA.Scripting +{ + [ScriptGlobal("Player")] + public class PlayerGlobal : ScriptGlobal + { + public PlayerGlobal(ScriptContext context) : base(context) { } + + [Desc("Returns the player with the specified internal name, or nil if a match is not found.")] + public Player GetPlayer(string name) + { + return context.World.Players.FirstOrDefault(p => p.InternalName == name); + } + + [Desc("Returns a table of players filtered by the specified function.")] + public LuaTable GetPlayers(LuaFunction filter) + { + var players = context.World.Players + .Select(p => p.ToLuaValue(context)) + .Where(a => + { + using (var f = filter.Call(a)) + return f.First().ToBoolean(); + }); + + return players.ToLuaTable(context); + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs new file mode 100644 index 0000000000..c22b941f60 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Global/TriggerGlobal.cs @@ -0,0 +1,112 @@ +#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 System; +using System.Collections.Generic; +using System.Linq; +using Eluant; +using OpenRA.Effects; +using OpenRA.Scripting; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptGlobal("Trigger")] + public class TriggerGlobal : ScriptGlobal + { + public TriggerGlobal(ScriptContext context) : base(context) { } + + ScriptTriggers GetScriptTriggers(Actor a) + { + var events = a.TraitOrDefault(); + if (events == null) + throw new LuaException("Actor '{0}' requires the ScriptTriggers trait before attaching a trigger".F(a.Info.Name)); + + return events; + } + + [Desc("Call a function after a specified delay. The callback function will be called as func().")] + public void AfterDelay(int delay, LuaFunction func) + { + var f = func.CopyReference() as LuaFunction; + + Action doCall = () => + { + try + { + using (f) + f.Call(); + } + catch (LuaException e) + { + context.FatalError(e.Message); + } + }; + + context.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, doCall))); + } + + [Desc("Call a function each tick that the actor is idle. " + + "The callback function will be called as func(Actor self).")] + public void OnIdle(Actor a, LuaFunction func) + { + GetScriptTriggers(a).RegisterIdleCallback(func, context); + } + + [Desc("Call a function when the actor is damaged. The callback " + + "function will be called as func(Actor self, Actor attacker).")] + public void OnDamaged(Actor a, LuaFunction func) + { + GetScriptTriggers(a).RegisterDamagedCallback(func, context); + } + + [Desc("Call a function when the actor is killed. The callback " + + "function will be called as func(Actor self, Actor killer).")] + public void OnKilled(Actor a, LuaFunction func) + { + GetScriptTriggers(a).RegisterKilledCallback(func, context); + } + + [Desc("Call a function when all of the actors in a group are killed. The callback " + + "function will be called as func().")] + public void OnAllKilled(LuaTable actors, LuaFunction func) + { + List group = new List(); + foreach (var kv in actors) + { + Actor actor; + if (!kv.Value.TryGetClrValue(out actor)) + throw new LuaException("OnAllKilled requires a table of int,Actor pairs. Recieved {0},{1}".F(kv.Key.GetType().Name, kv.Value.GetType().Name)); + + group.Add(actor); + } + + var copy = (LuaFunction)func.CopyReference(); + Action OnMemberKilled = m => + { + group.Remove(m); + if (!group.Any()) + { + copy.Call(); + copy.Dispose(); + } + }; + + foreach (var a in group) + GetScriptTriggers(a).OnKilledInternal += OnMemberKilled; + } + + [Desc("Call a function when this actor produces another actor. " + + "The callback function will be called as func(Actor producer, Actor produced).")] + public void OnProduction(Actor a, LuaFunction func) + { + GetScriptTriggers(a).RegisterProductionCallback(func, context); + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Global/UtilsGlobal.cs b/OpenRA.Mods.RA/Scripting/Global/UtilsGlobal.cs new file mode 100644 index 0000000000..5f65effcc2 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Global/UtilsGlobal.cs @@ -0,0 +1,100 @@ +#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 System; +using System.Linq; +using Eluant; +using OpenRA.Effects; +using OpenRA.Scripting; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptGlobal("Utils")] + public class UtilsGlobal : ScriptGlobal + { + public UtilsGlobal(ScriptContext context) : base(context) { } + + [Desc("Calls a function on every value in table.")] + public void Do(LuaTable table, LuaFunction func) + { + foreach (var kv in table) + func.Call(kv.Value).Dispose(); + } + + [Desc("Returns true if func returns true for any value in table.")] + public bool Any(LuaTable table, LuaFunction func) + { + foreach (var kv in table) + { + using (var ret = func.Call(kv.Value)) + { + var result = ret.FirstOrDefault(); + if (result != null && result.ToBoolean()) + return true; + } + } + + return false; + } + + [Desc("Returns true if func returns true for all values in table.")] + public bool All(LuaTable table, LuaFunction func) + { + foreach (var kv in table) + { + using (var ret = func.Call(kv.Value)) + { + var result = ret.FirstOrDefault(); + if (result == null || !result.ToBoolean()) + return false; + } + } + + return true; + } + + [Desc("Returns a random value from table.")] + public LuaValue Random(LuaTable table) + { + return table.Values.Random(context.World.SharedRandom); + } + + [Desc("Expands the given footprint one step along the coordinate axes, and (if requested) diagonals")] + public LuaTable ExpandFootprint(LuaTable cells, bool allowDiagonal) + { + var footprint = cells.Values.Select(c => + { + CPos cell; + if (!c.TryGetClrValue(out cell)) + throw new LuaException("ExpandFootprint only accepts a table of CPos"); + + return cell; + }); + + var expanded = Traits.Util.ExpandFootprint(footprint, allowDiagonal); + return expanded.ToLuaTable(context); + } + + [Desc("Returns a random integer x in the range low <= x < high.")] + public int RandomInteger(int low, int high) + { + if (high <= low) + return low; + + return context.World.SharedRandom.Next(low, high); + } + + [Desc("Returns the center of a cell in world coordinates.")] + public WPos CenterOfCell(CPos cell) + { + return cell.CenterPosition; + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs new file mode 100644 index 0000000000..1e5aba5391 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs @@ -0,0 +1,43 @@ +#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 System.Linq; +using Eluant; +using OpenRA; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("Support Powers")] + public class ChronsphereProperties : ScriptActorProperties, Requires + { + public ChronsphereProperties(Actor self) + : base(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) + { + foreach (var kv in unitLocationPairs) + { + Actor actor; + CPos cell; + if (!kv.Key.TryGetClrValue(out actor) || !kv.Value.TryGetClrValue(out cell)) + throw new LuaException("Chronoshift requires a table of Actor,CPos pairs. Received {0},{1}".F(kv.Key.WrappedClrType().Name, kv.Value.WrappedClrType().Name)); + + var cs = actor.TraitOrDefault(); + if (cs != null && cs.CanChronoshiftTo(actor, cell)) + cs.Teleport(actor, cell, duration, killCargo, self); + } + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs new file mode 100644 index 0000000000..0167305f08 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/CombatProperties.cs @@ -0,0 +1,42 @@ +#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 OpenRA; +using OpenRA.FileFormats; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Move; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("Combat")] + public class CombatProperties : ScriptActorProperties, Requires, Requires + { + public CombatProperties(Actor self) : base(self) { } + + [ScriptActorPropertyActivity] + [Desc("Seek out and attack nearby targets.")] + public void Hunt() + { + self.QueueActivity(new Hunt(self)); + } + + [ScriptActorPropertyActivity] + [Desc("Move to a cell, but stop and attack anything within range on the way. " + + "closeEnough defines an optional range (in cells) that will be considered " + + "close enough to complete the activity.")] + public void AttackMove(CPos cell, int closeEnough = 0) + { + 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 new file mode 100644 index 0000000000..cdc967f480 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/GeneralProperties.cs @@ -0,0 +1,139 @@ +#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 OpenRA; +using OpenRA.FileFormats; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Move; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("General")] + public class GeneralProperties : ScriptActorProperties + { + readonly IFacing facing; + readonly AutoTarget autotarget; + + public GeneralProperties(Actor self) : base(self) + { + facing = self.TraitOrDefault(); + autotarget = self.TraitOrDefault(); + } + + [Desc("Specifies whether the actor is in the world.")] + public bool IsInWorld + { + get + { + return self.IsInWorld; + } + + set + { + if (value) + self.World.AddFrameEndTask(w => w.Add(self)); + else + self.World.AddFrameEndTask(w => w.Remove(self)); + } + } + + [Desc("Specifies whether the actor is idle (not performing any activities).")] + public bool IsIdle { get { return self.IsIdle; } } + + [Desc("The actor position in cell coordinates.")] + public CPos Location { get { return self.Location; } } + + [Desc("The actor position in world coordinates.")] + public WPos CenterPosition { get { return self.CenterPosition; } } + + [Desc("The player that owns the actor.")] + public Player Owner { get { return self.Owner; } } + + [Desc("The direction that the actor is facing.")] + public int Facing + { + get + { + if (facing == null) + throw new LuaException("Actor '{0}' doesn't define a facing".F(self)); + + return facing.Facing; + } + } + + [ScriptActorPropertyActivity] + [Desc("Instantly moves the actor to the specified cell.")] + public void Teleport(CPos cell) + { + self.QueueActivity(new SimpleTeleport(cell)); + } + + [ScriptActorPropertyActivity] + [Desc("Run an arbitrary lua function.")] + public void CallFunc(LuaFunction func) + { + self.QueueActivity(new CallLuaFunc(func)); + } + + [ScriptActorPropertyActivity] + [Desc("Wait for a specified number of game ticks (25 ticks = 1 second).")] + public void Wait(int ticks) + { + self.QueueActivity(new Wait(ticks)); + } + + [ScriptActorPropertyActivity] + [Desc("Remove the actor from the game, without triggering any death notification.")] + public void Destroy() + { + self.QueueActivity(new RemoveSelf()); + } + + [Desc("Attempt to cancel any active activities.")] + public void Stop() + { + self.CancelActivity(); + } + + [Desc("Current actor stance. Returns nil if this actor doesn't support stances.")] + public string Stance + { + get + { + if (autotarget == null) + return null; + + return autotarget.Stance.ToString(); + } + + set + { + if (autotarget == null) + return; + + UnitStance stance; + if (!Enum.TryParse(value, true, out stance)) + throw new LuaException("Unknown stance type '{0}'".F(value)); + + autotarget.Stance = stance; + } + } + + [Desc("Test whether an actor has a specific property.")] + public bool HasProperty(string name) + { + return self.HasScriptProperty(name); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs new file mode 100644 index 0000000000..46f9727f58 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/HealthProperties.cs @@ -0,0 +1,63 @@ +#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 OpenRA; +using OpenRA.FileFormats; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Move; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("General")] + public class HealthProperties : ScriptActorProperties, Requires + { + Health health; + public HealthProperties(Actor self) + : base(self) + { + health = self.Trait(); + } + + [Desc("Current health of the actor.")] + public int Health + { + get { return health.HP; } + set { health.InflictDamage(self, self, health.HP - value, null, true); } + } + + [Desc("Maximum health of the actor.")] + public int MaxHealth { get { return health.MaxHP; } } + + [Desc("Specifies whether the actor is alive or dead.")] + public bool IsDead { get { return health.IsDead; } } + } + + [ScriptPropertyGroup("General")] + public class InvulnerableProperties : ScriptActorProperties, Requires + { + ScriptInvulnerable invulnerable; + public InvulnerableProperties(Actor self) + : base(self) + { + invulnerable = self.Trait(); + } + + [Desc("Set or query unit invulnerablility.")] + public bool Invulnerable + { + get { return invulnerable.Invulnerable; } + set { invulnerable.Invulnerable = value; } + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/MobileProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/MobileProperties.cs new file mode 100644 index 0000000000..e7fd4f9414 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/MobileProperties.cs @@ -0,0 +1,40 @@ +#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 OpenRA; +using OpenRA.FileFormats; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Move; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("Movement")] + public class MobileProperties : ScriptActorProperties, Requires + { + public MobileProperties(Actor self) : base(self) { } + + [ScriptActorPropertyActivity] + [Desc("Moves within the cell grid. closeEnough defines an optional range " + + "(in cells) that will be considered close enough to complete the activity.")] + public void Move(CPos cell, int closeEnough = 0) + { + self.QueueActivity(new Move.Move(cell, WRange.FromCells(closeEnough))); + } + + [ScriptActorPropertyActivity] + [Desc("Moves within the cell grid, ignoring lane biases.")] + public void ScriptedMove(CPos cell) + { + self.QueueActivity(new Move.Move(cell)); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs new file mode 100644 index 0000000000..8e2d7b9f40 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs @@ -0,0 +1,43 @@ +#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 System.Linq; +using Eluant; +using OpenRA; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("Production")] + public class ProductionProperties : ScriptActorProperties, Requires + { + readonly Production p; + + public ProductionProperties(Actor self) + : base(self) + { + p = self.Trait(); + } + + [ScriptActorPropertyActivity] + [Desc("Build a unit, ignoring the production queue. The activity will wait if the exit is blocked")] + public void Produce(string actorType) + { + ActorInfo actorInfo; + if (!Rules.Info.TryGetValue(actorType, out actorInfo)) + throw new LuaException("Unknown actor type '{0}'".F(actorType)); + + self.QueueActivity(new WaitFor(() => p.Produce(self, actorInfo))); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/ResourceProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ResourceProperties.cs new file mode 100644 index 0000000000..acaa6dae55 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/ResourceProperties.cs @@ -0,0 +1,46 @@ +#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 System; +using OpenRA; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("Resources")] + public class ResourceProperties : ScriptPlayerProperties, Requires + { + readonly PlayerResources pr; + + public ResourceProperties(Player player) + : base(player) + { + pr = player.PlayerActor.Trait(); + } + + [Desc("The amount of harvestable resources held by the player.")] + public int Resources + { + get { return pr.Ore; } + set { pr.Ore = value.Clamp(0, pr.OreCapacity); } + } + + [Desc("The maximum resource storage of the player.")] + public int ResourceCapacity { get { return pr.OreCapacity; } } + + [Desc("The amount of cash held by the player.")] + public int Cash + { + get { return pr.Cash; } + set { pr.Cash = Math.Max(0, value); } + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs new file mode 100644 index 0000000000..6e5e58e1e1 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs @@ -0,0 +1,66 @@ +#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 System.Linq; +using Eluant; +using OpenRA; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Air; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [ScriptPropertyGroup("Transports")] + public class TransportProperties : ScriptActorProperties, Requires + { + readonly Cargo cargo; + + public TransportProperties(Actor self) + : base(self) + { + cargo = self.Trait(); + } + + [Desc("Specifies whether transport has any passengers.")] + public bool HasPassengers { get { return cargo.Passengers.Any(); } } + + [Desc("Teleport an existing actor inside this transport.")] + public void LoadPassenger(Actor a) { cargo.Load(self, a); } + + [ScriptActorPropertyActivity] + [Desc("Command transport to unload passengers.")] + public void UnloadPassengers() + { + self.QueueActivity(new UnloadCargo(self, true)); + } + } + + [ScriptPropertyGroup("Transports")] + public class ParadropPowers : ScriptActorProperties, Requires, Requires + { + readonly ParaDrop paradrop; + + public ParadropPowers(Actor self) + : base(self) + { + paradrop = self.Trait(); + } + + [ScriptActorPropertyActivity] + [Desc("Command transport to paradrop passengers near the target cell.")] + public void Paradrop(CPos cell) + { + paradrop.SetLZ(cell); + self.QueueActivity(new FlyAttack(Target.FromCell(cell))); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/ScriptInvulnerable.cs b/OpenRA.Mods.RA/Scripting/ScriptInvulnerable.cs new file mode 100644 index 0000000000..eb6356bb69 --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/ScriptInvulnerable.cs @@ -0,0 +1,28 @@ +#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 OpenRA.GameRules; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + [Desc("Allows map scripts to make this actor invulnerable via actor.Invulnerable = true.")] + class ScriptInvulnerableInfo : TraitInfo {} + + class ScriptInvulnerable : IDamageModifier + { + public bool Invulnerable = false; + + public float GetDamageModifier(Actor attacker, WarheadInfo warhead) + { + return Invulnerable ? 0.0f : 1.0f; + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs new file mode 100644 index 0000000000..1dfe5d0e7a --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/ScriptTriggers.cs @@ -0,0 +1,121 @@ +#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 System; +using System.Collections.Generic; +using System.Linq; +using Eluant; +using OpenRA.Primitives; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + [Desc("Allows map scripts to attach triggers to this actor via the Triggers global.")] + public class ScriptTriggersInfo : TraitInfo { } + + public class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction + { + public event Action OnKilledInternal = _ => {}; + + List> onIdle = new List>(); + List> onDamaged = new List>(); + List> onKilled = new List>(); + List> onProduction = new List>(); + + public void RegisterIdleCallback(LuaFunction func, ScriptContext context) + { + onIdle.Add(Pair.New((LuaFunction)func.CopyReference(), context)); + } + + public void RegisterDamagedCallback(LuaFunction func, ScriptContext context) + { + onDamaged.Add(Pair.New((LuaFunction)func.CopyReference(), context)); + } + + public void RegisterKilledCallback(LuaFunction func, ScriptContext context) + { + onKilled.Add(Pair.New((LuaFunction)func.CopyReference(), context)); + } + + public void RegisterProductionCallback(LuaFunction func, ScriptContext context) + { + onProduction.Add(Pair.New((LuaFunction)func.CopyReference(), context)); + } + + public void TickIdle(Actor self) + { + foreach (var f in onIdle) + { + var a = self.ToLuaValue(f.Second); + f.First.Call(a).Dispose(); + a.Dispose(); + } + } + + public void Damaged(Actor self, AttackInfo e) + { + foreach (var f in onDamaged) + { + var a = self.ToLuaValue(f.Second); + 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 onKilled) + { + var a = self.ToLuaValue(f.Second); + var b = e.Attacker.ToLuaValue(f.Second); + f.First.Call(a, b).Dispose(); + a.Dispose(); + b.Dispose(); + } + + // Run any internally bound callbacks + OnKilledInternal(self); + } + + public void UnitProduced(Actor self, Actor other, CPos exit) + { + foreach (var f in onProduction) + { + var a = self.ToLuaValue(f.Second); + var b = other.ToLuaValue(f.Second); + f.First.Call(a, b).Dispose(); + a.Dispose(); + b.Dispose(); + } + } + + bool disposed; + public void Dispose() + { + disposed = true; + var toDispose = new [] { onIdle, onDamaged, onKilled, onProduction }; + + foreach (var f in toDispose.SelectMany(f => f)) + f.First.Dispose(); + + GC.SuppressFinalize(this); + } + + ~ScriptTriggers() + { + if (!disposed) + Game.RunAfterTick(Dispose); + } + } +} diff --git a/OpenRA.Utility/UpgradeRules.cs b/OpenRA.Utility/UpgradeRules.cs index 24447f26fc..2a2823b754 100644 --- a/OpenRA.Utility/UpgradeRules.cs +++ b/OpenRA.Utility/UpgradeRules.cs @@ -242,6 +242,13 @@ namespace OpenRA.Utility node.Key = "MaxDistance"; } + // Added new Lua API + if (engineVersion < 20140421) + { + if (depth == 0 && node.Value.Nodes.Any(n => n.Key == "LuaScriptEvents")) + node.Value.Nodes.Add(new MiniYamlNode("ScriptTriggers", "")); + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/mods/cnc/maps/gdi04a/map.yaml b/mods/cnc/maps/gdi04a/map.yaml index f03ef094ee..7a74ce92b7 100644 --- a/mods/cnc/maps/gdi04a/map.yaml +++ b/mods/cnc/maps/gdi04a/map.yaml @@ -520,10 +520,10 @@ Actors: Location: 11,52 Owner: Neutral GDIReinforcementsEntry: waypoint - Location: 7, 50 + Location: 7,50 Owner: Neutral Auto1Trigger: waypoint - Location: 52, 47 + Location: 52,47 Owner: Neutral NodHeliEntry: waypoint Location: 41,23 @@ -561,6 +561,7 @@ Rules: -GiveCashCrateAction: -ExplodeCrateAction@fire: -CloakCrateAction: + ScriptTriggers: Sequences: diff --git a/mods/cnc/maps/shellmap/map.yaml b/mods/cnc/maps/shellmap/map.yaml index 92291613fe..d09caa5565 100644 --- a/mods/cnc/maps/shellmap/map.yaml +++ b/mods/cnc/maps/shellmap/map.yaml @@ -993,8 +993,8 @@ Rules: PlayMusicOnMapLoad: Music: map1 Loop: true - LuaScriptInterface: - LuaScripts: shellmap.lua + LuaScript: + Scripts: shellmap.lua LoadWidgetAtGameStart: Widget: MENU_BACKGROUND LST: diff --git a/mods/cnc/maps/shellmap/shellmap.lua b/mods/cnc/maps/shellmap/shellmap.lua index ba266799b2..2b72205e06 100644 --- a/mods/cnc/maps/shellmap/shellmap.lua +++ b/mods/cnc/maps/shellmap/shellmap.lua @@ -5,15 +5,14 @@ speed = 5 Tick = function() ticks = ticks + 1 local t = (ticks + 45) % (360 * speed) * (math.pi / 180) / speed; - OpenRA.SetViewportCenterPosition(WPos.op_Addition(viewportOrigin, WVec.New(-15360 * math.sin(t), 4096 * math.cos(t)))) + Camera.Position = viewportOrigin + WVec.New(-15360 * math.sin(t), 4096 * math.cos(t), 0) end WorldLoaded = function() - viewportOrigin = OpenRA.GetViewportCenterPosition() - CreateUnitsInTransport(lst1, { "htnk" }); - CreateUnitsInTransport(lst2, { "mcv" }); - CreateUnitsInTransport(lst3, { "htnk" }); - + viewportOrigin = Camera.Position + LoadTransport(lst1, "htnk") + LoadTransport(lst2, "mcv") + LoadTransport(lst3, "htnk") local units = { boat1, boat2, boat3, boat4, lst1, lst2, lst3} for i, unit in ipairs(units) do LoopTrack(unit, CPos.New(8, unit.Location.Y), CPos.New(87, unit.Location.Y)) @@ -21,17 +20,11 @@ WorldLoaded = function() end LoopTrack = function(actor, left, right) - Actor.ScriptedMove(actor, left) - Actor.Teleport(actor, right) - Actor.CallFunc(actor, function() LoopTrack(actor, left, right) end) + actor.ScriptedMove(left) + actor.Teleport(right) + actor.CallFunc(function() LoopTrack(actor, left, right) end) end -CreateUnitsInTransport = function(transport, passengerNames) - local cargo = Actor.Trait(transport, "Cargo") - local owner = Actor.Owner(transport) - local facing = Actor.Facing(transport) - - for i, passengerName in ipairs(passengerNames) do - cargo:Load(transport, Actor.Create(passengerName, { AddToWorld = false, Owner = owner, Facing = { facing, "Int32" } })) - end +LoadTransport = function(transport, passenger) + transport.LoadPassenger(Actor.Create(passenger, false, { Owner = transport.Owner, Facing = transport.Facing })) end \ No newline at end of file diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index cc2038e2bd..01a01e3a69 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -42,6 +42,7 @@ UncloakSound: trans1.aud Huntable: LuaScriptEvents: + ScriptTriggers: ^Tank: AppearsOnRadar: @@ -90,6 +91,7 @@ UncloakSound: trans1.aud Huntable: LuaScriptEvents: + ScriptTriggers: ^Helicopter: AppearsOnRadar: @@ -122,6 +124,7 @@ UpdatesPlayerStatistics: Huntable: LuaScriptEvents: + ScriptTriggers: ^Infantry: AppearsOnRadar: @@ -183,6 +186,7 @@ LuaScriptEvents: DetectCloaked: Range: 1 + ScriptTriggers: ^CivInfantry: Inherits: ^Infantry @@ -261,6 +265,7 @@ UpdatesPlayerStatistics: Huntable: LuaScriptEvents: + ScriptTriggers: ^Plane: AppearsOnRadar: @@ -280,6 +285,7 @@ Huntable: AttackMove: LuaScriptEvents: + ScriptTriggers: ^Ship: AppearsOnRadar: @@ -305,6 +311,7 @@ UpdatesPlayerStatistics: Huntable: LuaScriptEvents: + ScriptTriggers: ^Building: AppearsOnRadar: @@ -348,6 +355,7 @@ Huntable: LuaScriptEvents: Demolishable: + ScriptTriggers: ^BaseBuilding: Inherits: ^Building @@ -389,6 +397,7 @@ FrozenUnderFog: StartsRevealed: true LuaScriptEvents: + ScriptTriggers: ^TechBuilding: Inherits: ^CivBuilding @@ -429,6 +438,7 @@ FrozenUnderFog: StartsRevealed: true LuaScriptEvents: + ScriptTriggers: ^Wall: AppearsOnRadar: @@ -460,6 +470,7 @@ BodyOrientation: FrozenUnderFog: LuaScriptEvents: + ScriptTriggers: ^Tree: Tooltip: @@ -484,6 +495,7 @@ FrozenUnderFog: StartsRevealed: true LuaScriptEvents: + ScriptTriggers: ^TibTree: Tooltip: @@ -520,6 +532,7 @@ FrozenUnderFog: StartsRevealed: true LuaScriptEvents: + ScriptTriggers: ^Husk: Health: @@ -545,6 +558,7 @@ BodyOrientation: LuaScriptEvents: DisabledOverlay: + ScriptTriggers: ^HelicopterHusk: Inherits: ^Husk @@ -573,4 +587,5 @@ DestroyedSound: xplobig4.aud BodyOrientation: LuaScriptEvents: + ScriptTriggers: diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 55a17f8556..bb7ecc7d7d 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -38,6 +38,7 @@ Huntable: LuaScriptEvents: Demolishable: + ScriptTriggers: ^Tank: AppearsOnRadar: @@ -79,6 +80,7 @@ Huntable: LuaScriptEvents: Demolishable: + ScriptTriggers: ^Husk: Health: @@ -107,6 +109,7 @@ TransformOnCapture: ForceHealthPercentage: 25 DisabledOverlay: + ScriptTriggers: ^TowerHusk: Health: @@ -126,6 +129,7 @@ Types: Husk BodyOrientation: LuaScriptEvents: + ScriptTriggers: ^AircraftHusk: Inherits: ^Husk @@ -190,6 +194,7 @@ UpdatesPlayerStatistics: Huntable: LuaScriptEvents: + ScriptTriggers: ^Plane: AppearsOnRadar: @@ -214,6 +219,7 @@ Huntable: AttackMove: LuaScriptEvents: + ScriptTriggers: ^Helicopter: Inherits: ^Plane @@ -270,3 +276,5 @@ Weapons: shrapnel Pieces: 3, 7 Range: 2c0, 5c0 + ScriptTriggers: + diff --git a/mods/ra/maps/desert-shellmap/desert-shellmap.lua b/mods/ra/maps/desert-shellmap/desert-shellmap.lua index c91de275af..22d182c27a 100644 --- a/mods/ra/maps/desert-shellmap/desert-shellmap.lua +++ b/mods/ra/maps/desert-shellmap/desert-shellmap.lua @@ -1,4 +1,4 @@ -local ants = OpenRA.GetRandomInteger(0, 51) == 0 +local ants = Utils.RandomInteger(0, 51) == 0 if ants then UnitTypes = { "ant", "ant", "ant" } @@ -29,85 +29,110 @@ else { SovietWarFactory1, { "3tnk", "4tnk", "v2rl", "ttnk", "apc" } } } end -ParadropPlaneType = "badr" -ParadropWaypointCount = 8 -SendSovietUnits = function(entryCell, unitTypes, interval) - local units = Reinforcements.Reinforce(soviets, unitTypes, entryCell, entryCell, interval) - local team = Team.New(units) - Team.AddEventHandler(team.OnAllKilled, function() - SendSovietUnits(entryCell, unitTypes, interval) - end) - Team.Do(team, function(a) - Actor.OnDamaged(a, function() - if not Actor.CargoIsEmpty(a) then - Actor.Stop(a) - Actor.UnloadCargo(a, true) +ParadropWaypoints = { Paradrop1, Paradrop2, Paradrop3, Paradrop4, Paradrop5, Paradrop6, Paradrop7, Paradrop8 } + +BindActorTriggers = function(a) + if a.HasProperty("Hunt") then + if a.Owner == allies then + Trigger.OnIdle(a, a.Hunt) + else + Trigger.OnIdle(a, function(a) a.AttackMove(AlliedTechnologyCenter.Location) end) + end + end + + if a.HasProperty("HasPassengers") then + Trigger.OnDamaged(a, function() + if a.HasPassengers then + a.Stop() + a.UnloadPassengers() end end) - Actor.OnIdle(a, function() Actor.AttackMove(a, AlliedTechnologyCenter.Location) end) + end +end + +SendSovietUnits = function(entryCell, unitTypes, interval) + local i = 0 + team = {} + + Utils.Do(unitTypes, function(type) + local a = Actor.Create(type, false, { Owner = soviets, Location = entryCell }) + BindActorTriggers(a) + Trigger.AfterDelay(i * interval, function() a.IsInWorld = true end) + table.insert(team, a) + i = i + 1 end) + + Trigger.OnAllKilled(team, function() SendSovietUnits(entryCell, unitTypes, interval) end) end ShipAlliedUnits = function() - local transport, reinforcements = Reinforcements.Insert(allies, "lst", { "1tnk", "1tnk", "jeep", "2tnk", "2tnk" }, { LstEntry.Location, LstUnload.Location }, { LstEntry.Location }) - Utils.Do(reinforcements, function(a) Actor.OnIdle(a, Actor.Hunt) end) - OpenRA.RunAfterDelay(60 * 25, ShipAlliedUnits) + local transport = Actor.Create("lst", true, { Location = LstEntry.Location, Owner = allies }) + + Utils.Do({ "1tnk", "1tnk", "jeep", "2tnk", "2tnk" }, function(type) + local a = Actor.Create(type, false, { Owner = allies }) + BindActorTriggers(a) + transport.LoadPassenger(a) + end) + + transport.Move(LstUnload.Location) + transport.UnloadPassengers() + transport.Wait(50) + transport.Move(LstEntry.Location) + transport.Destroy() + Trigger.AfterDelay(60 * 25, ShipAlliedUnits) end ParadropSovietUnits = function() - local lz = Map.GetNamedActor("Paradrop" .. OpenRA.GetRandomInteger(1, ParadropWaypointCount - 1)).Location - local plane, passengers = SupportPowers.Paradrop(soviets, ParadropPlaneType, ParadropUnitTypes, Map.GetRandomEdgeCell(), lz) - Utils.Do(passengers, function(a) Actor.OnIdle(a, Actor.Hunt) end) - OpenRA.RunAfterDelay(35 * 25, ParadropSovietUnits) + local lz = Utils.Random(ParadropWaypoints).Location + local start = Utils.CenterOfCell(Map.RandomEdgeCell()) + WVec.New(0, 0, Actor.CruiseAltitude("badr")) + local transport = Actor.Create("badr", true, { CenterPosition = start, Owner = soviets, Facing = (Utils.CenterOfCell(lz) - start).Facing }) + + Utils.Do(ParadropUnitTypes, function(type) + local a = Actor.Create(type, false, { Owner = soviets }) + BindActorTriggers(a) + transport.LoadPassenger(a) + end) + + transport.Paradrop(lz) + Trigger.AfterDelay(35 * 25, ParadropSovietUnits) end -ProduceUnits = function() - Utils.Do(ProducedUnitTypes, function(t) - local factory = t[1] - if not Actor.IsDead(factory) and not Production.PerFactoryQueueIsBusy(factory) then - local unitType = t[2][OpenRA.GetRandomInteger(1, #t[2] + 1)] - Production.BuildWithPerFactoryQueue(factory, unitType) - end - end) - OpenRA.RunAfterDelay(15, ProduceUnits) +ProduceUnits = function(t) + local factory = t[1] + if not factory.IsDead then + local unitType = t[2][Utils.RandomInteger(1, #t[2] + 1)] + factory.Wait(Actor.BuildTime(unitType)) + factory.Produce(unitType) + factory.CallFunc(function() ProduceUnits(t) end) + end end SetupAlliedUnits = function() - for a in Utils.Enumerate(Map.GetNamedActors()) do - if Actor.Owner(a) == allies then - if Actor.HasTrait(a, "LuaScriptEvents") then - a:AddTrait(OpenRA.New("Invulnerable")) -- todo: replace - end - Actor.SetStance(a, "Defend") + Utils.Do(Map.NamedActors, function(a) + if a.Owner == allies and a.HasProperty("Invulnerable") then + a.Invulnerable = true + a.Stance = "Defend" end - end -end - -SetupFactories = function() - Utils.Do(ProducedUnitTypes, function(pair) - Actor.OnProduced(pair[1], function(self, other, ex) - Actor.Hunt(other) - Actor.OnDamaged(other, function() - if not Actor.CargoIsEmpty(other) then - Actor.Stop(other) - Actor.UnloadCargo(other, true) - end - end) - end) end) end +SetupFactories = function() + Utils.Do(ProducedUnitTypes, function(pair) + Trigger.OnProduction(pair[1], function(_, a) BindActorTriggers(a) end) + end) +end + ChronoshiftAlliedUnits = function() - local cells = Map.ExpandFootprint({ ChronoshiftLocation.Location }, false) + local cells = Utils.ExpandFootprint({ ChronoshiftLocation.Location }, false) local units = { } for i = 1, #cells do - local unit = Actor.Create("2tnk", { Owner = allies, Facing = { 0, "Int32" } }) - Actor.OnIdle(unit, Actor.Hunt) - table.insert(units, { unit, cells[i] }) - end - SupportPowers.Chronoshift(units, Chronosphere) - OpenRA.RunAfterDelay(60 * 25, ChronoshiftAlliedUnits) + local unit = Actor.Create("2tnk", true, { Owner = allies, Facing = 0 }) + BindActorTriggers(unit) + units[unit] = cells[i] + end + Chronosphere.Chronoshift(units) + Trigger.AfterDelay(60 * 25, ChronoshiftAlliedUnits) end ticks = 0 @@ -117,33 +142,21 @@ Tick = function() ticks = ticks + 1 local t = (ticks + 45) % (360 * speed) * (math.pi / 180) / speed; - OpenRA.SetViewportCenterPosition(WPos.op_Addition(viewportOrigin, WVec.New(19200 * math.sin(t), 20480 * math.cos(t)))) - - if ticks % 150 == 0 then - Utils.Do(Actor.ActorsWithTrait("AttackBase"), function(a) - if Actor.IsIdle(a) and not Map.IsNamedActor(a) and not Actor.IsDead(a) and Actor.IsInWorld(a) and (Actor.Owner(a) == soviets or Actor.Owner(a) == allies) then - Actor.Hunt(a) - end - end) - end + Camera.Position = viewportOrigin + WVec.New(19200 * math.sin(t), 20480 * math.cos(t), 0) end WorldLoaded = function() - allies = OpenRA.GetPlayer("Allies") - soviets = OpenRA.GetPlayer("Soviets") - - viewportOrigin = OpenRA.GetViewportCenterPosition() - + allies = Player.GetPlayer("Allies") + soviets = Player.GetPlayer("Soviets") + viewportOrigin = Camera.Position + SetupAlliedUnits() SetupFactories() - ProduceUnits() ShipAlliedUnits() ParadropSovietUnits() - OpenRA.RunAfterDelay(5 * 25, ChronoshiftAlliedUnits) - - OpenRA.GiveCash(allies, 1000000) - OpenRA.GiveCash(soviets, 1000000) - + Trigger.AfterDelay(5 * 25, ChronoshiftAlliedUnits) + Utils.Do(ProducedUnitTypes, ProduceUnits) + SendSovietUnits(Entry1.Location, UnitTypes, 50) SendSovietUnits(Entry2.Location, UnitTypes, 50) SendSovietUnits(Entry3.Location, UnitTypes, 50) diff --git a/mods/ra/maps/desert-shellmap/map.yaml b/mods/ra/maps/desert-shellmap/map.yaml index 6046df662e..1277021602 100644 --- a/mods/ra/maps/desert-shellmap/map.yaml +++ b/mods/ra/maps/desert-shellmap/map.yaml @@ -315,9 +315,6 @@ Actors: Actor110: fcom Location: 106,44 Owner: Soviets - Actor111: silo - Location: 96,28 - Owner: Soviets Actor106: fact Location: 114,43 Owner: Soviets @@ -477,9 +474,6 @@ Actors: Actor105: brik Location: 94,70 Owner: Allies - Actor154: silo - Location: 82,86 - Owner: Allies SovietBarracks1: barr Location: 109,48 Owner: Soviets @@ -956,30 +950,6 @@ Actors: Actor195: hpad Location: 70,75 Owner: Allies - Actor75: oilb - Location: 4,126 - Owner: Allies - Actor334: oilb - Location: 6,126 - Owner: Allies - Actor335: oilb - Location: 8,126 - Owner: Allies - Actor336: oilb - Location: 10,126 - Owner: Allies - Actor337: oilb - Location: 12,126 - Owner: Allies - Actor338: oilb - Location: 14,126 - Owner: Allies - Actor339: oilb - Location: 2,126 - Owner: Allies - Actor340: oilb - Location: 0,126 - Owner: Allies Actor341: dome Location: 63,73 Owner: Allies @@ -1034,15 +1004,6 @@ Actors: Actor361: pbox.e1 Location: 71,96 Owner: Allies - Actor55: silo - Location: 81,85 - Owner: Allies - Actor76: silo - Location: 81,86 - Owner: Allies - Actor159: silo - Location: 82,85 - Owner: Allies Actor365: hpad Location: 64,78 Owner: Allies @@ -1303,11 +1264,16 @@ Rules: -CrateSpawner: -SpawnMPUnits: -MPStartLocations: - LuaScriptInterface: - LuaScripts: desert-shellmap.lua + ResourceType@ore: + ValuePerUnit: 0 + LuaScript: + Scripts: desert-shellmap.lua LoadWidgetAtGameStart: Widget: MAINMENU -StartGameNotification: + OILB: + CashTrickler: + ShowTicks: false TRAN.Husk2: Burns: Damage: 0 @@ -1316,24 +1282,6 @@ Rules: APC: Cargo: InitialUnits: e1, e1, e2, e3, e4 - TENT: - ProductionQueue: - Type: Infantry - Group: Infantry - BuildSpeed: .4 - LowPowerSlowdown: 3 - BARR: - ProductionQueue: - Type: Infantry - Group: Infantry - BuildSpeed: .4 - LowPowerSlowdown: 3 - WEAP: - ProductionQueue: - Type: Vehicle - Group: Vehicle - BuildSpeed: .4 - LowPowerSlowdown: 3 Ant: Buildable: Owner: soviet @@ -1341,6 +1289,7 @@ Rules: Health: HP: 200 ^Vehicle: + ScriptInvulnerable: GivesBounty: Percentage: 0 GainsExperience: @@ -1349,6 +1298,7 @@ Rules: ArmorModifier: SpeedModifier: ^Tank: + ScriptInvulnerable: GivesBounty: Percentage: 0 GainsExperience: @@ -1357,6 +1307,7 @@ Rules: ArmorModifier: SpeedModifier: ^Infantry: + ScriptInvulnerable: -Selectable: # short-term hack to make infantry not play die sounds until we fix RenderInfantry GivesBounty: Percentage: 0 @@ -1366,12 +1317,15 @@ Rules: ArmorModifier: SpeedModifier: ^Ship: + ScriptInvulnerable: GivesBounty: Percentage: 0 ^Plane: + ScriptInvulnerable: GivesBounty: Percentage: 0 ^Building: + ScriptInvulnerable: GivesBounty: Percentage: 0 diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 6655d48eae..eedca89b59 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -52,6 +52,7 @@ CancelActivity: True CaptureNotification: Notification: UnitStolen + ScriptTriggers: ^Tank: AppearsOnRadar: @@ -107,6 +108,7 @@ CancelActivity: True CaptureNotification: Notification: UnitStolen + ScriptTriggers: ^Infantry: AppearsOnRadar: @@ -165,6 +167,7 @@ RequiresTech: InfantryHealing Huntable: LuaScriptEvents: + ScriptTriggers: ^Ship: AppearsOnRadar: @@ -197,6 +200,7 @@ BodyOrientation: Huntable: LuaScriptEvents: + ScriptTriggers: ^Plane: AppearsOnRadar: @@ -231,6 +235,7 @@ BodyOrientation: Huntable: LuaScriptEvents: + ScriptTriggers: ^Helicopter: Inherits: ^Plane @@ -283,6 +288,7 @@ Huntable: LuaScriptEvents: Demolishable: + ScriptTriggers: ^Wall: AppearsOnRadar: @@ -319,6 +325,7 @@ BodyOrientation: FrozenUnderFog: LuaScriptEvents: + ScriptTriggers: ^TechBuilding: Inherits: ^Building @@ -426,6 +433,7 @@ FrozenUnderFog: StartsRevealed: true LuaScriptEvents: + ScriptTriggers: ^Husk: Husk: @@ -455,6 +463,7 @@ TransformOnCapture: ForceHealthPercentage: 25 DisabledOverlay: + ScriptTriggers: ^HelicopterHusk: Inherits: ^Husk @@ -499,6 +508,7 @@ AutoTargetIgnore: BodyOrientation: LuaScriptEvents: + ScriptTriggers: ^Rock: Tooltip: @@ -520,6 +530,7 @@ FrozenUnderFog: StartsRevealed: true LuaScriptEvents: + ScriptTriggers: ^DesertCivBuilding: Inherits: ^CivBuilding diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 82574ac88c..0e18444daf 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -39,6 +39,7 @@ Huntable: LuaScriptEvents: Demolishable: + ScriptTriggers: ^Wall: AppearsOnRadar: @@ -77,6 +78,7 @@ BodyOrientation: LuaScriptEvents: Demolishable: + ScriptTriggers: ^Infantry: AppearsOnRadar: @@ -129,6 +131,7 @@ BodyOrientation: Huntable: LuaScriptEvents: + ScriptTriggers: ^CivilianInfantry: Inherits: ^Infantry @@ -199,6 +202,7 @@ CameraPitch: 90 Huntable: LuaScriptEvents: + ScriptTriggers: ^Helicopter: AppearsOnRadar: @@ -232,4 +236,5 @@ CameraPitch: 90 Huntable: LuaScriptEvents: + ScriptTriggers: