Add CompositeActorInit and simplify chronoshift inits.

This commit is contained in:
Paul Chote
2020-06-02 19:43:35 +01:00
committed by teinarss
parent 0eb0041f90
commit 10aac03f75
4 changed files with 121 additions and 47 deletions

View File

@@ -86,7 +86,7 @@ namespace OpenRA
* 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<T> which hides the low-level plumbing.
* - Most ActorInits will want to inherit either ValueActorInit<T> or CompositeActorInit which hide 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
@@ -134,6 +134,54 @@ namespace OpenRA
}
}
public abstract class CompositeActorInit : ActorInit
{
protected CompositeActorInit(TraitInfo info)
: base(info.InstanceName) { }
protected CompositeActorInit()
: base() { }
public virtual void Initialize(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
}
public virtual void Initialize(Dictionary<string, object> values)
{
object value;
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
if (!sa.Serialize)
continue;
if (values.TryGetValue(field.Name, out value))
field.SetValue(this, value);
}
}
public virtual Dictionary<string, Type> InitializeArgs()
{
var dict = new Dictionary<string, Type>();
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
if (!sa.Serialize)
continue;
dict[field.Name] = field.FieldType;
}
return dict;
}
public override MiniYaml Save()
{
return FieldSaver.Save(this);
}
}
public class LocationInit : ValueActorInit<CPos>
{
public LocationInit(TraitInfo info, CPos value)

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
@@ -60,14 +59,18 @@ namespace OpenRA.Mods.Cnc.Traits
: base(info)
{
self = init.Self;
ReturnTicks = init.GetValue<ChronoshiftReturnInit, int>(info, 0);
duration = init.GetValue<ChronoshiftDurationInit, int>(info, 0);
Origin = init.GetValue<ChronoshiftOriginInit, CPos>(info, CPos.Zero);
var returnInit = init.GetOrDefault<ChronoshiftReturnInit>(info);
if (returnInit != null)
{
ReturnTicks = returnInit.Ticks;
duration = returnInit.Duration;
Origin = returnInit.Origin;
// Defer to the end of tick as the lazy value may reference an actor that hasn't been created yet
var chronosphereInit = init.GetOrDefault<ChronoshiftChronosphereInit>(info);
if (chronosphereInit != null)
init.World.AddFrameEndTask(w => chronosphere = chronosphereInit.Value.Actor(init.World).Value);
if (returnInit.Chronosphere != null)
init.World.AddFrameEndTask(w => chronosphere = returnInit.Chronosphere.Actor(init.World).Value);
}
}
void ITick.Tick(Actor self)
@@ -91,7 +94,7 @@ namespace OpenRA.Mods.Cnc.Traits
typeof(Actor).GetProperty("CurrentActivity").SetValue(self, null);
// The actor is killed using Info.DamageTypes if the teleport fails
self.QueueActivity(false, new Teleport(chronosphere, Origin, null, true, killCargo, Info.ChronoshiftSound,
self.QueueActivity(false, new Teleport(chronosphere ?? self, Origin, null, true, killCargo, Info.ChronoshiftSound,
false, true, Info.DamageTypes));
}
}
@@ -166,38 +169,26 @@ namespace OpenRA.Mods.Cnc.Traits
if (IsTraitDisabled || !Info.ReturnToOrigin || ReturnTicks <= 0)
return;
init.Add(new ChronoshiftOriginInit(Origin));
init.Add(new ChronoshiftReturnInit(ReturnTicks));
init.Add(new ChronoshiftDurationInit(duration));
if (chronosphere != self)
init.Add(new ChronoshiftChronosphereInit(chronosphere));
init.Add(new ChronoshiftReturnInit(ReturnTicks, duration, Origin, chronosphere));
}
void IDeathActorInitModifier.ModifyDeathActorInit(Actor self, TypeDictionary init) { ModifyActorInit(init); }
void ITransformActorInitModifier.ModifyTransformActorInit(Actor self, TypeDictionary init) { ModifyActorInit(init); }
}
public class ChronoshiftReturnInit : ValueActorInit<int>
public class ChronoshiftReturnInit : CompositeActorInit
{
public ChronoshiftReturnInit(int value)
: base(value) { }
}
public readonly int Ticks;
public readonly int Duration;
public readonly CPos Origin;
public readonly ActorInitActorReference Chronosphere;
public class ChronoshiftDurationInit : ValueActorInit<int>
public ChronoshiftReturnInit(int ticks, int duration, CPos origin, Actor chronosphere)
{
public ChronoshiftDurationInit(int value)
: base(value) { }
Ticks = ticks;
Duration = duration;
Origin = origin;
Chronosphere = chronosphere;
}
public class ChronoshiftOriginInit : ValueActorInit<CPos>
{
public ChronoshiftOriginInit(CPos value)
: base(value) { }
}
public class ChronoshiftChronosphereInit : ValueActorInit<ActorInitActorReference>
{
public ChronoshiftChronosphereInit(Actor value)
: base(value) { }
}
}

