Add a new native-lua implementation.
This commit is contained in:
40
OpenRA.Game/Actor.cs
Executable file → Normal file
40
OpenRA.Game/Actor.cs
Executable file → Normal file
@@ -12,13 +12,16 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class Actor
|
||||
public class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
|
||||
{
|
||||
public readonly ActorInfo Info;
|
||||
|
||||
@@ -240,5 +243,40 @@ namespace OpenRA
|
||||
|
||||
health.Value.InflictDamage(this, attacker, health.Value.MaxHP, null, true);
|
||||
}
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
Lazy<ScriptActorInterface> luaInterface;
|
||||
public void OnScriptBind(ScriptContext context)
|
||||
{
|
||||
luaInterface = Exts.Lazy(() => new ScriptActorInterface(context, this));
|
||||
}
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue keyValue]
|
||||
{
|
||||
get { return luaInterface.Value[runtime, keyValue]; }
|
||||
set { luaInterface.Value[runtime, keyValue] = value; }
|
||||
}
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
Actor a, b;
|
||||
if (!left.TryGetClrValue<Actor>(out a) || !right.TryGetClrValue<Actor>(out b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public LuaValue ToString(LuaRuntime runtime)
|
||||
{
|
||||
return "Actor ({0})".F(this);
|
||||
}
|
||||
|
||||
public bool HasScriptProperty(string name)
|
||||
{
|
||||
return luaInterface.Value.ContainsKey(name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
/// <summary>
|
||||
/// Cell coordinate position in the world (coarse).
|
||||
/// </summary>
|
||||
public struct CPos
|
||||
public struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding
|
||||
{
|
||||
public readonly int X, Y;
|
||||
|
||||
@@ -60,6 +60,56 @@ namespace OpenRA
|
||||
|
||||
public override string ToString() { return "{0},{1}".F(X, Y); }
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
CVec b;
|
||||
if (!left.TryGetClrValue<CPos>(out a) || !right.TryGetClrValue<CVec>(out b))
|
||||
throw new LuaException("Attempted to call CPos.Add(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
}
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
CVec b;
|
||||
if (!left.TryGetClrValue<CPos>(out a) || !right.TryGetClrValue<CVec>(out b))
|
||||
throw new LuaException("Attempted to call CPos.Subtract(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a, b;
|
||||
if (!left.TryGetClrValue<CPos>(out a) || !right.TryGetClrValue<CPos>(out b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue key]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (key.ToString())
|
||||
{
|
||||
case "X": return X;
|
||||
case "Y": return Y;
|
||||
default: throw new LuaException("CPos does not define a member '{0}'".F(key));
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new LuaException("CPos is read-only. Use CPos.New to create a new value");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class RectangleExtensions
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
/// <summary>
|
||||
/// Cell coordinate vector (coarse).
|
||||
/// </summary>
|
||||
public struct CVec
|
||||
public struct CVec : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaUnaryMinusBinding, ILuaEqualityBinding, ILuaTableBinding
|
||||
{
|
||||
public readonly int X, Y;
|
||||
|
||||
@@ -82,5 +82,60 @@ namespace OpenRA
|
||||
new CVec(1, 0),
|
||||
new CVec(1, 1),
|
||||
};
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue<CVec>(out a) || !right.TryGetClrValue<CVec>(out b))
|
||||
throw new LuaException("Attempted to call CVec.Add(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
}
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue<CVec>(out a) || !right.TryGetClrValue<CVec>(out b))
|
||||
throw new LuaException("Attempted to call CVec.Subtract(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
|
||||
public LuaValue Minus(LuaRuntime runtime)
|
||||
{
|
||||
return new LuaCustomClrObject(-this);
|
||||
}
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue<CVec>(out a) || !right.TryGetClrValue<CVec>(out b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue key]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (key.ToString())
|
||||
{
|
||||
case "X": return X;
|
||||
case "Y": return Y;
|
||||
case "Facing": return Traits.Util.GetFacing(this, 0);
|
||||
default: throw new LuaException("CVec does not define a member '{0}'".F(key));
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new LuaException("WVec is read-only. Use CVec.New to create a new value");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,9 @@
|
||||
<Package>mono.nat</Package>
|
||||
<HintPath>..\thirdparty\Mono.Nat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Eluant">
|
||||
<HintPath>..\thirdparty\Eluant.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Tao.OpenAl, Version=1.1.0.1, Culture=neutral, PublicKeyToken=a7579dda88828311">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\thirdparty\Tao\Tao.OpenAl.dll</HintPath>
|
||||
@@ -236,6 +239,13 @@
|
||||
<Compile Include="Widgets\SpriteWidget.cs" />
|
||||
<Compile Include="Widgets\SpriteSequenceWidget.cs" />
|
||||
<Compile Include="Widgets\RGBASpriteWidget.cs" />
|
||||
<Compile Include="Scripting\ScriptContext.cs" />
|
||||
<Compile Include="Scripting\ScriptActorInterface.cs" />
|
||||
<Compile Include="Scripting\ScriptObjectWrapper.cs" />
|
||||
<Compile Include="Scripting\ScriptTypes.cs" />
|
||||
<Compile Include="Scripting\ScriptMemberWrapper.cs" />
|
||||
<Compile Include="Scripting\ScriptMemberExts.cs" />
|
||||
<Compile Include="Scripting\ScriptPlayerInterface.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
||||
@@ -359,4 +369,7 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup>
|
||||
<Folder Include="Scripting\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* 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,
|
||||
@@ -8,12 +8,16 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
@@ -21,7 +25,7 @@ namespace OpenRA
|
||||
public enum PowerState { Normal, Low, Critical };
|
||||
public enum WinState { Won, Lost, Undefined };
|
||||
|
||||
public class Player
|
||||
public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
|
||||
{
|
||||
public Actor PlayerActor;
|
||||
public WinState WinState = WinState.Undefined;
|
||||
@@ -106,5 +110,35 @@ namespace OpenRA
|
||||
// Observers are considered as allies
|
||||
return p == null || Stances[p] == Stance.Ally;
|
||||
}
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
Lazy<ScriptPlayerInterface> luaInterface;
|
||||
public void OnScriptBind(ScriptContext context)
|
||||
{
|
||||
luaInterface = Exts.Lazy(() => new ScriptPlayerInterface(context, this));
|
||||
}
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue keyValue]
|
||||
{
|
||||
get { return luaInterface.Value[runtime, keyValue]; }
|
||||
set { luaInterface.Value[runtime, keyValue] = value; }
|
||||
}
|
||||
|
||||
public LuaValue Equals (LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
Player a, b;
|
||||
if (!left.TryGetClrValue<Player>(out a) || !right.TryGetClrValue<Player>(out b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public LuaValue ToString(LuaRuntime runtime)
|
||||
{
|
||||
return "Player ({0})".F(PlayerName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
45
OpenRA.Game/Scripting/ScriptActorInterface.cs
Normal file
45
OpenRA.Game/Scripting/ScriptActorInterface.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
#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 System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public class ScriptActorInterface : ScriptObjectWrapper
|
||||
{
|
||||
readonly Actor actor;
|
||||
|
||||
protected override string DuplicateKeyError(string memberName) { return "Actor '{0}' defines the command '{1}' on multiple traits".F(actor.Info.Name, memberName); }
|
||||
protected override string MemberNotFoundError(string memberName) { return "Actor '{0}' does not define a property '{1}'".F(actor.Info.Name, memberName); }
|
||||
|
||||
public ScriptActorInterface(ScriptContext context, Actor actor)
|
||||
: base(context)
|
||||
{
|
||||
this.actor = actor;
|
||||
|
||||
var args = new [] { actor };
|
||||
var objects = context.ActorCommands[actor.Info].Select(cg =>
|
||||
{
|
||||
var groupCtor = cg.GetConstructor(new Type[] { typeof(Actor) });
|
||||
return groupCtor.Invoke(args);
|
||||
});
|
||||
|
||||
Bind(objects);
|
||||
}
|
||||
}
|
||||
}
|
||||
233
OpenRA.Game/Scripting/ScriptContext.cs
Normal file
233
OpenRA.Game/Scripting/ScriptContext.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
#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.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
// Tag interfaces specifying the type of bindings to create
|
||||
public interface IScriptBindable { }
|
||||
|
||||
// For objects that need the context to create their bindings
|
||||
public interface IScriptNotifyBind
|
||||
{
|
||||
void OnScriptBind(ScriptContext context);
|
||||
}
|
||||
|
||||
// For traitinfos that provide actor / player commands
|
||||
public class ScriptPropertyGroupAttribute : Attribute
|
||||
{
|
||||
public readonly string Category;
|
||||
public ScriptPropertyGroupAttribute(string category) { Category = category; }
|
||||
}
|
||||
|
||||
public class ScriptActorPropertyActivityAttribute : Attribute { }
|
||||
|
||||
public abstract class ScriptActorProperties
|
||||
{
|
||||
protected readonly Actor self;
|
||||
public ScriptActorProperties(Actor self) { this.self = self; }
|
||||
}
|
||||
|
||||
public abstract class ScriptPlayerProperties
|
||||
{
|
||||
protected readonly Player player;
|
||||
public ScriptPlayerProperties(Player player) { this.player = player; }
|
||||
}
|
||||
|
||||
// For global-level bindings
|
||||
public abstract class ScriptGlobal : ScriptObjectWrapper
|
||||
{
|
||||
protected override string DuplicateKeyError(string memberName) { return "Table '{0}' defines multiple members '{1}'".F(Name, memberName); }
|
||||
protected override string MemberNotFoundError(string memberName) { return "Table '{0}' does not define a property '{1}'".F(Name, memberName); }
|
||||
|
||||
public readonly string Name;
|
||||
public ScriptGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
// The 'this.' resolves the actual (subclass) type
|
||||
var type = this.GetType();
|
||||
var names = type.GetCustomAttributes<ScriptGlobalAttribute>(true);
|
||||
if (names.Count() != 1)
|
||||
throw new InvalidOperationException("[LuaGlobal] attribute not found for global table '{0}'".F(type));
|
||||
|
||||
Name = names.First().Name;
|
||||
Bind(new [] { this });
|
||||
}
|
||||
}
|
||||
|
||||
public class ScriptGlobalAttribute : Attribute
|
||||
{
|
||||
public readonly string Name;
|
||||
public ScriptGlobalAttribute(string name) { Name = name; }
|
||||
}
|
||||
|
||||
public class ScriptContext
|
||||
{
|
||||
public World World { get; private set; }
|
||||
public WorldRenderer WorldRenderer { get; private set; }
|
||||
|
||||
readonly MemoryConstrainedLuaRuntime runtime;
|
||||
readonly LuaFunction tick;
|
||||
|
||||
// Restrict user scripts (excluding system libraries) to 50 MB of memory use
|
||||
const int MaxUserScriptMemory = 50 * 1024 * 1024;
|
||||
|
||||
// Restrict the number of instructions that will be run per map function call
|
||||
const int MaxUserScriptInstructions = 1000000;
|
||||
|
||||
readonly Type[] knownActorCommands;
|
||||
public readonly Cache<ActorInfo, Type[]> ActorCommands;
|
||||
public readonly Type[] PlayerCommands;
|
||||
|
||||
public ScriptContext(World world, WorldRenderer worldRenderer,
|
||||
IEnumerable<string> scripts)
|
||||
{
|
||||
runtime = new MemoryConstrainedLuaRuntime();
|
||||
|
||||
World = world;
|
||||
WorldRenderer = worldRenderer;
|
||||
knownActorCommands = Game.modData.ObjectCreator
|
||||
.GetTypesImplementing<ScriptActorProperties>()
|
||||
.ToArray();
|
||||
|
||||
ActorCommands = new Cache<ActorInfo, Type[]>(FilterActorCommands);
|
||||
PlayerCommands = Game.modData.ObjectCreator
|
||||
.GetTypesImplementing<ScriptPlayerProperties>()
|
||||
.ToArray();
|
||||
|
||||
runtime.DoBuffer(GlobalFileSystem.Open(Path.Combine("lua", "scriptwrapper.lua")).ReadAllText(), "scriptwrapper.lua").Dispose();
|
||||
tick = (LuaFunction)runtime.Globals["Tick"];
|
||||
|
||||
// Register globals
|
||||
using (var fn = runtime.CreateFunctionFromDelegate((Action<string>)FatalError))
|
||||
runtime.Globals["FatalError"] = fn;
|
||||
|
||||
runtime.Globals["MaxUserScriptInstructions"] = MaxUserScriptInstructions;
|
||||
|
||||
using (var registerGlobal = (LuaFunction)runtime.Globals["RegisterSandboxedGlobal"])
|
||||
{
|
||||
using (var fn = runtime.CreateFunctionFromDelegate((Action<string>)Console.WriteLine))
|
||||
registerGlobal.Call("print", fn).Dispose();
|
||||
|
||||
// Register global tables
|
||||
var bindings = Game.modData.ObjectCreator.GetTypesImplementing<ScriptGlobal>();
|
||||
foreach (var b in bindings)
|
||||
{
|
||||
var ctor = b.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c =>
|
||||
{
|
||||
var p = c.GetParameters();
|
||||
return p.Length == 1 && p.First().ParameterType == typeof(ScriptContext);
|
||||
});
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException("{0} must define a constructor that takes a ScriptContext context parameter".F(b.Name));
|
||||
|
||||
var binding = (ScriptGlobal)ctor.Invoke(new [] { this });
|
||||
using (var obj = binding.ToLuaValue(this))
|
||||
registerGlobal.Call(binding.Name, obj).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// System functions do not count towards the memory limit
|
||||
runtime.MaxMemoryUse = runtime.MemoryUse + MaxUserScriptMemory;
|
||||
|
||||
using (var loadScript = (LuaFunction)runtime.Globals["ExecuteSandboxedScript"])
|
||||
{
|
||||
foreach (var s in scripts)
|
||||
loadScript.Call(s, GlobalFileSystem.Open(s).ReadAllText()).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
bool error;
|
||||
public void FatalError(string message)
|
||||
{
|
||||
Console.WriteLine("Fatal Lua Error: {0}", message);
|
||||
error = true;
|
||||
}
|
||||
|
||||
public void RegisterMapActor(string name, Actor a)
|
||||
{
|
||||
using (var registerGlobal = (LuaFunction)runtime.Globals["RegisterSandboxedGlobal"])
|
||||
{
|
||||
if (runtime.Globals.ContainsKey(name))
|
||||
throw new LuaException("The global name '{0}' is reserved, and may not be used by a map actor".F(name));
|
||||
|
||||
using (var obj = a.ToLuaValue(this))
|
||||
registerGlobal.Call(name, obj).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void WorldLoaded()
|
||||
{
|
||||
if (error)
|
||||
return;
|
||||
|
||||
using (var worldLoaded = (LuaFunction)runtime.Globals["WorldLoaded"])
|
||||
worldLoaded.Call().Dispose();
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (error)
|
||||
return;
|
||||
|
||||
using (new PerfSample("tick_lua"))
|
||||
tick.Call().Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (runtime == null)
|
||||
return;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
runtime.Dispose();
|
||||
}
|
||||
|
||||
~ScriptContext()
|
||||
{
|
||||
if (runtime != null)
|
||||
Game.RunAfterTick(Dispose);
|
||||
}
|
||||
|
||||
static Type[] ExtractRequiredTypes(Type t)
|
||||
{
|
||||
// Returns the inner types of all the Requires<T> interfaces on this type
|
||||
var outer = t.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Requires<>));
|
||||
|
||||
return outer.SelectMany(i => i.GetGenericArguments()).ToArray();
|
||||
}
|
||||
|
||||
static readonly object[] NoArguments = new object[0];
|
||||
Type[] FilterActorCommands(ActorInfo ai)
|
||||
{
|
||||
var method = typeof(TypeDictionary).GetMethod("Contains");
|
||||
return knownActorCommands.Where(c => ExtractRequiredTypes(c)
|
||||
.All(t => (bool)method.MakeGenericMethod(t).Invoke(ai.Traits, NoArguments)))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public LuaTable CreateTable() { return runtime.CreateTable(); }
|
||||
}
|
||||
}
|
||||
74
OpenRA.Game/Scripting/ScriptMemberExts.cs
Normal file
74
OpenRA.Game/Scripting/ScriptMemberExts.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
#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 System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public static class ScriptMemberExts
|
||||
{
|
||||
static readonly Dictionary<string, string> LuaTypeNameReplacements = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Void", "void" },
|
||||
{ "Int32", "int" },
|
||||
{ "String", "string" },
|
||||
{ "Boolean", "bool" }
|
||||
};
|
||||
|
||||
public static string LuaDocString(this Type t)
|
||||
{
|
||||
string ret;
|
||||
if (!LuaTypeNameReplacements.TryGetValue(t.Name, out ret))
|
||||
ret = t.Name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static string LuaDocString(this ParameterInfo pi)
|
||||
{
|
||||
var ret = "{0} {1}".F(pi.ParameterType.LuaDocString(), pi.Name);
|
||||
if (pi.IsOptional)
|
||||
ret += " = {0}".F(pi.DefaultValue);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static string LuaDocString(this MemberInfo mi)
|
||||
{
|
||||
if (mi is MethodInfo)
|
||||
{
|
||||
var methodInfo = mi as MethodInfo;
|
||||
var parameters = methodInfo.GetParameters().Select(pi => pi.LuaDocString());
|
||||
return "{0} {1}({2})".F(methodInfo.ReturnType.LuaDocString(), mi.Name, parameters.JoinWith(", "));
|
||||
}
|
||||
|
||||
if (mi is PropertyInfo)
|
||||
{
|
||||
var pi = mi as PropertyInfo;
|
||||
var types = new List<string>();
|
||||
if (pi.GetGetMethod() != null)
|
||||
types.Add("get;");
|
||||
if (pi.GetSetMethod() != null)
|
||||
types.Add("set;");
|
||||
|
||||
return "{0} {1} {{ {2} }}".F(pi.PropertyType.LuaDocString(), mi.Name, types.JoinWith(" "));
|
||||
}
|
||||
|
||||
return "Unknown field: {0}".F(mi.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
OpenRA.Game/Scripting/ScriptMemberWrapper.cs
Normal file
127
OpenRA.Game/Scripting/ScriptMemberWrapper.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
#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 System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public class ScriptMemberWrapper
|
||||
{
|
||||
readonly ScriptContext context;
|
||||
public readonly object Target;
|
||||
public readonly MemberInfo Member;
|
||||
|
||||
public readonly bool IsMethod;
|
||||
public readonly bool IsGetProperty;
|
||||
public readonly bool IsSetProperty;
|
||||
|
||||
public ScriptMemberWrapper(ScriptContext context, object target, MemberInfo mi)
|
||||
{
|
||||
this.context = context;
|
||||
Target = target;
|
||||
Member = mi;
|
||||
|
||||
var property = mi as PropertyInfo;
|
||||
if (property != null)
|
||||
{
|
||||
IsGetProperty = property.GetGetMethod() != null;
|
||||
IsSetProperty = property.GetSetMethod() != null;
|
||||
}
|
||||
else
|
||||
IsMethod = true;
|
||||
}
|
||||
|
||||
LuaValue Invoke(LuaVararg args)
|
||||
{
|
||||
if (!IsMethod)
|
||||
throw new LuaException("Trying to invoke a ScriptMemberWrapper that isn't a method!");
|
||||
|
||||
var mi = Member as MethodInfo;
|
||||
var pi = mi.GetParameters();
|
||||
|
||||
object[] clrArgs = new object[pi.Length];
|
||||
var argCount = args.Count;
|
||||
for (var i = 0; i < pi.Length; i++)
|
||||
{
|
||||
if (i >= argCount)
|
||||
{
|
||||
if (!pi[i].IsOptional)
|
||||
throw new LuaException("Argument '{0}' of '{1}' is not optional.".F(pi[i].LuaDocString(), Member.LuaDocString()));
|
||||
|
||||
clrArgs[i] = pi[i].DefaultValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!args[i].TryGetClrValue(pi[i].ParameterType, out clrArgs[i]))
|
||||
throw new LuaException("Unable to convert parameter {0} to {1}".F(i, pi[i].ParameterType.Name));
|
||||
}
|
||||
|
||||
var ret = (Member as MethodInfo).Invoke(Target, clrArgs);
|
||||
return ret.ToLuaValue(context);
|
||||
}
|
||||
|
||||
public LuaValue Get(LuaRuntime runtime)
|
||||
{
|
||||
if (IsMethod)
|
||||
return runtime.CreateFunctionFromDelegate((Func<LuaVararg, LuaValue>)Invoke);
|
||||
|
||||
if (IsGetProperty)
|
||||
{
|
||||
var pi = Member as PropertyInfo;
|
||||
return pi.GetValue(Target, null).ToLuaValue(context);
|
||||
}
|
||||
|
||||
throw new LuaException("The property '{0}' is write-only".F(Member.Name));
|
||||
}
|
||||
|
||||
public void Set(LuaRuntime runtime, LuaValue value)
|
||||
{
|
||||
if (IsSetProperty)
|
||||
{
|
||||
var pi = Member as PropertyInfo;
|
||||
object clrValue;
|
||||
if (!value.TryGetClrValue(pi.PropertyType, out clrValue))
|
||||
throw new LuaException("Unable to convert '{0}' to Clr type '{1}'".F(value.WrappedClrType().Name, pi.PropertyType));
|
||||
|
||||
pi.SetValue(Target, clrValue, null);
|
||||
}
|
||||
else
|
||||
throw new LuaException("The property '{0}' is read-only".F(Member.Name));
|
||||
}
|
||||
|
||||
public static IEnumerable<MemberInfo> WrappableMembers(Type t)
|
||||
{
|
||||
// Only expose defined public non-static methods that were explicitly declared by the author
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
return t.GetMembers(flags).Where(mi =>
|
||||
{
|
||||
// Properties are always wrappable
|
||||
if (mi is PropertyInfo)
|
||||
return true;
|
||||
|
||||
// Methods are allowed if they aren't generic, and aren't generated by the compiler
|
||||
var method = mi as MethodInfo;
|
||||
if (method != null && !method.IsGenericMethodDefinition && !method.IsSpecialName)
|
||||
return true;
|
||||
|
||||
// Fields aren't allowed
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
77
OpenRA.Game/Scripting/ScriptObjectWrapper.cs
Normal file
77
OpenRA.Game/Scripting/ScriptObjectWrapper.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
#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 System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public abstract class ScriptObjectWrapper : IScriptBindable, ILuaTableBinding
|
||||
{
|
||||
protected abstract string DuplicateKeyError(string memberName);
|
||||
protected abstract string MemberNotFoundError(string memberName);
|
||||
|
||||
protected readonly ScriptContext context;
|
||||
Dictionary<string, ScriptMemberWrapper> members;
|
||||
|
||||
public ScriptObjectWrapper(ScriptContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected void Bind(IEnumerable<object> clrObjects)
|
||||
{
|
||||
members = new Dictionary<string, ScriptMemberWrapper>();
|
||||
foreach (var obj in clrObjects)
|
||||
{
|
||||
var wrappable = ScriptMemberWrapper.WrappableMembers(obj.GetType());
|
||||
foreach (var m in wrappable)
|
||||
{
|
||||
if (members.ContainsKey(m.Name))
|
||||
throw new LuaException(DuplicateKeyError(m.Name));
|
||||
|
||||
members.Add(m.Name, new ScriptMemberWrapper(context, obj, m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key) { return members.ContainsKey(key); }
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue keyValue]
|
||||
{
|
||||
get
|
||||
{
|
||||
var name = keyValue.ToString();
|
||||
ScriptMemberWrapper wrapper;
|
||||
if (!members.TryGetValue(name, out wrapper))
|
||||
throw new LuaException(MemberNotFoundError(name));
|
||||
|
||||
return wrapper.Get(runtime);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var name = keyValue.ToString();
|
||||
ScriptMemberWrapper wrapper;
|
||||
if (!members.TryGetValue(name, out wrapper))
|
||||
throw new LuaException(MemberNotFoundError(name));
|
||||
|
||||
wrapper.Set(runtime, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
OpenRA.Game/Scripting/ScriptPlayerInterface.cs
Normal file
45
OpenRA.Game/Scripting/ScriptPlayerInterface.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
#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 System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public class ScriptPlayerInterface : ScriptObjectWrapper
|
||||
{
|
||||
readonly Player player;
|
||||
|
||||
protected override string DuplicateKeyError(string memberName) { return "Player '{0}' defines the command '{1}' on multiple traits".F(player.PlayerName, memberName); }
|
||||
protected override string MemberNotFoundError(string memberName) { return "Player '{0}' does not define a property '{1}'".F(player.PlayerName, memberName); }
|
||||
|
||||
public ScriptPlayerInterface(ScriptContext context, Player player)
|
||||
: base(context)
|
||||
{
|
||||
this.player = player;
|
||||
|
||||
var args = new [] { player };
|
||||
var objects = context.PlayerCommands.Select(cg =>
|
||||
{
|
||||
var groupCtor = cg.GetConstructor(new Type[] { typeof(Player) });
|
||||
return groupCtor.Invoke(args);
|
||||
});
|
||||
|
||||
Bind(objects);
|
||||
}
|
||||
}
|
||||
}
|
||||
153
OpenRA.Game/Scripting/ScriptTypes.cs
Normal file
153
OpenRA.Game/Scripting/ScriptTypes.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
#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.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public static class LuaValueExts
|
||||
{
|
||||
public static Type WrappedClrType(this LuaValue value)
|
||||
{
|
||||
object inner;
|
||||
if (value.TryGetClrObject(out inner))
|
||||
return inner.GetType();
|
||||
|
||||
return value.GetType();
|
||||
}
|
||||
|
||||
public static bool TryGetClrValue<T>(this LuaValue value, out T clrObject)
|
||||
{
|
||||
object temp;
|
||||
var ret = value.TryGetClrValue(typeof(T), out temp);
|
||||
clrObject = ret ? (T)temp : default(T);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObject)
|
||||
{
|
||||
object temp;
|
||||
|
||||
// Value wraps a CLR object
|
||||
if (value.TryGetClrObject(out temp))
|
||||
{
|
||||
if (temp.GetType() == t)
|
||||
{
|
||||
clrObject = temp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (value is LuaNil && !t.IsValueType)
|
||||
{
|
||||
clrObject = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaBoolean && t.IsAssignableFrom(typeof(bool)))
|
||||
{
|
||||
clrObject = value.ToBoolean();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaNumber && t.IsAssignableFrom(typeof(double)))
|
||||
{
|
||||
clrObject = value.ToNumber().Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Need an explicit test for double -> int
|
||||
// TODO: Lua 5.3 will introduce an integer type, so this will be able to go away
|
||||
if (value is LuaNumber && t.IsAssignableFrom(typeof(int)))
|
||||
{
|
||||
clrObject = (int)(value.ToNumber().Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaString && t.IsAssignableFrom(typeof(string)))
|
||||
{
|
||||
clrObject = value.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaFunction && t.IsAssignableFrom(typeof(LuaFunction)))
|
||||
{
|
||||
clrObject = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaTable && t.IsAssignableFrom(typeof(LuaTable)))
|
||||
{
|
||||
clrObject = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Value isn't of the requested type.
|
||||
// Set a default output value and return false
|
||||
// Value types are assumed to specify a default constructor
|
||||
clrObject = t.IsValueType ? Activator.CreateInstance(t) : null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static LuaValue ToLuaValue(this object obj, ScriptContext context)
|
||||
{
|
||||
if (obj is LuaValue)
|
||||
return (LuaValue)obj;
|
||||
|
||||
if (obj == null)
|
||||
return LuaNil.Instance;
|
||||
|
||||
if (obj is double)
|
||||
return (LuaValue)(double)obj;
|
||||
|
||||
if (obj is int)
|
||||
return (LuaValue)(int)obj;
|
||||
|
||||
if (obj is bool)
|
||||
return (LuaValue)(bool)obj;
|
||||
|
||||
if (obj is string)
|
||||
return (LuaValue)(string)obj;
|
||||
|
||||
if (obj is IScriptBindable)
|
||||
{
|
||||
// Object needs additional notification / context
|
||||
var notify = obj as IScriptNotifyBind;
|
||||
if (notify != null)
|
||||
notify.OnScriptBind(context);
|
||||
|
||||
return new LuaCustomClrObject(obj);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Cannot convert type '{0}' to Lua. Class must implement IScriptBindable.".F(obj.GetType()));
|
||||
}
|
||||
|
||||
public static LuaTable ToLuaTable(this IEnumerable collection, ScriptContext context)
|
||||
{
|
||||
var i = 1;
|
||||
var table = context.CreateTable();
|
||||
foreach (var x in collection)
|
||||
table.Add(i++, x.ToLuaValue(context));
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
/// <summary>
|
||||
/// 3d World position - 1024 units = 1 cell.
|
||||
/// </summary>
|
||||
public struct WPos
|
||||
public struct WPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding
|
||||
{
|
||||
public readonly int X, Y, Z;
|
||||
|
||||
@@ -59,6 +59,71 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); }
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
WPos a;
|
||||
WVec b;
|
||||
if (!left.TryGetClrValue<WPos>(out a) || !right.TryGetClrValue<WVec>(out b))
|
||||
throw new LuaException("Attempted to call WPos.Add(WPos, WVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
}
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
WPos a;
|
||||
var rightType = right.WrappedClrType();
|
||||
if (!left.TryGetClrValue<WPos>(out a))
|
||||
throw new LuaException("Attempted to call WPos.Subtract(WPos, WVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType));
|
||||
|
||||
if (rightType == typeof(WPos))
|
||||
{
|
||||
WPos b;
|
||||
right.TryGetClrValue<WPos>(out b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
else if (rightType == typeof(WVec))
|
||||
{
|
||||
WVec b;
|
||||
right.TryGetClrValue<WVec>(out b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
|
||||
throw new LuaException("Attempted to call WPos.Subtract(WPos, WVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType));
|
||||
}
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
WPos a, b;
|
||||
if (!left.TryGetClrValue<WPos>(out a) || !right.TryGetClrValue<WPos>(out b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue key]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (key.ToString())
|
||||
{
|
||||
case "X": return X;
|
||||
case "Y": return Y;
|
||||
case "Z": return Z;
|
||||
default: throw new LuaException("WPos does not define a member '{0}'".F(key));
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new LuaException("WPos is read-only. Use WPos.New to create a new value");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class IEnumerableExtensions
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.Scripting;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
/// <summary>
|
||||
/// 3d World vector for describing offsets and distances - 1024 units = 1 cell.
|
||||
/// </summary>
|
||||
public struct WVec
|
||||
public struct WVec : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaUnaryMinusBinding, ILuaEqualityBinding, ILuaTableBinding
|
||||
{
|
||||
public readonly int X, Y, Z;
|
||||
|
||||
@@ -87,5 +87,61 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); }
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
WVec a, b;
|
||||
if (!left.TryGetClrValue<WVec>(out a) || !right.TryGetClrValue<WVec>(out b))
|
||||
throw new LuaException("Attempted to call WVec.Add(WVec, WVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
}
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
WVec a, b;
|
||||
if (!left.TryGetClrValue<WVec>(out a) || !right.TryGetClrValue<WVec>(out b))
|
||||
throw new LuaException("Attempted to call WVec.Subtract(WVec, WVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
|
||||
public LuaValue Minus(LuaRuntime runtime)
|
||||
{
|
||||
return new LuaCustomClrObject(-this);
|
||||
}
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
WVec a, b;
|
||||
if (!left.TryGetClrValue<WVec>(out a) || !right.TryGetClrValue<WVec>(out b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue key]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (key.ToString())
|
||||
{
|
||||
case "X": return X;
|
||||
case "Y": return Y;
|
||||
case "Z": return Z;
|
||||
case "Facing": return Traits.Util.GetFacing(this, 0);
|
||||
default: throw new LuaException("WVec does not define a member '{0}'".F(key));
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new LuaException("WVec is read-only. Use WVec.New to create a new value");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user