Initial commit: OpenRA game engine
Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
192
OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs
Normal file
192
OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Actor")]
|
||||
public class ActorGlobal : ScriptGlobal
|
||||
{
|
||||
public ActorGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
static ActorInit CreateInit(string initName, LuaValue value)
|
||||
{
|
||||
// Find the requested type
|
||||
var initInstance = initName.Split(ActorInfo.TraitInstanceSeparator);
|
||||
var initType = Game.ModData.ObjectCreator.FindType(initInstance[0] + "Init");
|
||||
if (initType == null)
|
||||
throw new LuaException($"Unknown initializer type '{initInstance[0]}'");
|
||||
|
||||
// Construct the ActorInit.
|
||||
var init = (ActorInit)RuntimeHelpers.GetUninitializedObject(initType);
|
||||
if (initInstance.Length > 1)
|
||||
initType.GetField(nameof(ActorInit.InstanceName)).SetValue(init, initInstance[1]);
|
||||
|
||||
if (value is LuaTable tableValue && init is CompositeActorInit compositeInit)
|
||||
{
|
||||
var args = compositeInit.InitializeArgs();
|
||||
var initValues = new Dictionary<string, object>();
|
||||
foreach (var kv in tableValue)
|
||||
{
|
||||
using (kv.Key)
|
||||
using (kv.Value)
|
||||
{
|
||||
var key = kv.Key.ToString();
|
||||
if (!args.TryGetValue(key, out var type))
|
||||
throw new LuaException($"Unknown initializer type '{initInstance[0]}.{key}'");
|
||||
|
||||
var isActorReference = type == typeof(ActorInitActorReference);
|
||||
if (isActorReference)
|
||||
type = kv.Value is LuaString ? typeof(string) : typeof(Actor);
|
||||
|
||||
if (!kv.Value.TryGetClrValue(type, out var clrValue))
|
||||
throw new LuaException($"Invalid data type for '{initInstance[0]}.{key}' (expected {type.Name}, got {kv.Value.WrappedClrType()})");
|
||||
|
||||
if (isActorReference)
|
||||
clrValue = type == typeof(string) ? new ActorInitActorReference((string)clrValue) : new ActorInitActorReference((Actor)clrValue);
|
||||
|
||||
initValues[key] = clrValue;
|
||||
}
|
||||
}
|
||||
|
||||
compositeInit.Initialize(initValues);
|
||||
return init;
|
||||
}
|
||||
|
||||
var initializers = initType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.Where(m => m.Name == "Initialize" && m.GetParameters().Length == 1)
|
||||
.ToList();
|
||||
|
||||
foreach (var initializer in initializers)
|
||||
{
|
||||
var parameterType = initializer.GetParameters().First().ParameterType;
|
||||
var valueType = parameterType.IsEnum ? Enum.GetUnderlyingType(parameterType) : parameterType;
|
||||
|
||||
// Try and coerce the table value to the required type
|
||||
if (!value.TryGetClrValue(valueType, out var clrValue))
|
||||
continue;
|
||||
|
||||
initializer.Invoke(init, [clrValue]);
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
var types = initializers.Select(y => y.GetParameters()[0].ParameterType.Name).JoinWith(", ");
|
||||
throw new LuaException($"Invalid data type for '{initInstance[0]}' (expected one of {types})");
|
||||
}
|
||||
|
||||
[Desc("Create a new actor. initTable specifies a list of key-value pairs that defines the initial parameters for the actor's traits.")]
|
||||
public Actor Create(string type, bool addToWorld, [ScriptEmmyTypeOverride("initTable")] LuaTable initTable)
|
||||
{
|
||||
var initDict = new TypeDictionary();
|
||||
|
||||
// Convert table entries into ActorInits
|
||||
foreach (var kv in initTable)
|
||||
{
|
||||
using (kv.Key)
|
||||
using (kv.Value)
|
||||
initDict.Add(CreateInit(kv.Key.ToString(), kv.Value));
|
||||
}
|
||||
|
||||
var owner = initDict.GetOrDefault<OwnerInit>();
|
||||
if (owner == null)
|
||||
throw new LuaException($"Tried to create actor '{type}' with an invalid or no owner init!");
|
||||
|
||||
// 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.",
|
||||
"An optional second value can be used to exactly specify the producing queue type.")]
|
||||
public int BuildTime(string type, string queue = null)
|
||||
{
|
||||
if (!Context.World.Map.Rules.Actors.TryGetValue(type, out var ai))
|
||||
throw new LuaException($"Unknown actor type '{type}'");
|
||||
|
||||
var bi = ai.TraitInfoOrDefault<BuildableInfo>();
|
||||
|
||||
if (bi == null)
|
||||
return 0;
|
||||
|
||||
var time = bi.BuildDuration;
|
||||
if (time == -1)
|
||||
{
|
||||
var valued = ai.TraitInfoOrDefault<ValuedInfo>();
|
||||
if (valued == null)
|
||||
return 0;
|
||||
else
|
||||
time = valued.Cost;
|
||||
}
|
||||
|
||||
int pbi;
|
||||
if (queue != null)
|
||||
{
|
||||
var pqueue = Context.World.Map.Rules.Actors.Values.SelectMany(a => a.TraitInfos<ProductionQueueInfo>()
|
||||
.Where(x => x.Type == queue)).FirstOrDefault();
|
||||
|
||||
if (pqueue == null)
|
||||
throw new LuaException($"The specified queue '{queue}' does not exist!");
|
||||
|
||||
pbi = pqueue.BuildDurationModifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pqueue = Context.World.Map.Rules.Actors.Values.SelectMany(a => a.TraitInfos<ProductionQueueInfo>()
|
||||
.Where(x => bi.Queue.Contains(x.Type))).FirstOrDefault();
|
||||
|
||||
if (pqueue == null)
|
||||
throw new LuaException($"No actors can produce actor '{type}'!");
|
||||
|
||||
pbi = pqueue.BuildDurationModifier;
|
||||
}
|
||||
|
||||
time = time * bi.BuildDurationModifier * pbi / 10000;
|
||||
return time;
|
||||
}
|
||||
|
||||
[Desc("Returns the cruise altitude of the requested unit type (zero if it is ground-based).")]
|
||||
public int CruiseAltitude(string type)
|
||||
{
|
||||
if (!Context.World.Map.Rules.Actors.TryGetValue(type, out var ai))
|
||||
throw new LuaException($"Unknown actor type '{type}'");
|
||||
|
||||
var pi = ai.TraitInfoOrDefault<ICruiseAltitudeInfo>();
|
||||
return pi != null ? pi.GetCruiseAltitude().Length : 0;
|
||||
}
|
||||
|
||||
[Desc("Returns the cost of the requested unit given by the Valued trait.")]
|
||||
public int Cost(string type)
|
||||
{
|
||||
if (!Context.World.Map.Rules.Actors.TryGetValue(type, out var ai))
|
||||
throw new LuaException($"Unknown actor type '{type}'");
|
||||
|
||||
var vi = ai.TraitInfoOrDefault<ValuedInfo>();
|
||||
if (vi == null)
|
||||
throw new LuaException($"Actor type '{type}' does not have the Valued trait required to get the Cost.");
|
||||
|
||||
return vi.Cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
OpenRA.Mods.Common/Scripting/Global/AngleGlobal.cs
Normal file
43
OpenRA.Mods.Common/Scripting/Global/AngleGlobal.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting.Global
|
||||
{
|
||||
[ScriptGlobal("Angle")]
|
||||
public class AngleGlobal : ScriptGlobal
|
||||
{
|
||||
public AngleGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
[Desc("0/1024 units = 0/360 degrees")]
|
||||
public WAngle North => WAngle.Zero;
|
||||
[Desc("128 units = 315 degrees")]
|
||||
public WAngle NorthWest => new(128);
|
||||
[Desc("256 units = 270 degrees")]
|
||||
public WAngle West => new(256);
|
||||
[Desc("384 units = 225 degrees")]
|
||||
public WAngle SouthWest => new(384);
|
||||
[Desc("512 units = 180 degrees")]
|
||||
public WAngle South => new(512);
|
||||
[Desc("640 units = 135 degrees")]
|
||||
public WAngle SouthEast => new(640);
|
||||
[Desc("768 units = 90 degrees")]
|
||||
public WAngle East => new(768);
|
||||
[Desc("896 units = 45 degrees")]
|
||||
public WAngle NorthEast => new(896);
|
||||
|
||||
[Desc("Create an arbitrary angle. 1024 units = 360 degrees. North is 0. " +
|
||||
"Units increase *counter* clockwise. Comparison given to degrees increasing clockwise.")]
|
||||
public WAngle New(int a) { return new WAngle(a); }
|
||||
}
|
||||
}
|
||||
54
OpenRA.Mods.Common/Scripting/Global/BeaconGlobal.cs
Normal file
54
OpenRA.Mods.Common/Scripting/Global/BeaconGlobal.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Effects;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Beacon")]
|
||||
public class BeaconGlobal : ScriptGlobal
|
||||
{
|
||||
readonly RadarPings radarPings;
|
||||
|
||||
public BeaconGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
radarPings = context.World.WorldActor.TraitOrDefault<RadarPings>();
|
||||
}
|
||||
|
||||
[Desc("Creates a new beacon that stays for the specified time at the specified WPos. " +
|
||||
"Does not remove player set beacons, nor gets removed by placing them. " +
|
||||
"Requires the 'PlaceBeacon' trait on the player actor.")]
|
||||
public void New(Player owner, WPos position, int duration = 750, bool showRadarPings = true)
|
||||
{
|
||||
var beacon = owner.PlayerActor.Info.TraitInfoOrDefault<PlaceBeaconInfo>();
|
||||
if (beacon == null)
|
||||
throw new LuaException("The player actor has no 'PlaceBeacon' trait.");
|
||||
|
||||
var playerBeacon = new Beacon(owner, position, duration, beacon.Palette, beacon.IsPlayerPalette,
|
||||
beacon.BeaconImage, beacon.BeaconSequence, beacon.ArrowSequence, beacon.CircleSequence);
|
||||
|
||||
owner.PlayerActor.World.AddFrameEndTask(w => w.Add(playerBeacon));
|
||||
|
||||
if (showRadarPings && radarPings != null)
|
||||
{
|
||||
radarPings.Add(
|
||||
() => owner.IsAlliedWith(owner.World.RenderPlayer),
|
||||
position,
|
||||
owner.Color,
|
||||
duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
OpenRA.Mods.Common/Scripting/Global/CameraGlobal.cs
Normal file
29
OpenRA.Mods.Common/Scripting/Global/CameraGlobal.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Camera")]
|
||||
public class CameraGlobal : ScriptGlobal
|
||||
{
|
||||
public CameraGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
[Desc("The center of the visible viewport.")]
|
||||
public WPos Position
|
||||
{
|
||||
get => Context.WorldRenderer.Viewport.CenterPosition;
|
||||
set => Context.WorldRenderer.Viewport.Center(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
OpenRA.Mods.Common/Scripting/Global/ColorGlobal.cs
Normal file
127
OpenRA.Mods.Common/Scripting/Global/ColorGlobal.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using Eluant;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting.Global
|
||||
{
|
||||
// Kept as HSLColor for backwards compatibility
|
||||
[ScriptGlobal("HSLColor")]
|
||||
public class ColorGlobal : ScriptGlobal
|
||||
{
|
||||
public ColorGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
[Desc("Create a new color with the specified hue/saturation/luminosity.")]
|
||||
public Color New(int hue, int saturation, int luminosity)
|
||||
{
|
||||
var h = (byte)hue.Clamp(0, 255);
|
||||
var s = (byte)saturation.Clamp(0, 255);
|
||||
var l = (byte)luminosity.Clamp(0, 255);
|
||||
|
||||
return Color.FromAhsl(255, h / 255f, s / 255f, l / 255f);
|
||||
}
|
||||
|
||||
[Desc("Create a new color with the specified red/green/blue/[alpha] values.")]
|
||||
public Color FromRGB(int red, int green, int blue, int alpha = 255)
|
||||
{
|
||||
return Color.FromArgb(
|
||||
alpha.Clamp(0, 255),
|
||||
red.Clamp(0, 255),
|
||||
green.Clamp(0, 255),
|
||||
blue.Clamp(0, 255));
|
||||
}
|
||||
|
||||
[Desc("Create a new color with the specified red/green/blue/[alpha] hex string (rrggbb[aa]).")]
|
||||
public Color FromHex(string value)
|
||||
{
|
||||
if (Color.TryParse(value, out var color))
|
||||
return color;
|
||||
|
||||
throw new LuaException("Invalid rrggbb[aa] hex string.");
|
||||
}
|
||||
|
||||
[Desc("FromHex(\"00FFFF\")")]
|
||||
public Color Aqua => Color.Aqua;
|
||||
[Desc("FromHex(\"000000\")")]
|
||||
public Color Black => Color.Black;
|
||||
[Desc("FromHex(\"0000FF\")")]
|
||||
public Color Blue => Color.Blue;
|
||||
[Desc("FromHex(\"A52A2A\")")]
|
||||
public Color Brown => Color.Brown;
|
||||
[Desc("FromHex(\"00FFFF\")")]
|
||||
public Color Cyan => Color.Cyan;
|
||||
[Desc("FromHex(\"00008B\")")]
|
||||
public Color DarkBlue => Color.DarkBlue;
|
||||
[Desc("FromHex(\"008B8B\")")]
|
||||
public Color DarkCyan => Color.DarkCyan;
|
||||
[Desc("FromHex(\"A9A9A9\")")]
|
||||
public Color DarkGray => Color.DarkGray;
|
||||
[Desc("FromHex(\"006400\")")]
|
||||
public Color DarkGreen => Color.DarkGreen;
|
||||
[Desc("FromHex(\"FF8C00\")")]
|
||||
public Color DarkOrange => Color.DarkOrange;
|
||||
[Desc("FromHex(\"8B0000\")")]
|
||||
public Color DarkRed => Color.DarkRed;
|
||||
[Desc("FromHex(\"FF00FF\")")]
|
||||
public Color Fuchsia => Color.Fuchsia;
|
||||
[Desc("FromHex(\"FFD700\")")]
|
||||
public Color Gold => Color.Gold;
|
||||
[Desc("FromHex(\"808080\")")]
|
||||
public Color Gray => Color.Gray;
|
||||
[Desc("FromHex(\"008000\")")]
|
||||
public Color Green => Color.Green;
|
||||
[Desc("FromHex(\"7CFC00\")")]
|
||||
public Color LawnGreen => Color.LawnGreen;
|
||||
[Desc("FromHex(\"ADD8E6\")")]
|
||||
public Color LightBlue => Color.LightBlue;
|
||||
[Desc("FromHex(\"E0FFFF\")")]
|
||||
public Color LightCyan => Color.LightCyan;
|
||||
[Desc("FromHex(\"D3D3D3\")")]
|
||||
public Color LightGray => Color.LightGray;
|
||||
[Desc("FromHex(\"90EE90\")")]
|
||||
public Color LightGreen => Color.LightGreen;
|
||||
[Desc("FromHex(\"FFFFE0\")")]
|
||||
public Color LightYellow => Color.LightYellow;
|
||||
[Desc("FromHex(\"00FF00\")")]
|
||||
public Color Lime => Color.Lime;
|
||||
[Desc("FromHex(\"32CD32\")")]
|
||||
public Color LimeGreen => Color.LimeGreen;
|
||||
[Desc("FromHex(\"FF00FF\")")]
|
||||
public Color Magenta => Color.Magenta;
|
||||
[Desc("FromHex(\"800000\")")]
|
||||
public Color Maroon => Color.Maroon;
|
||||
[Desc("FromHex(\"000080\")")]
|
||||
public Color Navy => Color.Navy;
|
||||
[Desc("FromHex(\"808000\")")]
|
||||
public Color Olive => Color.Olive;
|
||||
[Desc("FromHex(\"FFA500\")")]
|
||||
public Color Orange => Color.Orange;
|
||||
[Desc("FromHex(\"FF4500\")")]
|
||||
public Color OrangeRed => Color.OrangeRed;
|
||||
[Desc("FromHex(\"800080\")")]
|
||||
public Color Purple => Color.Purple;
|
||||
[Desc("FromHex(\"FF0000\")")]
|
||||
public Color Red => Color.Red;
|
||||
[Desc("FromHex(\"FA8072\")")]
|
||||
public Color Salmon => Color.Salmon;
|
||||
[Desc("FromHex(\"87CEEB\")")]
|
||||
public Color SkyBlue => Color.SkyBlue;
|
||||
[Desc("FromHex(\"008080\")")]
|
||||
public Color Teal => Color.Teal;
|
||||
[Desc("FromHex(\"FFFF00\")")]
|
||||
public Color Yellow => Color.Yellow;
|
||||
[Desc("FromHex(\"FFFFFF\")")]
|
||||
public Color White => Color.White;
|
||||
}
|
||||
}
|
||||
107
OpenRA.Mods.Common/Scripting/Global/CoordinateGlobals.cs
Normal file
107
OpenRA.Mods.Common/Scripting/Global/CoordinateGlobals.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("CPos")]
|
||||
public class CPosGlobal : ScriptGlobal
|
||||
{
|
||||
public CPosGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
[Desc("Create a new CPos with the specified coordinates on the ground (layer = 0).")]
|
||||
public CPos New(int x, int y) { return new CPos(x, y); }
|
||||
|
||||
[Desc("Create a new CPos with the specified coordinates on the specified layer. " +
|
||||
"The ground is layer 0, other layers have a unique ID. Examples include tunnels, underground, and elevated bridges.")]
|
||||
public CPos NewWithLayer(int x, int y, byte layer)
|
||||
{
|
||||
if (layer != 0)
|
||||
{
|
||||
var worldCmls = Context.World.GetCustomMovementLayers();
|
||||
if (layer >= worldCmls.Length || worldCmls[layer] == null)
|
||||
{
|
||||
var layerNames = typeof(CustomMovementLayerType)
|
||||
.GetFields()
|
||||
.Select(f => (Index: (byte)f.GetRawConstantValue(), f.Name))
|
||||
.ToArray();
|
||||
var validLayers = new[] { (Index: (byte)0, Name: "Ground") }
|
||||
.Concat(worldCmls
|
||||
.Where(cml => cml != null)
|
||||
.Select(cml => layerNames.Single(ln => ln.Index == cml.Index)));
|
||||
throw new LuaException($"Layer {layer} does not exist on this map. " +
|
||||
$"Valid layers on this map are: {string.Join(", ", validLayers.Select(x => $"{x.Index} ({x.Name})"))}");
|
||||
}
|
||||
}
|
||||
|
||||
return new CPos(x, y, layer);
|
||||
}
|
||||
|
||||
[Desc("The cell coordinate origin.")]
|
||||
public CPos Zero => 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 => 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 => 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 => WVec.Zero;
|
||||
}
|
||||
|
||||
[ScriptGlobal("WDist")]
|
||||
public class WDistGlobal : ScriptGlobal
|
||||
{
|
||||
public WDistGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
[Desc("Create a new WDist.")]
|
||||
public WDist New(int r) { return new WDist(r); }
|
||||
|
||||
[Desc("Create a new WDist by cell distance.")]
|
||||
public WDist FromCells(int numCells) { return WDist.FromCells(numCells); }
|
||||
}
|
||||
}
|
||||
94
OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs
Normal file
94
OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("DateTime")]
|
||||
public class DateGlobal : ScriptGlobal
|
||||
{
|
||||
readonly TimeLimitManager tlm;
|
||||
readonly int ticksPerSecond;
|
||||
|
||||
public DateGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
tlm = context.World.WorldActor.TraitOrDefault<TimeLimitManager>();
|
||||
var gameSpeeds = Game.ModData.GetOrCreate<GameSpeeds>();
|
||||
var defaultGameSpeed = gameSpeeds.Speeds[gameSpeeds.DefaultSpeed];
|
||||
ticksPerSecond = 1000 / defaultGameSpeed.Timestep;
|
||||
}
|
||||
|
||||
[Desc("True on the 31st of October.")]
|
||||
[Obsolete("Use CurrentMonth and CurrentDay instead.")]
|
||||
public bool IsHalloween => DateTime.Today.Month == 10 && DateTime.Today.Day == 31;
|
||||
|
||||
[Desc("Get the current game time (in ticks).")]
|
||||
public int GameTime => Context.World.WorldTick;
|
||||
|
||||
[Desc("Converts the number of seconds into game time (ticks).")]
|
||||
public int Seconds(int seconds)
|
||||
{
|
||||
return seconds * ticksPerSecond;
|
||||
}
|
||||
|
||||
[Desc("Get the current year (1-9999).")]
|
||||
public int CurrentYear => DateTime.Now.Year;
|
||||
[Desc("Get the current month (1-12).")]
|
||||
public int CurrentMonth => DateTime.Now.Month;
|
||||
[Desc("Get the current day (1-31).")]
|
||||
public int CurrentDay => DateTime.Now.Day;
|
||||
[Desc("Get the current hour (0-23).")]
|
||||
public int CurrentHour => DateTime.Now.Hour;
|
||||
[Desc("Get the current minute (0-59).")]
|
||||
public int CurrentMinute => DateTime.Now.Minute;
|
||||
[Desc("Get the current second (0-59).")]
|
||||
public int CurrentSecond => DateTime.Now.Second;
|
||||
|
||||
[Desc("Converts the number of minutes into game time (ticks).")]
|
||||
public int Minutes(int minutes)
|
||||
{
|
||||
return Seconds(minutes * 60);
|
||||
}
|
||||
|
||||
[Desc("Return or set the time limit (in ticks). When setting, the time limit will count from now. Setting the time limit to 0 will disable it.")]
|
||||
public int TimeLimit
|
||||
{
|
||||
get => tlm?.TimeLimit ?? 0;
|
||||
|
||||
set
|
||||
{
|
||||
if (tlm != null)
|
||||
tlm.TimeLimit = value == 0 ? 0 : value + GameTime;
|
||||
else
|
||||
throw new LuaException("Cannot set TimeLimit, TimeLimitManager trait is missing.");
|
||||
}
|
||||
}
|
||||
|
||||
[Desc("The notification string used for custom time limit warnings. See the TimeLimitManager trait documentation for details.")]
|
||||
public string TimeLimitNotification
|
||||
{
|
||||
get => tlm?.Notification;
|
||||
|
||||
set
|
||||
{
|
||||
if (tlm != null)
|
||||
tlm.Notification = value;
|
||||
else
|
||||
throw new LuaException("Cannot set TimeLimitNotification, TimeLimitManager trait is missing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
OpenRA.Mods.Common/Scripting/Global/LightingGlobal.cs
Normal file
67
OpenRA.Mods.Common/Scripting/Global/LightingGlobal.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Lighting")]
|
||||
public class LightingGlobal : ScriptGlobal
|
||||
{
|
||||
readonly IEnumerable<FlashPostProcessEffect> flashEffects;
|
||||
readonly TintPostProcessEffect tintEffect;
|
||||
|
||||
public LightingGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
flashEffects = context.World.WorldActor.TraitsImplementing<FlashPostProcessEffect>();
|
||||
tintEffect = context.World.WorldActor.TraitOrDefault<TintPostProcessEffect>();
|
||||
}
|
||||
|
||||
[Desc("Controls the `" + nameof(FlashPostProcessEffect) + "` trait.")]
|
||||
public void Flash(string type = null, int ticks = -1)
|
||||
{
|
||||
foreach (var effect in flashEffects)
|
||||
if (effect.Info.Type == type)
|
||||
effect.Enable(ticks);
|
||||
}
|
||||
|
||||
[Desc("Red component (0-1).")]
|
||||
public double Red
|
||||
{
|
||||
get => tintEffect?.Red ?? 1;
|
||||
set { if (tintEffect != null) tintEffect.Red = (float)value; }
|
||||
}
|
||||
|
||||
[Desc("Green component (0-1).")]
|
||||
public double Green
|
||||
{
|
||||
get => tintEffect?.Green ?? 1;
|
||||
set { if (tintEffect != null) tintEffect.Green = (float)value; }
|
||||
}
|
||||
|
||||
[Desc("Blue component (0-1).")]
|
||||
public double Blue
|
||||
{
|
||||
get => tintEffect?.Blue ?? 1;
|
||||
set { if (tintEffect != null) tintEffect.Blue = (float)value; }
|
||||
}
|
||||
|
||||
[Desc("Strength of the lighting (0-1).")]
|
||||
public double Ambient
|
||||
{
|
||||
get => tintEffect?.Ambient ?? 1;
|
||||
set { if (tintEffect != null) tintEffect.Ambient = (float)value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
166
OpenRA.Mods.Common/Scripting/Global/MapGlobal.cs
Normal file
166
OpenRA.Mods.Common/Scripting/Global/MapGlobal.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Map")]
|
||||
public class MapGlobal : ScriptGlobal
|
||||
{
|
||||
readonly SpawnMapActors sma;
|
||||
readonly World world;
|
||||
readonly GameSettings gameSettings;
|
||||
|
||||
public MapGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
sma = context.World.WorldActor.Trait<SpawnMapActors>();
|
||||
world = context.World;
|
||||
gameSettings = Game.Settings.Game;
|
||||
|
||||
// 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 Actor[] ActorsInCircle(WPos location, WDist radius, [ScriptEmmyTypeOverride("fun(a: actor):boolean")] LuaFunction filter = null)
|
||||
{
|
||||
var actors = Context.World.FindActorsInCircle(location, radius);
|
||||
return FilteredObjects(actors, filter).ToArray();
|
||||
}
|
||||
|
||||
[Desc("Returns a table of all actors within the requested rectangle, filtered using the specified function.")]
|
||||
public Actor[] ActorsInBox(WPos topLeft, WPos bottomRight, [ScriptEmmyTypeOverride("fun(a: actor):boolean")] LuaFunction filter = null)
|
||||
{
|
||||
var actors = Context.World.ActorMap.ActorsInBox(topLeft, bottomRight);
|
||||
return FilteredObjects(actors, filter).ToArray();
|
||||
}
|
||||
|
||||
// HACK: This API method abuses the coordinate system, and should be removed
|
||||
// in favour of proper actor queries. See #8549.
|
||||
[Obsolete("This function will be removed in future versions. Use Map.ActorsInWorld instead.")]
|
||||
[Desc("Returns the location of the top-left corner of the map (assuming zero terrain height).")]
|
||||
public WPos TopLeft => Context.World.Map.ProjectedTopLeft;
|
||||
|
||||
// HACK: This API method abuses the coordinate system, and should be removed
|
||||
// in favour of proper actor queries. See #8549.
|
||||
[Obsolete("This function will be removed in future versions. Use Map.ActorsInWorld instead.")]
|
||||
[Desc("Returns the location of the bottom-right corner of the map (assuming zero terrain height).")]
|
||||
public WPos BottomRight => Context.World.Map.ProjectedBottomRight;
|
||||
|
||||
[Desc("Returns a random cell inside the visible region of the map.")]
|
||||
public CPos RandomCell()
|
||||
{
|
||||
return Context.World.Map.ChooseRandomCell(Context.World.SharedRandom);
|
||||
}
|
||||
|
||||
[Desc("Returns a random cell on the visible border of the map.")]
|
||||
public CPos RandomEdgeCell()
|
||||
{
|
||||
return Context.World.Map.ChooseRandomEdgeCell(Context.World.SharedRandom);
|
||||
}
|
||||
|
||||
[Desc("Returns the closest cell on the visible border of the map from the given cell.")]
|
||||
public CPos ClosestEdgeCell(CPos givenCell)
|
||||
{
|
||||
return Context.World.Map.ChooseClosestEdgeCell(givenCell);
|
||||
}
|
||||
|
||||
[Desc("Returns the first cell on the visible border of the map from the given cell,",
|
||||
"matching the filter function called as function(cell: cpos):boolean.")]
|
||||
public CPos ClosestMatchingEdgeCell(CPos givenCell, [ScriptEmmyTypeOverride("fun(cell: cpos):boolean")] LuaFunction filter)
|
||||
{
|
||||
return FilteredObjects(Context.World.Map.AllEdgeCells.OrderBy(c => (givenCell - c).Length), filter).FirstOrDefault();
|
||||
}
|
||||
|
||||
[Desc("Returns the center of a cell in world coordinates.")]
|
||||
public WPos CenterOfCell(CPos cell)
|
||||
{
|
||||
return Context.World.Map.CenterOfCell(cell);
|
||||
}
|
||||
|
||||
[Desc("Returns the type of the terrain at the target cell.")]
|
||||
public string TerrainType(CPos cell)
|
||||
{
|
||||
return Context.World.Map.GetTerrainInfo(cell).Type;
|
||||
}
|
||||
|
||||
[Desc("Returns true if there is only one human player.")]
|
||||
public bool IsSinglePlayer => Context.World.LobbyInfo.NonBotPlayers.Count() == 1;
|
||||
|
||||
[Desc("Returns true if this is a shellmap and the player has paused animations.")]
|
||||
public bool IsPausedShellmap => Context.World.Type == WorldType.Shellmap && gameSettings.PauseShellmap;
|
||||
|
||||
[Desc("Returns the value of a `" + nameof(ScriptLobbyDropdown) + "` selected in the game lobby.")]
|
||||
public string LobbyOption(string id)
|
||||
{
|
||||
var option = Context.World.WorldActor.TraitsImplementing<ScriptLobbyDropdown>()
|
||||
.FirstOrDefault(sld => sld.Info.ID == id);
|
||||
|
||||
if (option == null)
|
||||
{
|
||||
Log.Write("lua", $"A {nameof(ScriptLobbyDropdown)} with ID `{id}` was not found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return option.Value;
|
||||
}
|
||||
|
||||
[Desc("Returns the value of a `" + nameof(ScriptLobbyDropdown) + "` selected in the game lobby or fallback to a default value.")]
|
||||
public string LobbyOptionOrDefault(string id, string fallback)
|
||||
{
|
||||
var option = Context.World.WorldActor.TraitsImplementing<ScriptLobbyDropdown>()
|
||||
.FirstOrDefault(sld => sld.Info.ID == id);
|
||||
|
||||
if (option == null)
|
||||
return fallback;
|
||||
|
||||
return option.Value;
|
||||
}
|
||||
|
||||
[Desc("Returns a table of all the actors that were specified in the map file.")]
|
||||
public Actor[] NamedActors => sma.Actors.Values.ToArray();
|
||||
|
||||
[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)
|
||||
{
|
||||
if (!sma.Actors.TryGetValue(actorName, out var ret))
|
||||
return null;
|
||||
|
||||
if (ret.Disposed)
|
||||
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;
|
||||
}
|
||||
|
||||
[Desc("Returns a table of all actors tagged with the given string.")]
|
||||
public Actor[] ActorsWithTag(string tag)
|
||||
{
|
||||
return Context.World.ActorsHavingTrait<ScriptTags>(t => t.HasTag(tag)).ToArray();
|
||||
}
|
||||
|
||||
[Desc("Returns a table of all the actors that are currently on the map/in the world.")]
|
||||
public Actor[] ActorsInWorld => world.Actors.ToArray();
|
||||
}
|
||||
}
|
||||
182
OpenRA.Mods.Common/Scripting/Global/MediaGlobal.cs
Normal file
182
OpenRA.Mods.Common/Scripting/Global/MediaGlobal.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using Eluant;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Mods.Common.Effects;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Media")]
|
||||
public class MediaGlobal : ScriptGlobal
|
||||
{
|
||||
readonly World world;
|
||||
readonly MusicPlaylist playlist;
|
||||
|
||||
public MediaGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
world = context.World;
|
||||
playlist = world.WorldActor.Trait<MusicPlaylist>();
|
||||
}
|
||||
|
||||
[Desc("Play an announcer voice listed in notifications.yaml")]
|
||||
public void PlaySpeechNotification(Player player, string notification)
|
||||
{
|
||||
Game.Sound.PlayNotification(world.Map.Rules, player, "Speech", notification, player?.Faction.InternalName);
|
||||
}
|
||||
|
||||
[Desc("Play a sound listed in notifications.yaml")]
|
||||
public void PlaySoundNotification(Player player, string notification)
|
||||
{
|
||||
Game.Sound.PlayNotification(world.Map.Rules, player, "Sounds", notification, player?.Faction.InternalName);
|
||||
}
|
||||
|
||||
[Desc("Play a sound file")]
|
||||
public void PlaySound(string file)
|
||||
{
|
||||
// TODO: Investigate how scripts use this function, and think about exposing the UI vs World distinction if needed
|
||||
Game.Sound.Play(SoundType.World, file);
|
||||
}
|
||||
|
||||
[Desc("Play track defined in music.yaml or map.yaml, or keep track empty for playing a random song.")]
|
||||
public void PlayMusic(string track = null, [ScriptEmmyTypeOverride("fun()")] LuaFunction onPlayComplete = null)
|
||||
{
|
||||
if (!playlist.IsMusicAvailable)
|
||||
return;
|
||||
|
||||
var musicInfo = !string.IsNullOrEmpty(track)
|
||||
? GetMusicTrack(track)
|
||||
: playlist.GetNextSong();
|
||||
|
||||
var onComplete = WrapOnPlayComplete(onPlayComplete);
|
||||
playlist.Play(musicInfo, onComplete);
|
||||
}
|
||||
|
||||
[Desc("Play track defined in music.yaml or map.yaml as background music." +
|
||||
" If music is already playing use Media.StopMusic() to stop it" +
|
||||
" and the background music will start automatically." +
|
||||
" Keep the track empty to disable background music.")]
|
||||
public void SetBackgroundMusic(string track = null)
|
||||
{
|
||||
if (!playlist.IsMusicAvailable)
|
||||
return;
|
||||
|
||||
playlist.SetBackgroundMusic(string.IsNullOrEmpty(track) ? null : GetMusicTrack(track));
|
||||
}
|
||||
|
||||
MusicInfo GetMusicTrack(string track)
|
||||
{
|
||||
var music = world.Map.Rules.Music;
|
||||
if (music.ContainsKey(track))
|
||||
return music[track];
|
||||
|
||||
Log.Write("lua", "Missing music track: " + track);
|
||||
return null;
|
||||
}
|
||||
|
||||
[Desc("Stop the current song.")]
|
||||
public void StopMusic()
|
||||
{
|
||||
playlist.Stop();
|
||||
}
|
||||
|
||||
[Desc("Play a video fullscreen. File name has to include the file extension.")]
|
||||
public void PlayMovieFullscreen(string videoFileName, [ScriptEmmyTypeOverride("fun()")] LuaFunction onPlayComplete = null)
|
||||
{
|
||||
var onComplete = WrapOnPlayComplete(onPlayComplete);
|
||||
Media.PlayFMVFullscreen(world, videoFileName, onComplete);
|
||||
}
|
||||
|
||||
[Desc("Play a video in the radar window. File name has to include the file extension.")]
|
||||
public void PlayMovieInRadar(string videoFileName, [ScriptEmmyTypeOverride("fun()")] LuaFunction onPlayComplete = null)
|
||||
{
|
||||
var onComplete = WrapOnPlayComplete(onPlayComplete);
|
||||
Media.PlayFMVInRadar(videoFileName, onComplete);
|
||||
}
|
||||
|
||||
[Desc("Display a text message to all players.")]
|
||||
public void DisplayMessage(string text, string prefix = "Mission", Color? color = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
var c = color ?? Color.White;
|
||||
TextNotificationsManager.AddMissionLine(prefix, text, c);
|
||||
}
|
||||
|
||||
[Desc("Display a text message only to this player.")]
|
||||
public void DisplayMessageToPlayer(Player player, string text, string prefix = "Mission", Color? color = null)
|
||||
{
|
||||
if (world.LocalPlayer != player)
|
||||
return;
|
||||
|
||||
DisplayMessage(text, prefix, color);
|
||||
}
|
||||
|
||||
[Desc("Display a system message to the player. If 'prefix' is nil the default system prefix is used.")]
|
||||
public void DisplaySystemMessage(string text, string prefix = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
if (string.IsNullOrEmpty(prefix))
|
||||
TextNotificationsManager.AddSystemLine(text);
|
||||
else
|
||||
TextNotificationsManager.AddSystemLine(prefix, text);
|
||||
}
|
||||
|
||||
[Desc("Displays a debug message to the player, if \"Show Map Debug Messages\" is checked in the settings.")]
|
||||
public void Debug(string format)
|
||||
{
|
||||
if (string.IsNullOrEmpty(format) || !Game.Settings.Debug.LuaDebug)
|
||||
return;
|
||||
|
||||
TextNotificationsManager.Debug(format);
|
||||
}
|
||||
|
||||
[Desc("Display a text message at the specified location.")]
|
||||
public void FloatingText(string text, WPos position, int duration = 30, Color? color = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || !world.Map.Contains(world.Map.CellContaining(position)))
|
||||
return;
|
||||
|
||||
var c = color ?? Color.White;
|
||||
world.AddFrameEndTask(w => w.Add(new FloatingText(position, c, text, duration)));
|
||||
}
|
||||
|
||||
Action WrapOnPlayComplete(LuaFunction onPlayComplete)
|
||||
{
|
||||
if (onPlayComplete != null)
|
||||
{
|
||||
var f = (LuaFunction)onPlayComplete.CopyReference();
|
||||
return () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (f)
|
||||
f.Call().Dispose();
|
||||
}
|
||||
catch (LuaException e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
return () => { };
|
||||
}
|
||||
}
|
||||
}
|
||||
36
OpenRA.Mods.Common/Scripting/Global/PlayerGlobal.cs
Normal file
36
OpenRA.Mods.Common/Scripting/Global/PlayerGlobal.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.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 Player[] GetPlayers([ScriptEmmyTypeOverride("fun(p: player):boolean")] LuaFunction filter)
|
||||
{
|
||||
return FilteredObjects(Context.World.Players, filter).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
OpenRA.Mods.Common/Scripting/Global/RadarGlobal.cs
Normal file
35
OpenRA.Mods.Common/Scripting/Global/RadarGlobal.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Radar")]
|
||||
public class RadarGlobal : ScriptGlobal
|
||||
{
|
||||
readonly RadarPings radarPings;
|
||||
|
||||
public RadarGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
radarPings = context.World.WorldActor.TraitOrDefault<RadarPings>();
|
||||
}
|
||||
|
||||
[Desc("Creates a new radar ping that stays for the specified time at the specified WPos.")]
|
||||
public void Ping(Player player, WPos position, Color color, int duration = 750)
|
||||
{
|
||||
radarPings?.Add(() => player.World.RenderPlayer == player, position, color, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
204
OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs
Normal file
204
OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Mods.Common.Effects;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.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)
|
||||
{
|
||||
if (!Context.World.Map.Rules.Actors.TryGetValue(actorType, out var ai))
|
||||
throw new LuaException($"Unknown actor type '{actorType}'");
|
||||
|
||||
var initDict = new TypeDictionary
|
||||
{
|
||||
new OwnerInit(owner)
|
||||
};
|
||||
|
||||
if (entryLocation.HasValue)
|
||||
{
|
||||
initDict.Add(new LocationInit(entryLocation.Value));
|
||||
|
||||
var pi = ai.TraitInfoOrDefault<AircraftInfo>();
|
||||
if (pi != null)
|
||||
initDict.Add(new CenterPositionInit(owner.World.Map.CenterOfCell(entryLocation.Value) + new WVec(0, 0, pi.CruiseAltitude.Length)));
|
||||
}
|
||||
|
||||
if (entryLocation.HasValue && nextLocation.HasValue)
|
||||
{
|
||||
var facing = Context.World.Map.FacingBetween(CPos.Zero, CPos.Zero + (nextLocation.Value - entryLocation.Value), WAngle.Zero);
|
||||
initDict.Add(new FacingInit(facing));
|
||||
}
|
||||
|
||||
// The actor must be added to the world at the end of the tick.
|
||||
var a = Context.World.CreateActor(false, actorType, initDict);
|
||||
if (addToWorld)
|
||||
Context.World.AddFrameEndTask(w => w.Add(a));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static void Move(Actor actor, CPos dest)
|
||||
{
|
||||
var move = actor.TraitOrDefault<IMove>();
|
||||
if (move == null)
|
||||
return;
|
||||
|
||||
actor.QueueActivity(move.MoveTo(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(a: actor). " +
|
||||
"Returns a table containing the deployed units.")]
|
||||
public Actor[] Reinforce(Player owner, string[] actorTypes, CPos[] entryPath, int interval = 25,
|
||||
[ScriptEmmyTypeOverride("fun(a: actor)")] LuaFunction actionFunc = null)
|
||||
{
|
||||
var actors = new List<Actor>();
|
||||
for (var i = 0; i < actorTypes.Length; i++)
|
||||
{
|
||||
var af = actionFunc != null ? (LuaFunction)actionFunc.CopyReference() : null;
|
||||
var actor = CreateActor(owner, actorTypes[i], false, entryPath[0], entryPath.Length > 1 ? entryPath[1] : null);
|
||||
actors.Add(actor);
|
||||
|
||||
var actionDelay = i * interval;
|
||||
Activity queuedActivity = null;
|
||||
if (af != null)
|
||||
{
|
||||
queuedActivity = new CallFunc(() =>
|
||||
{
|
||||
using (af)
|
||||
using (var a = actor.ToLuaValue(Context))
|
||||
af.Call(a);
|
||||
});
|
||||
}
|
||||
|
||||
// We need to exclude the spawn location from the movement path
|
||||
var path = entryPath.Skip(1).ToArray();
|
||||
|
||||
Context.World.AddFrameEndTask(w => w.Add(new SpawnActorEffect(actor, actionDelay, path, queuedActivity)));
|
||||
}
|
||||
|
||||
return actors.ToArray();
|
||||
}
|
||||
|
||||
[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(transport: actor, cargo: actor[]). exitFunc will be called as exitFunc(transport: actor). " +
|
||||
"dropRange determines how many cells away the transport will try to land " +
|
||||
"if the actual destination is blocked (if the transport is an aircraft). " +
|
||||
"Returns a table in which the first value is the transport, " +
|
||||
"and the second a table containing the deployed units.")]
|
||||
[return: ScriptEmmyTypeOverride("{ [1]: actor, [2]: actor[] }")]
|
||||
public LuaTable ReinforceWithTransport(Player owner, string actorType,
|
||||
[ScriptEmmyTypeOverride("string[]|nil")] string[] cargoTypes,
|
||||
CPos[] entryPath, CPos[] exitPath = null,
|
||||
[ScriptEmmyTypeOverride("fun(transport: actor, cargo: actor[])")] LuaFunction actionFunc = null,
|
||||
[ScriptEmmyTypeOverride("fun(transport: actor)")] LuaFunction exitFunc = null,
|
||||
int dropRange = 3)
|
||||
{
|
||||
var transport = CreateActor(owner, actorType, true, entryPath[0], entryPath.Length > 1 ? entryPath[1] : null);
|
||||
var cargo = transport.TraitOrDefault<Cargo>();
|
||||
|
||||
var passengers = new List<Actor>();
|
||||
if (cargo != null && cargoTypes != null)
|
||||
{
|
||||
foreach (var cargoType in cargoTypes)
|
||||
{
|
||||
var passenger = CreateActor(owner, cargoType, false, entryPath[0]);
|
||||
passengers.Add(passenger);
|
||||
cargo.Load(transport, passenger);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 1; i < entryPath.Length; i++)
|
||||
Move(transport, entryPath[i]);
|
||||
|
||||
if (actionFunc != null)
|
||||
{
|
||||
var af = (LuaFunction)actionFunc.CopyReference();
|
||||
transport.QueueActivity(new CallFunc(() =>
|
||||
{
|
||||
using (af)
|
||||
using (LuaValue t = transport.ToLuaValue(Context), p = passengers.ToArray().ToLuaValue(Context))
|
||||
af.Call(t, p);
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
var aircraft = transport.TraitOrDefault<Aircraft>();
|
||||
|
||||
// Scripted cargo aircraft must turn to default position before unloading.
|
||||
// TODO: pass facing through UnloadCargo instead.
|
||||
if (aircraft != null)
|
||||
transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, entryPath[^1]), WDist.FromCells(dropRange)));
|
||||
|
||||
if (cargo != null)
|
||||
transport.QueueActivity(new UnloadCargo(transport, WDist.FromCells(dropRange)));
|
||||
}
|
||||
|
||||
if (exitFunc != null)
|
||||
{
|
||||
var ef = (LuaFunction)exitFunc.CopyReference();
|
||||
transport.QueueActivity(new CallFunc(() =>
|
||||
{
|
||||
using (ef)
|
||||
using (var t = transport.ToLuaValue(Context))
|
||||
ef.Call(t);
|
||||
}));
|
||||
}
|
||||
else if (exitPath != null)
|
||||
{
|
||||
foreach (var wpt in exitPath)
|
||||
Move(transport, wpt);
|
||||
|
||||
transport.QueueActivity(new RemoveSelf());
|
||||
}
|
||||
|
||||
var ret = Context.CreateTable();
|
||||
using (LuaValue
|
||||
tKey = 1,
|
||||
tValue = transport.ToLuaValue(Context),
|
||||
pKey = 2,
|
||||
pValue = passengers.ToArray().ToLuaValue(Context))
|
||||
{
|
||||
ret.Add(tKey, tValue);
|
||||
ret.Add(pKey, pValue);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
588
OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs
Normal file
588
OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs
Normal file
@@ -0,0 +1,588 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Trigger")]
|
||||
public class TriggerGlobal : ScriptGlobal
|
||||
{
|
||||
public TriggerGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
public static ScriptTriggers GetScriptTriggers(Actor actor)
|
||||
{
|
||||
var events = actor.TraitOrDefault<ScriptTriggers>();
|
||||
if (events == null)
|
||||
throw new LuaException($"Actor '{actor.Info.Name}' requires the ScriptTriggers trait before attaching a trigger");
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
[Desc("Call a function after a specified delay. The callback function will be called as func().")]
|
||||
public void AfterDelay(int delay, [ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
var f = (LuaFunction)func.CopyReference();
|
||||
void DoCall()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (f)
|
||||
f.Call().Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
Context.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, DoCall)));
|
||||
}
|
||||
|
||||
[Desc("Call a function for each passenger when it enters a transport. " +
|
||||
"The callback function will be called as func(transport: actor, passenger: actor).")]
|
||||
public void OnPassengerEntered(Actor actor, [ScriptEmmyTypeOverride("fun(transport: actor, passenger: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnPassengerEntered, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function for each passenger when it exits a transport. " +
|
||||
"The callback function will be called as func(transport: actor, passenger: actor).")]
|
||||
public void OnPassengerExited(Actor actor, [ScriptEmmyTypeOverride("fun(transport: actor, passenger: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnPassengerExited, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function each tick that the actor is idle. " +
|
||||
"The callback function will be called as func(self: actor).")]
|
||||
public void OnIdle(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnIdle, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when the actor is damaged. " +
|
||||
"Repairs or other negative damage can activate this trigger. The callback " +
|
||||
"function will be called as func(self: actor, attacker: actor, damage: integer).")]
|
||||
public void OnDamaged(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor, attacker: actor, damage: integer)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnDamaged, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when the actor is killed. The callback " +
|
||||
"function will be called as func(self: actor, killer: actor).")]
|
||||
public void OnKilled(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor, killer: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnKilled, 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(Actor[] actors, [ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
if (actors == null)
|
||||
throw new NullReferenceException(nameof(actors));
|
||||
|
||||
var group = actors.ToList();
|
||||
var f = (LuaFunction)func.CopyReference();
|
||||
void OnMemberKilled(Actor m)
|
||||
{
|
||||
try
|
||||
{
|
||||
group.Remove(m);
|
||||
if (group.Count == 0)
|
||||
using (f)
|
||||
f.Call();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var a in group)
|
||||
GetScriptTriggers(a).OnKilledInternal += OnMemberKilled;
|
||||
}
|
||||
|
||||
[Desc("Call a function when one of the actors in a group is killed. " +
|
||||
"This trigger is only called once. The callback " +
|
||||
"function will be called as func(killed: actor).")]
|
||||
public void OnAnyKilled(Actor[] actors, [ScriptEmmyTypeOverride("fun(killed: actor)")] LuaFunction func)
|
||||
{
|
||||
var called = false;
|
||||
var f = (LuaFunction)func.CopyReference();
|
||||
void OnMemberKilled(Actor m)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (called)
|
||||
return;
|
||||
|
||||
using (f)
|
||||
using (var killed = m.ToLuaValue(Context))
|
||||
f.Call(killed).Dispose();
|
||||
|
||||
called = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (actors == null)
|
||||
throw new NullReferenceException(nameof(actors));
|
||||
|
||||
foreach (var a in actors)
|
||||
GetScriptTriggers(a).OnKilledInternal += OnMemberKilled;
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor produces another actor. " +
|
||||
"The callback function will be called as func(producer: actor, produced: actor).")]
|
||||
public void OnProduction(Actor actor, [ScriptEmmyTypeOverride("fun(producer: actor, produced: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnProduction, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when any actor produces another actor. The callback " +
|
||||
"function will be called as func(producer: actor, produced: actor, productionType: string).")]
|
||||
public void OnAnyProduction([ScriptEmmyTypeOverride("fun(producer: actor, produced: actor, productionType: string)")] LuaFunction func)
|
||||
{
|
||||
GetScriptTriggers(Context.World.WorldActor).RegisterCallback(Trigger.OnOtherProduction, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this player completes all primary objectives. " +
|
||||
"The callback function will be called as func(p: player).")]
|
||||
public void OnPlayerWon(Player player, [ScriptEmmyTypeOverride("fun(p: player)")] LuaFunction func)
|
||||
{
|
||||
if (player == null)
|
||||
throw new NullReferenceException(nameof(player));
|
||||
|
||||
GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnPlayerWon, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this player fails any primary objective. " +
|
||||
"The callback function will be called as func(p: player).")]
|
||||
public void OnPlayerLost(Player player, [ScriptEmmyTypeOverride("fun(p: player)")] LuaFunction func)
|
||||
{
|
||||
if (player == null)
|
||||
throw new NullReferenceException(nameof(player));
|
||||
|
||||
GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnPlayerLost, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this player is assigned a new objective. " +
|
||||
"The callback function will be called as func(p: player, objectiveId: integer).")]
|
||||
public void OnObjectiveAdded(Player player, [ScriptEmmyTypeOverride("fun(p: player, objectiveId: integer)")] LuaFunction func)
|
||||
{
|
||||
if (player == null)
|
||||
throw new NullReferenceException(nameof(player));
|
||||
|
||||
GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnObjectiveAdded, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this player completes an objective. " +
|
||||
"The callback function will be called as func(p: player, objectiveId: integer).")]
|
||||
public void OnObjectiveCompleted(Player player, [ScriptEmmyTypeOverride("fun(p: player, objectiveId: integer)")] LuaFunction func)
|
||||
{
|
||||
if (player == null)
|
||||
throw new NullReferenceException(nameof(player));
|
||||
|
||||
GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnObjectiveCompleted, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this player fails an objective. " +
|
||||
"The callback function will be called as func(p: player, objectiveId: integer).")]
|
||||
public void OnObjectiveFailed(Player player, [ScriptEmmyTypeOverride("fun(p: player, objectiveId: integer)")] LuaFunction func)
|
||||
{
|
||||
if (player == null)
|
||||
throw new NullReferenceException(nameof(player));
|
||||
|
||||
GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnObjectiveFailed, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this player places a building. " +
|
||||
"The callback function will be called as func(p: player, placed: actor).")]
|
||||
public void OnBuildingPlaced(Player player, [ScriptEmmyTypeOverride("fun(p: player, placed: actor)")] LuaFunction func)
|
||||
{
|
||||
if (player == null)
|
||||
throw new NullReferenceException(nameof(player));
|
||||
|
||||
GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnBuildingPlaced, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor is added to the world. " +
|
||||
"The callback function will be called as func(self: actor).")]
|
||||
public void OnAddedToWorld(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).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(self: actor).")]
|
||||
public void OnRemovedFromWorld(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnRemovedFromWorld, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when all of the actors in a group have been removed from the world. " +
|
||||
"The callback function will be called as func().")]
|
||||
public void OnAllRemovedFromWorld(Actor[] actors, [ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
if (actors == null)
|
||||
throw new NullReferenceException(nameof(actors));
|
||||
|
||||
var group = actors.ToList();
|
||||
|
||||
var f = (LuaFunction)func.CopyReference();
|
||||
void OnMemberRemoved(Actor m)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!group.Remove(m))
|
||||
return;
|
||||
|
||||
if (group.Count == 0)
|
||||
{
|
||||
// Functions can only be .Call()ed once, so operate on a copy so we can reuse it later
|
||||
var temp = (LuaFunction)f.CopyReference();
|
||||
using (temp)
|
||||
temp.Call().Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
void OnMemberAdded(Actor m)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!actors.Contains(m) || group.Contains(m))
|
||||
return;
|
||||
|
||||
group.Add(m);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var a in group)
|
||||
{
|
||||
GetScriptTriggers(a).OnRemovedInternal += OnMemberRemoved;
|
||||
GetScriptTriggers(a).OnAddedInternal += OnMemberAdded;
|
||||
}
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor is captured. The callback function " +
|
||||
"will be called as func(self: actor, captor: actor, oldOwner: player, newOwner: player).")]
|
||||
public void OnCapture(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor, captor: actor, oldOwner: player, newOwner: player)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnCapture, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor is killed or captured. " +
|
||||
"This trigger is only called once. " +
|
||||
"The callback function will be called as func().")]
|
||||
public void OnKilledOrCaptured(Actor actor, [ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
var called = false;
|
||||
|
||||
var f = (LuaFunction)func.CopyReference();
|
||||
void OnKilledOrCaptured(Actor m)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (called)
|
||||
return;
|
||||
|
||||
using (f)
|
||||
f.Call().Dispose();
|
||||
|
||||
called = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).OnCapturedInternal += OnKilledOrCaptured;
|
||||
GetScriptTriggers(actor).OnKilledInternal += OnKilledOrCaptured;
|
||||
}
|
||||
|
||||
[Desc("Call a function when all of the actors in a group have been killed or captured. " +
|
||||
"This trigger is only called once. " +
|
||||
"The callback function will be called as func().")]
|
||||
public void OnAllKilledOrCaptured(Actor[] actors, [ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
if (actors == null)
|
||||
throw new NullReferenceException(nameof(actors));
|
||||
|
||||
var group = actors.ToList();
|
||||
|
||||
var f = (LuaFunction)func.CopyReference();
|
||||
void OnMemberKilledOrCaptured(Actor m)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!group.Remove(m))
|
||||
return;
|
||||
|
||||
if (group.Count == 0)
|
||||
using (f)
|
||||
f.Call().Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var a in group)
|
||||
{
|
||||
GetScriptTriggers(a).OnCapturedInternal += OnMemberKilledOrCaptured;
|
||||
GetScriptTriggers(a).OnKilledInternal += OnMemberKilledOrCaptured;
|
||||
}
|
||||
}
|
||||
|
||||
[Desc("Call a function when a ground-based actor enters this cell footprint. " +
|
||||
"Returns the trigger ID for later removal using RemoveFootprintTrigger(id: integer). " +
|
||||
"The callback function will be called as func(a: actor, id: integer).")]
|
||||
public int OnEnteredFootprint(CPos[] cells, [ScriptEmmyTypeOverride("fun(a: actor, id: integer)")] LuaFunction func)
|
||||
{
|
||||
// We can't easily dispose onEntry, so we'll have to rely on finalization for it.
|
||||
var onEntry = (LuaFunction)func.CopyReference();
|
||||
var triggerId = 0;
|
||||
void InvokeEntry(Actor a)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var luaActor = a.ToLuaValue(Context))
|
||||
using (var id = triggerId.ToLuaValue(Context))
|
||||
onEntry.Call(luaActor, id).Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
triggerId = Context.World.ActorMap.AddCellTrigger(cells, InvokeEntry, null);
|
||||
|
||||
return triggerId;
|
||||
}
|
||||
|
||||
[Desc("Call a function when a ground-based actor leaves this cell footprint. " +
|
||||
"Returns the trigger ID for later removal using RemoveFootprintTrigger(id: integer). " +
|
||||
"The callback function will be called as func(a: actor, id: integer).")]
|
||||
public int OnExitedFootprint(CPos[] cells, [ScriptEmmyTypeOverride("fun(a: actor, id: integer)")] LuaFunction func)
|
||||
{
|
||||
// We can't easily dispose onExit, so we'll have to rely on finalization for it.
|
||||
var onExit = (LuaFunction)func.CopyReference();
|
||||
var triggerId = 0;
|
||||
void InvokeExit(Actor a)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var luaActor = a.ToLuaValue(Context))
|
||||
using (var id = triggerId.ToLuaValue(Context))
|
||||
onExit.Call(luaActor, id).Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
triggerId = Context.World.ActorMap.AddCellTrigger(cells, null, InvokeExit);
|
||||
|
||||
return triggerId;
|
||||
}
|
||||
|
||||
[Desc("Removes a previously created footprint trigger.")]
|
||||
public void RemoveFootprintTrigger(int id)
|
||||
{
|
||||
Context.World.ActorMap.RemoveCellTrigger(id);
|
||||
}
|
||||
|
||||
[Desc("Call a function when an actor enters this range. " +
|
||||
"Returns the trigger ID for later removal using RemoveProximityTrigger(id: integer). " +
|
||||
"The callback function will be called as func(a: actor, id: integer).")]
|
||||
public int OnEnteredProximityTrigger(WPos pos, WDist range, [ScriptEmmyTypeOverride("fun(a: actor, id: integer)")] LuaFunction func)
|
||||
{
|
||||
// We can't easily dispose onEntry, so we'll have to rely on finalization for it.
|
||||
var onEntry = (LuaFunction)func.CopyReference();
|
||||
var triggerId = 0;
|
||||
void InvokeEntry(Actor a)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var luaActor = a.ToLuaValue(Context))
|
||||
using (var id = triggerId.ToLuaValue(Context))
|
||||
onEntry.Call(luaActor, id).Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
triggerId = Context.World.ActorMap.AddProximityTrigger(pos, range, WDist.Zero, InvokeEntry, null);
|
||||
|
||||
return triggerId;
|
||||
}
|
||||
|
||||
[Desc("Call a function when an actor leaves this range. " +
|
||||
"Returns the trigger ID for later removal using RemoveProximityTrigger(id: integer). " +
|
||||
"The callback function will be called as func(a: actor, id: integer).")]
|
||||
public int OnExitedProximityTrigger(WPos pos, WDist range, [ScriptEmmyTypeOverride("fun(a: actor, id: integer)")] LuaFunction func)
|
||||
{
|
||||
// We can't easily dispose onExit, so we'll have to rely on finalization for it.
|
||||
var onExit = (LuaFunction)func.CopyReference();
|
||||
var triggerId = 0;
|
||||
void InvokeExit(Actor a)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var luaActor = a.ToLuaValue(Context))
|
||||
using (var id = triggerId.ToLuaValue(Context))
|
||||
onExit.Call(luaActor, id).Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
triggerId = Context.World.ActorMap.AddProximityTrigger(pos, range, WDist.Zero, null, InvokeExit);
|
||||
|
||||
return triggerId;
|
||||
}
|
||||
|
||||
[Desc("Removes a previously created proximity trigger.")]
|
||||
public void RemoveProximityTrigger(int id)
|
||||
{
|
||||
Context.World.ActorMap.RemoveProximityTrigger(id);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor is infiltrated. The callback function " +
|
||||
"will be called as func(self: actor, infiltrator: actor).")]
|
||||
public void OnInfiltrated(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor, infiltrator: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnInfiltrated, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor is discovered by an enemy or a player with a Neutral stance. " +
|
||||
"The callback function will be called as func(discovered: actor, discoverer: player). " +
|
||||
"The player actor needs the 'EnemyWatcher' trait. The actors to discover need the 'AnnounceOnSeen' trait.")]
|
||||
public void OnDiscovered(Actor actor, [ScriptEmmyTypeOverride("fun(discovered: actor, discoverer: player)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnDiscovered, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this player is discovered by an enemy or neutral player. " +
|
||||
"The callback function will be called as func(discovered: player, discoverer: player, discoveredActor: actor)." +
|
||||
"The player actor needs the 'EnemyWatcher' trait. The actors to discover need the 'AnnounceOnSeen' trait.")]
|
||||
public void OnPlayerDiscovered(
|
||||
Player discovered, [ScriptEmmyTypeOverride("fun(discovered: player, discoverer: player, discoveredActor: actor)")] LuaFunction func)
|
||||
{
|
||||
if (discovered == null)
|
||||
throw new NullReferenceException(nameof(discovered));
|
||||
|
||||
GetScriptTriggers(discovered.PlayerActor).RegisterCallback(Trigger.OnPlayerDiscovered, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor is sold. The callback function " +
|
||||
"will be called as func(self: actor).")]
|
||||
public void OnSold(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnSold, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when the game timer expires. The callback function will be called as func().")]
|
||||
public void OnTimerExpired([ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
GetScriptTriggers(Context.World.WorldActor).RegisterCallback(Trigger.OnTimerExpired, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Removes all triggers from this actor. " +
|
||||
"Note that the removal will only take effect at the end of a tick, " +
|
||||
"so you must not add new triggers at the same time that you are calling this function.")]
|
||||
public void ClearAll(Actor actor)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).ClearAll();
|
||||
}
|
||||
|
||||
[Desc("Removes the specified trigger from this actor. " +
|
||||
"Note that the removal will only take effect at the end of a tick, " +
|
||||
"so you must not add new triggers at the same time that you are calling this function.")]
|
||||
public void Clear(Actor actor, string triggerName)
|
||||
{
|
||||
var trigger = Enum.Parse<Trigger>(triggerName);
|
||||
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actor).Clear(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
OpenRA.Mods.Common/Scripting/Global/UserInterfaceGlobal.cs
Normal file
65
OpenRA.Mods.Common/Scripting/Global/UserInterfaceGlobal.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Widgets;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting.Global
|
||||
{
|
||||
[ScriptGlobal("UserInterface")]
|
||||
public class UserInterfaceGlobal : ScriptGlobal
|
||||
{
|
||||
public UserInterfaceGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
[Desc("Displays a text message at the top center of the screen.")]
|
||||
public void SetMissionText(string text, Color? color = null)
|
||||
{
|
||||
var luaLabel = Ui.Root.Get("INGAME_ROOT").Get<LabelWidget>("MISSION_TEXT");
|
||||
luaLabel.GetText = () => text;
|
||||
|
||||
var c = color ?? Color.White;
|
||||
luaLabel.GetColor = () => c;
|
||||
}
|
||||
|
||||
[Desc("Formats a language string for a given string key defined in the language files (*.ftl). " +
|
||||
"Args can be passed to be substituted into the resulting message.")]
|
||||
public string GetFluentMessage(string key, [ScriptEmmyTypeOverride("{ string: any }")] LuaTable args = null)
|
||||
{
|
||||
if (args != null)
|
||||
{
|
||||
var argumentDictionary = new object[args.Count * 2];
|
||||
var i = 0;
|
||||
foreach (var kv in args)
|
||||
{
|
||||
using (kv.Key)
|
||||
using (kv.Value)
|
||||
{
|
||||
if (!kv.Key.TryGetClrValue<string>(out var variable) || !kv.Value.TryGetClrValue<object>(out var value))
|
||||
throw new LuaException(
|
||||
"String arguments requires a table of [\"string\"]=value pairs. " +
|
||||
$"Received {kv.Key.WrappedClrType().Name},{kv.Value.WrappedClrType().Name}");
|
||||
|
||||
argumentDictionary[i++] = variable;
|
||||
argumentDictionary[i++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return FluentProvider.GetMessage(key, argumentDictionary);
|
||||
}
|
||||
|
||||
return FluentProvider.GetMessage(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
155
OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs
Normal file
155
OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Widgets;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA.Mods.Common.Scripting
|
||||
{
|
||||
[ScriptGlobal("Utils")]
|
||||
public class UtilsGlobal : ScriptGlobal
|
||||
{
|
||||
public UtilsGlobal(ScriptContext context)
|
||||
: base(context) { }
|
||||
|
||||
[Desc("Calls a function on every element in a collection.")]
|
||||
public void Do(
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] collection,
|
||||
[ScriptEmmyTypeOverride("fun(item: T)", "T")] LuaFunction func)
|
||||
{
|
||||
foreach (var c in collection)
|
||||
func.Call(c).Dispose();
|
||||
}
|
||||
|
||||
[Desc("Returns true if func returns true for any element in a collection.")]
|
||||
public bool Any(
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] collection,
|
||||
[ScriptEmmyTypeOverride("fun(item: T):boolean?", "T")] LuaFunction func)
|
||||
{
|
||||
foreach (var c in collection)
|
||||
using (var ret = func.Call(c))
|
||||
using (var result = ret.FirstOrDefault())
|
||||
if (result != null && result.ToBoolean())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Desc("Returns true if func returns true for all elements in a collection.")]
|
||||
public bool All(
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] collection,
|
||||
[ScriptEmmyTypeOverride("fun(item: T):boolean?", "T")] LuaFunction func)
|
||||
{
|
||||
foreach (var c in collection)
|
||||
using (var ret = func.Call(c))
|
||||
using (var result = ret.FirstOrDefault())
|
||||
if (result == null || !result.ToBoolean())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Desc("Returns the original collection filtered with the func.")]
|
||||
[return: ScriptEmmyTypeOverride("T[]", "T")]
|
||||
public LuaTable Where(
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] collection,
|
||||
[ScriptEmmyTypeOverride("fun(item: T):boolean?", "T")] LuaFunction func)
|
||||
{
|
||||
var t = Context.CreateTable();
|
||||
|
||||
foreach (var c in collection)
|
||||
using (var ret = func.Call(c))
|
||||
using (var result = ret.FirstOrDefault())
|
||||
if (result != null && result.ToBoolean())
|
||||
t.Add(t.Count + 1, c);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
[Desc("Returns the first n values from a collection.")]
|
||||
[return: ScriptEmmyTypeOverride("T[]", "T")]
|
||||
public LuaValue[] Take(
|
||||
int n,
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] source)
|
||||
{
|
||||
return source.Take(n).Select(v => v.CopyReference()).ToArray();
|
||||
}
|
||||
|
||||
[Desc("Skips over the first numElements members of a table and return the rest.")]
|
||||
[return: ScriptEmmyTypeOverride("T[]", "T")]
|
||||
public LuaTable Skip(
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaTable table,
|
||||
int numElements)
|
||||
{
|
||||
var t = Context.CreateTable();
|
||||
|
||||
for (var i = numElements; i <= table.Count; i++)
|
||||
using (LuaValue key = t.Count + 1, value = table[i])
|
||||
t.Add(key, value);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
[Desc("Concatenates two Lua tables into a single table.")]
|
||||
[return: ScriptEmmyTypeOverride("T[]", "T")]
|
||||
public LuaTable Concat(
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] firstCollection,
|
||||
[ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] secondCollection)
|
||||
{
|
||||
var t = Context.CreateTable();
|
||||
|
||||
foreach (var e in firstCollection)
|
||||
t.Add(t.Count + 1, e);
|
||||
|
||||
foreach (var e in secondCollection)
|
||||
t.Add(t.Count + 1, e);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
[Desc("Returns a random value from a collection.")]
|
||||
[return: ScriptEmmyTypeOverride("T", "T")]
|
||||
public LuaValue Random([ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] collection)
|
||||
{
|
||||
return collection.Random(Context.World.SharedRandom).CopyReference();
|
||||
}
|
||||
|
||||
[Desc("Returns the collection in a random order.")]
|
||||
[return: ScriptEmmyTypeOverride("T[]", "T")]
|
||||
public LuaValue[] Shuffle([ScriptEmmyTypeOverride("T[]", "T")] LuaValue[] collection)
|
||||
{
|
||||
return collection.Shuffle(Context.World.SharedRandom).ToArray();
|
||||
}
|
||||
|
||||
[Desc("Expands the given footprint one step along the coordinate axes, and (if requested) diagonals.")]
|
||||
public CPos[] ExpandFootprint(CPos[] footprint, bool allowDiagonal)
|
||||
{
|
||||
return Util.ExpandFootprint(footprint, allowDiagonal).ToArray();
|
||||
}
|
||||
|
||||
[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 ticks formatted to HH:MM:SS.")]
|
||||
public string FormatTime(int ticks, bool leadingMinuteZero = true)
|
||||
{
|
||||
return WidgetUtils.FormatTime(ticks, leadingMinuteZero, 40);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user