View File

@@ -96,14 +96,18 @@ namespace OpenRA.Mods.Cnc.Traits
wsb = self.TraitsImplementing<WithSpriteBody>().Single(w => w.Info.Name == info.Body);
faction = init.GetValue<FactionInit, string>(info, self.Owner.Faction.InternalName);
returnTicks = init.GetValue<ChronoshiftReturnInit, int>(info, 0);
duration = init.GetValue<ChronoshiftDurationInit, int>(info, 0);
origin = init.GetValue<ChronoshiftOriginInit, CPos>(info, CPos.Zero);
var returnInit = init.GetOrDefault<ChronoshiftReturnInit>(info);
if (returnInit != null)
{
returnTicks = returnInit.Ticks;
duration = returnInit.Duration;
origin = returnInit.Origin;
// Defer to the end of tick as the lazy value may reference an actor that hasn't been created yet
var chronosphereInit = init.GetOrDefault<ChronoshiftChronosphereInit>(info);
if (chronosphereInit != null)
init.World.AddFrameEndTask(w => chronosphere = chronosphereInit.Value.Actor(init.World).Value);
if (returnInit.Chronosphere != null)
init.World.AddFrameEndTask(w => chronosphere = returnInit.Chronosphere.Actor(init.World).Value);
}
}
IEnumerable<VariableObserver> IObservesVariables.GetVariableObservers()
@@ -228,11 +232,7 @@ namespace OpenRA.Mods.Cnc.Traits
if (returnTicks <= 0)
return;
init.Add(new ChronoshiftOriginInit(origin));
init.Add(new ChronoshiftReturnInit(returnTicks));
init.Add(new ChronoshiftDurationInit(duration));
if (chronosphere != self)
init.Add(new ChronoshiftChronosphereInit(chronosphere));
init.Add(new ChronoshiftReturnInit(returnTicks, duration, origin, chronosphere));
}
void IDeathActorInitModifier.ModifyDeathActorInit(Actor self, TypeDictionary init) { ModifyActorInit(init); }

View File

@@ -40,6 +40,41 @@ namespace OpenRA.Mods.Common.Scripting
if (initInstance.Length > 1)
initType.GetField("InstanceName").SetValue(init, initInstance[1]);
var compositeInit = init as CompositeActorInit;
var tableValue = value as LuaTable;
if (tableValue != null && compositeInit != null)
{
var args = compositeInit.InitializeArgs();
var initValues = new Dictionary<string, object>();
foreach (var kv in tableValue)
{
using (kv.Key)
using (kv.Value)
{
var key = kv.Key.ToString();
Type type;
if (!args.TryGetValue(key, out type))
throw new LuaException("Unknown initializer type '{0}.{1}'".F(initInstance[0], key));
object clrValue;
var isActorReference = type == typeof(ActorInitActorReference);
if (isActorReference)
type = kv.Value is LuaString ? typeof(string) : typeof(Actor);
if (!kv.Value.TryGetClrValue(type, out clrValue))
throw new LuaException("Invalid data type for '{0}.{1}' (expected {2}, got {3})".F(initInstance[0], key, type.Name, kv.Value.WrappedClrType()));
if (isActorReference)
clrValue = type == typeof(string) ? new ActorInitActorReference((string)clrValue) : new ActorInitActorReference((Actor)clrValue);
initValues[key] = clrValue;
}
}
compositeInit.Initialize(initValues);
return init;
}
var initializers = initType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.Name == "Initialize" && m.GetParameters().Length == 1);