diff --git a/OpenRA.Game/Map/ActorInitializer.cs b/OpenRA.Game/Map/ActorInitializer.cs index 06267b385f..ef26739e6a 100644 --- a/OpenRA.Game/Map/ActorInitializer.cs +++ b/OpenRA.Game/Map/ActorInitializer.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using OpenRA.Primitives; using OpenRA.Traits; @@ -19,11 +21,11 @@ namespace OpenRA public interface IActorInitializer { World World { get; } - T GetOrDefault(TraitInfo info) where T : IActorInit; - T Get(TraitInfo info) where T : IActorInit; - U GetValue(TraitInfo info) where T : IActorInit; - U GetValue(TraitInfo info, U fallback) where T : IActorInit; - bool Contains(TraitInfo info) where T : IActorInit; + T GetOrDefault(TraitInfo info) where T : ActorInit; + T Get(TraitInfo info) where T : ActorInit; + U GetValue(TraitInfo info) where T : ValueActorInit; + U GetValue(TraitInfo info, U fallback) where T : ValueActorInit; + bool Contains(TraitInfo info) where T : ActorInit; } public class ActorInitializer : IActorInitializer @@ -39,12 +41,12 @@ namespace OpenRA Dict = dict; } - public T GetOrDefault(TraitInfo info) where T : IActorInit + public T GetOrDefault(TraitInfo info) where T : ActorInit { return Dict.GetOrDefault(); } - public T Get(TraitInfo info) where T : IActorInit + public T Get(TraitInfo info) where T : ActorInit { var init = GetOrDefault(info); if (init == null) @@ -53,59 +55,119 @@ namespace OpenRA return init; } - public U GetValue(TraitInfo info) where T : IActorInit + public U GetValue(TraitInfo info) where T : ValueActorInit { return Get(info).Value; } - public U GetValue(TraitInfo info, U fallback) where T : IActorInit + public U GetValue(TraitInfo info, U fallback) where T : ValueActorInit { var init = GetOrDefault(info); return init != null ? init.Value : fallback; } - public bool Contains(TraitInfo info) where T : IActorInit { return GetOrDefault(info) != null; } + public bool Contains(TraitInfo info) where T : ActorInit { return GetOrDefault(info) != null; } } - public interface IActorInit { } - - public interface IActorInit : IActorInit + /* + * Things to be aware of when writing ActorInits: + * + * - ActorReference and ActorGlobal can dynamically create objects without calling a constructor. + * The object will be allocated directly then the best matching Initialize() method will be called to set valid state. + * - ActorReference will always attempt to call Initialize(MiniYaml). ActorGlobal will use whichever one it first + * finds with an argument type that matches the given LuaValue. + * - Most ActorInits will want to inherit ValueActorInit which hides the low-level plumbing. + * - Inits that reference actors should use ActorInitActorReference which allows actors to be referenced by name in map.yaml + */ + public abstract class ActorInit { - T Value { get; } + public abstract MiniYaml Save(); } - public class LocationInit : IActorInit + public abstract class ValueActorInit : ActorInit { - [FieldFromYamlKey] - readonly CPos value = CPos.Zero; + protected readonly T value; - public LocationInit() { } - public LocationInit(CPos init) { value = init; } - public CPos Value { get { return value; } } - } + protected ValueActorInit(TraitInfo info, T value) { this.value = value; } - public class OwnerInit : IActorInit - { - [FieldFromYamlKey] - public readonly string InternalName = "Neutral"; + protected ValueActorInit(T value) { this.value = value; } - Player player; + public virtual T Value { get { return value; } } - public OwnerInit() { } - public OwnerInit(string playerName) { InternalName = playerName; } - - public OwnerInit(Player player) + public virtual void Initialize(MiniYaml yaml) { - this.player = player; - InternalName = player.InternalName; + var valueType = typeof(T).IsEnum ? Enum.GetUnderlyingType(typeof(T)) : typeof(T); + Initialize((T)FieldLoader.GetValue("value", valueType, yaml.Value)); + } + + public virtual void Initialize(T value) + { + var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance); + if (field != null) + field.SetValue(this, value); + } + + public override MiniYaml Save() + { + return new MiniYaml(FieldSaver.FormatValue(value)); + } + } + + public class LocationInit : ValueActorInit + { + public LocationInit(TraitInfo info, CPos value) + : base(info, value) { } + + public LocationInit(CPos value) + : base(value) { } + } + + public class OwnerInit : ActorInit + { + public readonly string InternalName; + protected readonly Player value; + + public OwnerInit(Player value) + { + this.value = value; + InternalName = value.InternalName; + } + + public OwnerInit(string value) + { + InternalName = value; } public Player Value(World world) { - if (player != null) - return player; + return value ?? world.Players.First(x => x.InternalName == InternalName); + } - return world.Players.First(x => x.InternalName == InternalName); + public void Initialize(MiniYaml yaml) + { + var field = GetType().GetField("InternalName", BindingFlags.Public | BindingFlags.Instance); + if (field != null) + field.SetValue(this, yaml.Value); + } + + public void Initialize(Player player) + { + var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance); + if (field != null) + field.SetValue(this, player); + } + + public override MiniYaml Save() + { + return new MiniYaml(InternalName); + } + } + + public abstract class RuntimeFlagInit : ActorInit, ISuppressInitExport + { + public override MiniYaml Save() + { + throw new NotImplementedException("RuntimeFlagInit cannot be saved"); } } } diff --git a/OpenRA.Game/Map/ActorReference.cs b/OpenRA.Game/Map/ActorReference.cs index d007c06d28..02ad97775b 100644 --- a/OpenRA.Game/Map/ActorReference.cs +++ b/OpenRA.Game/Map/ActorReference.cs @@ -12,6 +12,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; using OpenRA.Primitives; namespace OpenRA @@ -43,26 +45,36 @@ namespace OpenRA }); } - static IActorInit LoadInit(string traitName, MiniYaml my) + static ActorInit LoadInit(string initName, MiniYaml initYaml) { - var info = Game.CreateObject(traitName + "Init"); - FieldLoader.Load(info, my); - return info; + var type = Game.ModData.ObjectCreator.FindType(initName + "Init"); + if (type == null) + throw new InvalidDataException("Unknown initializer type '{0}Init'".F(initName)); + + var init = (ActorInit)FormatterServices.GetUninitializedObject(type); + var loader = type.GetMethod("Initialize", new[] { typeof(MiniYaml) }); + if (loader == null) + throw new InvalidDataException("{0}Init does not define a yaml-assignable type.".F(initName)); + + loader.Invoke(init, new[] { initYaml }); + return init; } - public MiniYaml Save(Func initFilter = null) + public MiniYaml Save(Func initFilter = null) { var ret = new MiniYaml(Type); - foreach (var init in InitDict) + foreach (var o in InitDict) { - if (init is ISuppressInitExport) + var init = o as ActorInit; + if (init == null || o is ISuppressInitExport) continue; if (initFilter != null && !initFilter(init)) continue; - var initName = init.GetType().Name; - ret.Nodes.Add(new MiniYamlNode(initName.Substring(0, initName.Length - 4), FieldSaver.Save(init))); + var initTypeName = init.GetType().Name; + var initName = initTypeName.Substring(0, initTypeName.Length - 4); + ret.Nodes.Add(new MiniYamlNode(initName, init.Save())); } return ret; diff --git a/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs b/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs index 668151b39f..6ec09f14c0 100644 --- a/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs +++ b/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs @@ -11,6 +11,7 @@ using System; using OpenRA.Mods.Cnc.Activities; +using OpenRA.Mods.Common; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -66,7 +67,7 @@ namespace OpenRA.Mods.Cnc.Traits // Defer to the end of tick as the lazy value may reference an actor that hasn't been created yet var chronosphereInit = init.GetOrDefault(info); if (chronosphereInit != null) - init.World.AddFrameEndTask(w => chronosphere = chronosphereInit.Value(init.World).Value); + init.World.AddFrameEndTask(w => chronosphere = chronosphereInit.Value.Actor(init.World).Value); } void ITick.Tick(Actor self) @@ -176,40 +177,27 @@ namespace OpenRA.Mods.Cnc.Traits void ITransformActorInitModifier.ModifyTransformActorInit(Actor self, TypeDictionary init) { ModifyActorInit(init); } } - public class ChronoshiftReturnInit : IActorInit + public class ChronoshiftReturnInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value = 0; - - public ChronoshiftReturnInit() { } - public ChronoshiftReturnInit(int init) { value = init; } - public int Value { get { return value; } } + public ChronoshiftReturnInit(int value) + : base(value) { } } - public class ChronoshiftDurationInit : IActorInit + public class ChronoshiftDurationInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value = 0; - - public ChronoshiftDurationInit() { } - public ChronoshiftDurationInit(int init) { value = init; } - public int Value { get { return value; } } + public ChronoshiftDurationInit(int value) + : base(value) { } } - public class ChronoshiftOriginInit : IActorInit + public class ChronoshiftOriginInit : ValueActorInit { - [FieldFromYamlKey] - readonly CPos value; - - public ChronoshiftOriginInit(CPos init) { value = init; } - public CPos Value { get { return value; } } + public ChronoshiftOriginInit(CPos value) + : base(value) { } } - public class ChronoshiftChronosphereInit : IActorInit + public class ChronoshiftChronosphereInit : ValueActorInit { - readonly Actor value; - public ChronoshiftChronosphereInit(Actor init) { value = init; } - - public Lazy Value(World world) { return new Lazy(() => value); } + public ChronoshiftChronosphereInit(Actor value) + : base(value) { } } } diff --git a/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs b/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs index d5636fc525..d140d4bf98 100644 --- a/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs +++ b/OpenRA.Mods.Cnc/Traits/ConyardChronoReturn.cs @@ -103,7 +103,7 @@ namespace OpenRA.Mods.Cnc.Traits // Defer to the end of tick as the lazy value may reference an actor that hasn't been created yet var chronosphereInit = init.GetOrDefault(info); if (chronosphereInit != null) - init.World.AddFrameEndTask(w => chronosphere = chronosphereInit.Value(init.World).Value); + init.World.AddFrameEndTask(w => chronosphere = chronosphereInit.Value.Actor(init.World).Value); } IEnumerable IObservesVariables.GetVariableObservers() diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithBuildingBib.cs b/OpenRA.Mods.Cnc/Traits/Render/WithBuildingBib.cs index c0ad19a56f..ef478ec625 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithBuildingBib.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithBuildingBib.cs @@ -133,8 +133,5 @@ namespace OpenRA.Mods.Cnc.Traits } } - class HideBibPreviewInit : IActorInit, ISuppressInitExport - { - public HideBibPreviewInit() { } - } + class HideBibPreviewInit : RuntimeFlagInit { } } diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs index b11da36525..c93f1654d8 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs @@ -95,13 +95,9 @@ namespace OpenRA.Mods.Cnc.Traits.Render } } - public class BodyAnimationFrameInit : IActorInit + public class BodyAnimationFrameInit : ValueActorInit { - [FieldFromYamlKey] - readonly uint value = 0; - - public BodyAnimationFrameInit() { } - public BodyAnimationFrameInit(uint init) { value = init; } - public uint Value { get { return value; } } + public BodyAnimationFrameInit(uint value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/ActorInitializer.cs b/OpenRA.Mods.Common/ActorInitializer.cs index 724bed4f0d..5405aaccb1 100644 --- a/OpenRA.Mods.Common/ActorInitializer.cs +++ b/OpenRA.Mods.Common/ActorInitializer.cs @@ -10,77 +10,128 @@ #endregion using System; +using System.ComponentModel; +using OpenRA.Mods.Common.Traits; using OpenRA.Traits; namespace OpenRA.Mods.Common { - public class FacingInit : IActorInit + public class FacingInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value = 128; - - public FacingInit() { } - public FacingInit(int init) { value = init; } - public int Value { get { return value; } } + public FacingInit(int value) + : base(value) { } } - public class CreationActivityDelayInit : IActorInit + public class CreationActivityDelayInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value = 0; - - public CreationActivityDelayInit() { } - public CreationActivityDelayInit(int init) { value = init; } - public int Value { get { return value; } } + public CreationActivityDelayInit(int value) + : base(value) { } } - public class DynamicFacingInit : IActorInit> + public class DynamicFacingInit : ValueActorInit> { - readonly Func func; - - public DynamicFacingInit(Func func) { this.func = func; } - public Func Value { get { return func; } } + public DynamicFacingInit(Func value) + : base(value) { } } - public class SubCellInit : IActorInit + public class SubCellInit : ValueActorInit { - [FieldFromYamlKey] - readonly byte value = (byte)SubCell.FullCell; - - public SubCellInit() { } - public SubCellInit(byte init) { value = init; } - public SubCellInit(SubCell init) { value = (byte)init; } - public SubCell Value { get { return (SubCell)value; } } + public SubCellInit(SubCell value) + : base(value) { } } - public class CenterPositionInit : IActorInit + public class CenterPositionInit : ValueActorInit { - [FieldFromYamlKey] - readonly WPos value = WPos.Zero; - - public CenterPositionInit() { } - public CenterPositionInit(WPos init) { value = init; } - public WPos Value { get { return value; } } + public CenterPositionInit(WPos value) + : base(value) { } } // Allows maps / transformations to specify the faction variant of an actor. - public class FactionInit : IActorInit + public class FactionInit : ValueActorInit { - [FieldFromYamlKey] - public readonly string Faction; - - public FactionInit() { } - public FactionInit(string faction) { Faction = faction; } - public string Value { get { return Faction; } } + public FactionInit(string value) + : base(value) { } } - public class EffectiveOwnerInit : IActorInit + public class EffectiveOwnerInit : ValueActorInit { - [FieldFromYamlKey] - readonly Player value = null; + public EffectiveOwnerInit(Player value) + : base(value) { } + } - public EffectiveOwnerInit() { } - public EffectiveOwnerInit(Player owner) { value = owner; } - public Player Value { get { return value; } } + internal class ActorInitLoader : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + return new ActorInitActorReference(value as string); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(string) || base.CanConvertTo(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + { + if (destinationType == typeof(string)) + { + var reference = value as ActorInitActorReference; + if (reference != null) + return reference.InternalName; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + + [TypeConverter(typeof(ActorInitLoader))] + public class ActorInitActorReference + { + public readonly string InternalName; + readonly Actor actor; + + public ActorInitActorReference(Actor actor) + { + this.actor = actor; + } + + public ActorInitActorReference(string internalName) + { + InternalName = internalName; + } + + Actor InnerValue(World world) + { + if (actor != null) + return actor; + + var sma = world.WorldActor.Trait(); + return sma.Actors[InternalName]; + } + + /// + /// The lazy value may reference other actors that have not been created + /// yet, so must not be resolved from the actor constructor or Created method. + /// Use a FrameEndTask or wait until it is actually needed. + /// + public Lazy Actor(World world) + { + return new Lazy(() => InnerValue(world)); + } + + public static implicit operator ActorInitActorReference(Actor a) + { + return new ActorInitActorReference(a); + } + + public static implicit operator ActorInitActorReference(string mapName) + { + return new ActorInitActorReference(mapName); + } } } diff --git a/OpenRA.Mods.Common/Graphics/ActorPreview.cs b/OpenRA.Mods.Common/Graphics/ActorPreview.cs index feab85a55a..6a29a2cd0f 100644 --- a/OpenRA.Mods.Common/Graphics/ActorPreview.cs +++ b/OpenRA.Mods.Common/Graphics/ActorPreview.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -41,12 +42,12 @@ namespace OpenRA.Mods.Common.Graphics this.dict = dict; } - public T GetOrDefault(TraitInfo info) where T : IActorInit + public T GetOrDefault(TraitInfo info) where T : ActorInit { return dict.GetOrDefault(); } - public T Get(TraitInfo info) where T : IActorInit + public T Get(TraitInfo info) where T : ActorInit { var init = GetOrDefault(info); if (init == null) @@ -55,18 +56,18 @@ namespace OpenRA.Mods.Common.Graphics return init; } - public U GetValue(TraitInfo info) where T : IActorInit + public U GetValue(TraitInfo info) where T : ValueActorInit { return Get(info).Value; } - public U GetValue(TraitInfo info, U fallback) where T : IActorInit + public U GetValue(TraitInfo info, U fallback) where T : ValueActorInit { var init = GetOrDefault(info); return init != null ? init.Value : fallback; } - public bool Contains(TraitInfo info) where T : IActorInit { return GetOrDefault(info) != null; } + public bool Contains(TraitInfo info) where T : ActorInit { return GetOrDefault(info) != null; } public Func GetOrientation() { diff --git a/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs index abb9c19d0b..8662d50761 100644 --- a/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs @@ -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 - 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(); diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 31ffe6a7d4..68dbf68dee 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -123,7 +123,7 @@ namespace OpenRA.Mods.Common.Traits var stance = init != null ? init.Value : InitialStance; return stances[(int)stance]; }, - (actor, value) => actor.ReplaceInit(new StanceInit((UnitStance)stances.IndexOf(value)))); + (actor, value) => actor.ReplaceInit(new StanceInit(this, (UnitStance)stances.IndexOf(value)))); } } @@ -445,13 +445,9 @@ namespace OpenRA.Mods.Common.Traits } } - public class StanceInit : IActorInit + public class StanceInit : ValueActorInit { - [FieldFromYamlKey] - readonly UnitStance value = UnitStance.AttackAnything; - - public StanceInit() { } - public StanceInit(UnitStance init) { value = init; } - public UnitStance Value { get { return value; } } + public StanceInit(TraitInfo info, UnitStance value) + : base(info, value) { } } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/FreeActor.cs b/OpenRA.Mods.Common/Traits/Buildings/FreeActor.cs index dc389bd1fb..607c7522e1 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/FreeActor.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/FreeActor.cs @@ -50,7 +50,7 @@ namespace OpenRA.Mods.Common.Traits }, (actor, value) => { - actor.ReplaceInit(new FreeActorInit(value)); + actor.ReplaceInit(new FreeActorInit(this, value)); }); } @@ -87,20 +87,15 @@ namespace OpenRA.Mods.Common.Traits } } - public class FreeActorInit : IActorInit + public class FreeActorInit : ValueActorInit { - [FieldFromYamlKey] - public readonly bool ActorValue = true; - public FreeActorInit() { } - public FreeActorInit(bool init) { ActorValue = init; } - public bool Value { get { return ActorValue; } } + public FreeActorInit(TraitInfo info, bool value) + : base(info, value) { } } - public class ParentActorInit : IActorInit + public class ParentActorInit : ValueActorInit { - readonly Actor value; - public ParentActorInit(Actor init) { value = init; } - - public Lazy Value(World world) { return new Lazy(() => value); } + public ParentActorInit(Actor value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/LegacyBridgeHut.cs b/OpenRA.Mods.Common/Traits/Buildings/LegacyBridgeHut.cs index fea345fd69..24b8143784 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/LegacyBridgeHut.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/LegacyBridgeHut.cs @@ -33,10 +33,10 @@ namespace OpenRA.Mods.Common.Traits public LegacyBridgeHut(ActorInitializer init, LegacyBridgeHutInfo info) { - var bridge = init.GetOrDefault(info); + var bridge = init.Get(info).Value; init.World.AddFrameEndTask(_ => { - Bridge = bridge.Value(init.World).Value.Trait(); + Bridge = bridge.Actor(init.World).Value.Trait(); Bridge.AddHut(this); FirstBridge = Bridge.Enumerate(0, true).Last(); }); diff --git a/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs b/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs index 16df62756d..413fa179cb 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs @@ -16,33 +16,29 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { public enum LineBuildDirection { Unset, X, Y } - public class LineBuildDirectionInit : IActorInit + public class LineBuildDirectionInit : ValueActorInit { - [FieldFromYamlKey] - readonly LineBuildDirection value = LineBuildDirection.Unset; - - public LineBuildDirectionInit() { } - public LineBuildDirectionInit(LineBuildDirection init) { value = init; } - public LineBuildDirection Value { get { return value; } } + public LineBuildDirectionInit(LineBuildDirection value) + : base(value) { } } - public class LineBuildParentInit : IActorInit + public class LineBuildParentInit : ValueActorInit { - [FieldFromYamlKey] - public readonly string[] ParentNames = new string[0]; - readonly Actor[] parents = null; - public LineBuildParentInit() { } - public LineBuildParentInit(Actor[] init) { parents = init; } - public string[] Value { get { return ParentNames; } } + public LineBuildParentInit(Actor[] value) + : base(new string[0]) + { + parents = value; + } + public Actor[] ActorValue(World world) { if (parents != null) return parents; var sma = world.WorldActor.Trait(); - return ParentNames.Select(n => sma.Actors[n]).ToArray(); + return value.Select(n => sma.Actors[n]).ToArray(); } } diff --git a/OpenRA.Mods.Common/Traits/Cargo.cs b/OpenRA.Mods.Common/Traits/Cargo.cs index 1140e64fe7..6a6cc0b7e3 100644 --- a/OpenRA.Mods.Common/Traits/Cargo.cs +++ b/OpenRA.Mods.Common/Traits/Cargo.cs @@ -479,25 +479,19 @@ namespace OpenRA.Mods.Common.Traits void ITransformActorInitModifier.ModifyTransformActorInit(Actor self, TypeDictionary init) { - init.Add(new RuntimeCargoInit(Passengers.ToArray())); + init.Add(new RuntimeCargoInit(Info, Passengers.ToArray())); } } - public class RuntimeCargoInit : IActorInit, ISuppressInitExport + public class RuntimeCargoInit : ValueActorInit, ISuppressInitExport { - [FieldFromYamlKey] - readonly Actor[] value = { }; - public RuntimeCargoInit() { } - public RuntimeCargoInit(Actor[] init) { value = init; } - public Actor[] Value { get { return value; } } + public RuntimeCargoInit(TraitInfo info, Actor[] value) + : base(info, value) { } } - public class CargoInit : IActorInit + public class CargoInit : ValueActorInit { - [FieldFromYamlKey] - readonly string[] value = { }; - public CargoInit() { } - public CargoInit(string[] init) { value = init; } - public string[] Value { get { return value; } } + public CargoInit(TraitInfo info, string[] value) + : base(info, value) { } } } diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs index 6386d9977a..11993f3225 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs @@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits }, (actor, value) => { - actor.ReplaceInit(new DeployStateInit(value ? DeployState.Deployed : DeployState.Undeployed)); + actor.ReplaceInit(new DeployStateInit(this, value ? DeployState.Deployed : DeployState.Undeployed)); }); } @@ -337,12 +337,12 @@ namespace OpenRA.Mods.Common.Traits } } - public class DeployStateInit : IActorInit + public class DeployStateInit : ValueActorInit { - [FieldFromYamlKey] - readonly DeployState value = DeployState.Deployed; - public DeployStateInit() { } - public DeployStateInit(DeployState init) { value = init; } - public DeployState Value { get { return value; } } + public DeployStateInit(TraitInfo info, DeployState value) + : base(info, value) { } + + public DeployStateInit(DeployState value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/Traits/GainsExperience.cs b/OpenRA.Mods.Common/Traits/GainsExperience.cs index 5026100fde..eb548ae902 100644 --- a/OpenRA.Mods.Common/Traits/GainsExperience.cs +++ b/OpenRA.Mods.Common/Traits/GainsExperience.cs @@ -141,17 +141,13 @@ namespace OpenRA.Mods.Common.Traits void ITransformActorInitModifier.ModifyTransformActorInit(Actor self, TypeDictionary init) { - init.Add(new ExperienceInit(experience)); + init.Add(new ExperienceInit(info, experience)); } } - class ExperienceInit : IActorInit + class ExperienceInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value; - - public ExperienceInit() { } - public ExperienceInit(int init) { value = init; } - public int Value { get { return value; } } + public ExperienceInit(TraitInfo info, int value) + : base(info, value) { } } } diff --git a/OpenRA.Mods.Common/Traits/Health.cs b/OpenRA.Mods.Common/Traits/Health.cs index d8944ad885..8bcd58b655 100644 --- a/OpenRA.Mods.Common/Traits/Health.cs +++ b/OpenRA.Mods.Common/Traits/Health.cs @@ -233,23 +233,17 @@ namespace OpenRA.Mods.Common.Traits } } - public class HealthInit : IActorInit + public class HealthInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value = 100; - readonly bool allowZero; - public HealthInit() { } - public HealthInit(int init) - : this(init, false) { } - public HealthInit(int init, bool allowZero) - { - this.allowZero = allowZero; - value = init; - } + public HealthInit(TraitInfo info, int value, bool allowZero = false) + : base(info, value) { this.allowZero = allowZero; } - public int Value + public HealthInit(int value, bool allowZero = false) + : base(value) { this.allowZero = allowZero; } + + public override int Value { get { diff --git a/OpenRA.Mods.Common/Traits/Husk.cs b/OpenRA.Mods.Common/Traits/Husk.cs index deefd4ba84..a3e003b721 100644 --- a/OpenRA.Mods.Common/Traits/Husk.cs +++ b/OpenRA.Mods.Common/Traits/Husk.cs @@ -171,13 +171,9 @@ namespace OpenRA.Mods.Common.Traits Player IEffectiveOwner.Owner { get { return effectiveOwner; } } } - public class HuskSpeedInit : IActorInit + public class HuskSpeedInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value = 0; - - public HuskSpeedInit() { } - public HuskSpeedInit(int init) { value = init; } - public int Value { get { return value; } } + public HuskSpeedInit(int value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 88c7443bd1..61f4335669 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -146,7 +146,7 @@ namespace OpenRA.Mods.Common.Traits if (turretInit != null) { var newTurretFacing = (turretInit.Value + newFacing - oldFacing + 255) % 255; - actor.ReplaceInit(new TurretFacingInit(newTurretFacing)); + actor.ReplaceInit(new TurretFacingInit(this, newTurretFacing)); } if (turretsInit != null) diff --git a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs index 2ce6ef3957..0a836d5db3 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs @@ -195,5 +195,5 @@ namespace OpenRA.Mods.Common.Traits } } - public class HiddenUnderFogInit : IActorInit { } + public class HiddenUnderFogInit : RuntimeFlagInit { } } diff --git a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs index fe4d60f56a..601d9b2f61 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs @@ -16,7 +16,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { // Allows third party mods to detect whether an actor was created by PlaceBuilding. - public class PlaceBuildingInit : IActorInit { } + public class PlaceBuildingInit : RuntimeFlagInit { } [Desc("Allows the player to execute build orders.", " Attach this to the player actor.")] public class PlaceBuildingInfo : TraitInfo diff --git a/OpenRA.Mods.Common/Traits/Pluggable.cs b/OpenRA.Mods.Common/Traits/Pluggable.cs index 80f39b4d39..351b43d21e 100644 --- a/OpenRA.Mods.Common/Traits/Pluggable.cs +++ b/OpenRA.Mods.Common/Traits/Pluggable.cs @@ -120,12 +120,9 @@ namespace OpenRA.Mods.Common.Traits } } - public class PlugsInit : IActorInit> + public class PlugsInit : ValueActorInit> { - [DictionaryFromYamlKey] - readonly Dictionary value = new Dictionary(); - public PlugsInit() { } - public PlugsInit(Dictionary init) { value = init; } - public Dictionary Value { get { return value; } } + public PlugsInit(Dictionary value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs b/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs index 0f8f9199b5..6b1cfff386 100644 --- a/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs @@ -110,13 +110,9 @@ namespace OpenRA.Mods.Common.Traits } } - public class ProductionSpawnLocationInit : IActorInit + public class ProductionSpawnLocationInit : ValueActorInit { - [FieldFromYamlKey] - readonly CPos value = CPos.Zero; - - public ProductionSpawnLocationInit() { } - public ProductionSpawnLocationInit(CPos init) { value = init; } - public CPos Value { get { return value; } } + public ProductionSpawnLocationInit(TraitInfo info, CPos value) + : base(info, value) { } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs index 96384ec508..edabc58255 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs @@ -168,13 +168,9 @@ namespace OpenRA.Mods.Common.Traits.Render } } - public class RuntimeNeighbourInit : IActorInit>, ISuppressInitExport + public class RuntimeNeighbourInit : ValueActorInit>, ISuppressInitExport { - [FieldFromYamlKey] - readonly Dictionary value = null; - - public RuntimeNeighbourInit() { } - public RuntimeNeighbourInit(Dictionary init) { value = init; } - public Dictionary Value { get { return value; } } + public RuntimeNeighbourInit(Dictionary value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/Traits/ScriptTags.cs b/OpenRA.Mods.Common/Traits/ScriptTags.cs index c09c4a2d8e..b9f1e6b54d 100644 --- a/OpenRA.Mods.Common/Traits/ScriptTags.cs +++ b/OpenRA.Mods.Common/Traits/ScriptTags.cs @@ -49,13 +49,9 @@ namespace OpenRA.Mods.Common.Traits } /// Allows mappers to 'tag' actors with arbitrary strings that may have meaning in their scripts. - public class ScriptTagsInit : IActorInit + public class ScriptTagsInit : ValueActorInit { - [FieldFromYamlKey] - readonly string[] value = new string[0]; - - public ScriptTagsInit() { } - public ScriptTagsInit(string[] init) { value = init; } - public string[] Value { get { return value; } } + public ScriptTagsInit(string[] value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/Traits/Turreted.cs b/OpenRA.Mods.Common/Traits/Turreted.cs index 60dc686c40..2ed68c8248 100644 --- a/OpenRA.Mods.Common/Traits/Turreted.cs +++ b/OpenRA.Mods.Common/Traits/Turreted.cs @@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits // HACK: The ActorInit system does not support multiple instances of the same type // Make sure that we only return one TurretFacingInit, even for actors with multiple turrets if (ai.TraitInfos().FirstOrDefault() == this) - yield return new TurretFacingInit(PreviewFacing); + yield return new TurretFacingInit(this, PreviewFacing); } IEnumerable IEditorActorOptions.ActorOptions(ActorInfo ai, World world) @@ -66,6 +66,8 @@ namespace OpenRA.Mods.Common.Traits (actor, value) => { actor.RemoveInit(); + + // Force a single global turret facing for multi-turret actors by not passing this TraitInfo instance actor.ReplaceInit(new TurretFacingInit((int)value)); }); } @@ -230,7 +232,7 @@ namespace OpenRA.Mods.Common.Traits var facings = init.GetOrDefault(); if (facings == null) { - facings = new TurretFacingsInit(); + facings = new TurretFacingsInit(new Dictionary()); init.Add(facings); } @@ -243,7 +245,7 @@ namespace OpenRA.Mods.Common.Traits var facings = inits.GetOrDefault(); if (facings == null) { - facings = new DynamicTurretFacingsInit(); + facings = new DynamicTurretFacingsInit(Info, new Dictionary>()); inits.Add(facings); } @@ -267,31 +269,24 @@ namespace OpenRA.Mods.Common.Traits } } - public class TurretFacingInit : IActorInit + public class TurretFacingInit : ValueActorInit { - [FieldFromYamlKey] - readonly int value = 128; + public TurretFacingInit(TraitInfo info, int value) + : base(info, value) { } - public TurretFacingInit() { } - public TurretFacingInit(int init) { value = init; } - public int Value { get { return value; } } + public TurretFacingInit(int value) + : base(value) { } } - public class TurretFacingsInit : IActorInit> + public class TurretFacingsInit : ValueActorInit> { - [DictionaryFromYamlKey] - readonly Dictionary value = new Dictionary(); - - public TurretFacingsInit() { } - public TurretFacingsInit(Dictionary init) { value = init; } - public Dictionary Value { get { return value; } } + public TurretFacingsInit(Dictionary value) + : base(value) { } } - public class DynamicTurretFacingsInit : IActorInit>> + public class DynamicTurretFacingsInit : ValueActorInit>> { - readonly Dictionary> value = new Dictionary>(); - public DynamicTurretFacingsInit() { } - public DynamicTurretFacingsInit(Dictionary> init) { value = init; } - public Dictionary> Value { get { return value; } } + public DynamicTurretFacingsInit(TraitInfo info, Dictionary> value) + : base(info, value) { } } } diff --git a/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs b/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs index 2011e00692..20feb914df 100644 --- a/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs +++ b/OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs @@ -62,12 +62,10 @@ namespace OpenRA.Mods.Common.Traits } } - public class SkipMakeAnimsInit : IActorInit, ISuppressInitExport { } - public class SpawnedByMapInit : IActorInit, ISuppressInitExport + public class SkipMakeAnimsInit : RuntimeFlagInit { } + public class SpawnedByMapInit : ValueActorInit, ISuppressInitExport { - public readonly string Name; - public SpawnedByMapInit(string name) { Name = name; } - - public string Value { get { return Name; } } + public SpawnedByMapInit(string value) + : base(value) { } } } diff --git a/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs index 8eaacf3a69..e41e86684e 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs @@ -19,6 +19,7 @@ using OpenRA.Graphics; using OpenRA.Mods.Common.FileFormats; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; +using OpenRA.Traits; namespace OpenRA.Mods.Common.UtilityCommands { @@ -416,7 +417,7 @@ namespace OpenRA.Mods.Common.UtilityCommands initDict.Add(new FacingInit(255 - facing)); if (section == "INFANTRY") - actor.Add(new SubCellInit(Exts.ParseByte(parts[4]))); + actor.Add(new SubCellInit((SubCell)Exts.ParseByte(parts[4]))); var actorCount = map.ActorDefinitions.Count;