Allow ActorInits to target a specific trait by matching the @ suffix.
This commit is contained in:
@@ -23,6 +23,7 @@ namespace OpenRA
|
|||||||
public class ActorInfo
|
public class ActorInfo
|
||||||
{
|
{
|
||||||
public const string AbstractActorPrefix = "^";
|
public const string AbstractActorPrefix = "^";
|
||||||
|
public const char TraitInstanceSeparator = '@';
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
|
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
|
||||||
@@ -46,7 +47,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
||||||
// LoadTraitInfo will only return null to signal us to abort here if the linter is running
|
// LoadTraitInfo will only return null to signal us to abort here if the linter is running
|
||||||
var trait = LoadTraitInfo(creator, t.Key.Split('@')[0], t.Value);
|
var trait = LoadTraitInfo(creator, t.Key, t.Value);
|
||||||
if (trait != null)
|
if (trait != null)
|
||||||
traits.Add(trait);
|
traits.Add(trait);
|
||||||
}
|
}
|
||||||
@@ -80,12 +81,16 @@ namespace OpenRA
|
|||||||
|
|
||||||
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
||||||
// ObjectCreator will only return null to signal us to abort here if the linter is running
|
// ObjectCreator will only return null to signal us to abort here if the linter is running
|
||||||
var info = creator.CreateObject<TraitInfo>(traitName + "Info");
|
var traitInstance = traitName.Split(TraitInstanceSeparator);
|
||||||
|
var info = creator.CreateObject<TraitInfo>(traitInstance[0] + "Info");
|
||||||
if (info == null)
|
if (info == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (traitInstance.Length > 1)
|
||||||
|
info.GetType().GetField("InstanceName").SetValue(info, traitInstance[1]);
|
||||||
|
|
||||||
FieldLoader.Load(info, my);
|
FieldLoader.Load(info, my);
|
||||||
}
|
}
|
||||||
catch (FieldLoader.MissingFieldsException e)
|
catch (FieldLoader.MissingFieldsException e)
|
||||||
|
|||||||
@@ -43,7 +43,17 @@ namespace OpenRA
|
|||||||
|
|
||||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||||
{
|
{
|
||||||
return Dict.GetOrDefault<T>();
|
var inits = Dict.WithInterface<T>();
|
||||||
|
|
||||||
|
// Traits tagged with an instance name prefer inits with the same name.
|
||||||
|
// If a more specific init is not available, fall back to an unnamed init.
|
||||||
|
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||||
|
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||||
|
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||||
|
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||||
|
|
||||||
|
// Untagged traits will only use untagged inits
|
||||||
|
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||||
@@ -81,6 +91,16 @@ namespace OpenRA
|
|||||||
*/
|
*/
|
||||||
public abstract class ActorInit
|
public abstract class ActorInit
|
||||||
{
|
{
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
public readonly string InstanceName;
|
||||||
|
|
||||||
|
protected ActorInit(string instanceName)
|
||||||
|
{
|
||||||
|
InstanceName = instanceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ActorInit() { }
|
||||||
|
|
||||||
public abstract MiniYaml Save();
|
public abstract MiniYaml Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +108,8 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
protected readonly T value;
|
protected readonly T value;
|
||||||
|
|
||||||
protected ValueActorInit(TraitInfo info, T value) { this.value = value; }
|
protected ValueActorInit(TraitInfo info, T value)
|
||||||
|
: base(info.InstanceName) { this.value = value; }
|
||||||
|
|
||||||
protected ValueActorInit(T value) { this.value = value; }
|
protected ValueActorInit(T value) { this.value = value; }
|
||||||
|
|
||||||
|
|||||||
@@ -47,14 +47,18 @@ namespace OpenRA
|
|||||||
|
|
||||||
static ActorInit LoadInit(string initName, MiniYaml initYaml)
|
static ActorInit LoadInit(string initName, MiniYaml initYaml)
|
||||||
{
|
{
|
||||||
var type = Game.ModData.ObjectCreator.FindType(initName + "Init");
|
var initInstance = initName.Split(ActorInfo.TraitInstanceSeparator);
|
||||||
|
var type = Game.ModData.ObjectCreator.FindType(initInstance[0] + "Init");
|
||||||
if (type == null)
|
if (type == null)
|
||||||
throw new InvalidDataException("Unknown initializer type '{0}Init'".F(initName));
|
throw new InvalidDataException("Unknown initializer type '{0}Init'".F(initInstance[0]));
|
||||||
|
|
||||||
var init = (ActorInit)FormatterServices.GetUninitializedObject(type);
|
var init = (ActorInit)FormatterServices.GetUninitializedObject(type);
|
||||||
|
if (initInstance.Length > 1)
|
||||||
|
type.GetField("InstanceName").SetValue(init, initInstance[1]);
|
||||||
|
|
||||||
var loader = type.GetMethod("Initialize", new[] { typeof(MiniYaml) });
|
var loader = type.GetMethod("Initialize", new[] { typeof(MiniYaml) });
|
||||||
if (loader == null)
|
if (loader == null)
|
||||||
throw new InvalidDataException("{0}Init does not define a yaml-assignable type.".F(initName));
|
throw new InvalidDataException("{0}Init does not define a yaml-assignable type.".F(initInstance[0]));
|
||||||
|
|
||||||
loader.Invoke(init, new[] { initYaml });
|
loader.Invoke(init, new[] { initYaml });
|
||||||
return init;
|
return init;
|
||||||
@@ -74,6 +78,9 @@ namespace OpenRA
|
|||||||
|
|
||||||
var initTypeName = init.GetType().Name;
|
var initTypeName = init.GetType().Name;
|
||||||
var initName = initTypeName.Substring(0, initTypeName.Length - 4);
|
var initName = initTypeName.Substring(0, initTypeName.Length - 4);
|
||||||
|
if (!string.IsNullOrEmpty(init.InstanceName))
|
||||||
|
initName += ActorInfo.TraitInstanceSeparator + init.InstanceName;
|
||||||
|
|
||||||
ret.Nodes.Add(new MiniYamlNode(initName, init.Save()));
|
ret.Nodes.Add(new MiniYamlNode(initName, init.Save()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -327,6 +327,9 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public abstract class TraitInfo : ITraitInfoInterface
|
public abstract class TraitInfo : ITraitInfoInterface
|
||||||
{
|
{
|
||||||
|
// Value is set using reflection during TraitInfo creation
|
||||||
|
public readonly string InstanceName = null;
|
||||||
|
|
||||||
public abstract object Create(ActorInitializer init);
|
public abstract object Create(ActorInitializer init);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,17 @@ namespace OpenRA.Mods.Common.Graphics
|
|||||||
|
|
||||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||||
{
|
{
|
||||||
return dict.GetOrDefault<T>();
|
var inits = dict.WithInterface<T>();
|
||||||
|
|
||||||
|
// Traits tagged with an instance name prefer inits with the same name.
|
||||||
|
// If a more specific init is not available, fall back to an unnamed init.
|
||||||
|
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||||
|
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||||
|
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||||
|
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||||
|
|
||||||
|
// Untagged traits will only use untagged inits
|
||||||
|
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||||
|
|||||||
@@ -30,12 +30,16 @@ namespace OpenRA.Mods.Common.Scripting
|
|||||||
ActorInit CreateInit(string initName, LuaValue value)
|
ActorInit CreateInit(string initName, LuaValue value)
|
||||||
{
|
{
|
||||||
// Find the requested type
|
// Find the requested type
|
||||||
var initType = Game.ModData.ObjectCreator.FindType(initName + "Init");
|
var initInstance = initName.Split(ActorInfo.TraitInstanceSeparator);
|
||||||
|
var initType = Game.ModData.ObjectCreator.FindType(initInstance[0] + "Init");
|
||||||
if (initType == null)
|
if (initType == null)
|
||||||
throw new LuaException("Unknown initializer type '{0}'".F(initName));
|
throw new LuaException("Unknown initializer type '{0}'".F(initInstance[0]));
|
||||||
|
|
||||||
// Construct the ActorInit.
|
// Construct the ActorInit.
|
||||||
var init = (ActorInit)FormatterServices.GetUninitializedObject(initType);
|
var init = (ActorInit)FormatterServices.GetUninitializedObject(initType);
|
||||||
|
if (initInstance.Length > 1)
|
||||||
|
initType.GetField("InstanceName").SetValue(init, initInstance[1]);
|
||||||
|
|
||||||
var initializers = initType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
|
var initializers = initType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
|
||||||
.Where(m => m.Name == "Initialize" && m.GetParameters().Length == 1);
|
.Where(m => m.Name == "Initialize" && m.GetParameters().Length == 1);
|
||||||
|
|
||||||
@@ -55,7 +59,7 @@ namespace OpenRA.Mods.Common.Scripting
|
|||||||
}
|
}
|
||||||
|
|
||||||
var types = initializers.Select(y => y.GetParameters()[0].ParameterType.Name).JoinWith(", ");
|
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));
|
throw new LuaException("Invalid data type for '{0}' (expected one of {1})".F(initInstance[0], types));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Desc("Create a new actor. initTable specifies a list of key-value pairs that defines the initial parameters for the actor's traits.")]
|
[Desc("Create a new actor. initTable specifies a list of key-value pairs that defines the initial parameters for the actor's traits.")]
|
||||||
|
|||||||
Reference in New Issue
Block a user