Replace IActorInit with an abstract class.

A shared ValueActorInit<T> is introduced to reduce duplication
in the most common init cases, and an ActorInitActorReference
allow actors to be referenced by map.yaml name.
This commit is contained in:
Paul Chote
2020-06-02 19:37:18 +01:00
committed by teinarss
parent 4df5ac0385
commit b38018af9c
28 changed files with 365 additions and 306 deletions

View File

@@ -10,7 +10,10 @@
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Eluant;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
@@ -24,6 +27,37 @@ namespace OpenRA.Mods.Common.Scripting
public ActorGlobal(ScriptContext context)
: base(context) { }
ActorInit CreateInit(string initName, LuaValue value)
{
// Find the requested type
var initType = Game.ModData.ObjectCreator.FindType(initName + "Init");
if (initType == null)
throw new LuaException("Unknown initializer type '{0}'".F(initName));
// Construct the ActorInit.
var init = (ActorInit)FormatterServices.GetUninitializedObject(initType);
var initializers = initType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "Initialize" && m.GetParameters().Length == 1);
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
object clrValue;
if (!value.TryGetClrValue(valueType, out clrValue))
continue;
initializer.Invoke(init, new[] { clrValue });
return init;
}
var types = initializers.Select(y => y.GetParameters()[0].ParameterType.Name).JoinWith(", ");
throw new LuaException("Invalid data type for '{0}' (expected one of {1})".F(initName, 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, LuaTable initTable)
{
@@ -34,35 +68,7 @@ namespace OpenRA.Mods.Common.Scripting
{
using (kv.Key)
using (kv.Value)
{
// Find the requested type
var typeName = kv.Key.ToString();
var initType = Game.ModData.ObjectCreator.FindType(typeName + "Init");
if (initType == null)
throw new LuaException("Unknown initializer type '{0}'".F(typeName));
// HACK: Handle OwnerInit as a special case until ActorInit creation can be rewritten
Type innerType, valueType;
if (initType != typeof(OwnerInit))
{
// Cast it up to an IActorInit<T>
var genericType = initType.GetInterfaces()
.First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IActorInit<>));
innerType = genericType.GetGenericArguments().First();
valueType = innerType.IsEnum ? Enum.GetUnderlyingType(innerType) : innerType;
}
else
innerType = valueType = typeof(Player);
// Try and coerce the table value to the required type
object value;
if (!kv.Value.TryGetClrValue(valueType, out value))
throw new LuaException("Invalid data type for '{0}' (expected '{1}')".F(typeName, valueType.Name));
// Construct the ActorInit. Phew!
var test = initType.GetConstructor(new[] { innerType }).Invoke(new[] { value });
initDict.Add(test);
}
initDict.Add(CreateInit(kv.Key.ToString(), kv.Value));
}
var owner = initDict.GetOrDefault<OwnerInit>();