Allow lua API to bind a minimal set of properties to dead actors.

This commit is contained in:
Paul Chote
2014-09-28 14:25:13 +13:00
parent b010f1df1e
commit ca6119821f
5 changed files with 68 additions and 27 deletions

View File

@@ -212,6 +212,9 @@ namespace OpenRA
World.traitDict.RemoveActor(this); World.traitDict.RemoveActor(this);
Destroyed = true; Destroyed = true;
if (luaInterface != null)
luaInterface.Value.OnActorDestroyed();
}); });
} }

View File

@@ -9,6 +9,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace OpenRA.Scripting namespace OpenRA.Scripting
@@ -18,15 +19,33 @@ namespace OpenRA.Scripting
readonly Actor actor; 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 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); } protected override string MemberNotFoundError(string memberName)
{
var actorName = actor.Info.Name;
if (actor.IsDead())
actorName += " (dead)";
return "Actor '{0}' does not define a property '{1}'".F(actorName, memberName);
}
public ScriptActorInterface(ScriptContext context, Actor actor) public ScriptActorInterface(ScriptContext context, Actor actor)
: base(context) : base(context)
{ {
this.actor = actor; this.actor = actor;
InitializeBindings();
}
void InitializeBindings()
{
var commandClasses = context.ActorCommands[actor.Info].AsEnumerable();
// Destroyed actors cannot have their traits queried
if (actor.Destroyed)
commandClasses = commandClasses.Where(c => c.HasAttribute<ExposedForDestroyedActors>());
var args = new object[] { context, actor }; var args = new object[] { context, actor };
var objects = context.ActorCommands[actor.Info].Select(cg => var objects = commandClasses.Select(cg =>
{ {
var groupCtor = cg.GetConstructor(new Type[] { typeof(ScriptContext), typeof(Actor) }); var groupCtor = cg.GetConstructor(new Type[] { typeof(ScriptContext), typeof(Actor) });
return groupCtor.Invoke(args); return groupCtor.Invoke(args);
@@ -34,5 +53,11 @@ namespace OpenRA.Scripting
Bind(objects); Bind(objects);
} }
public void OnActorDestroyed()
{
// Regenerate bindings to remove access to bogus trait state
InitializeBindings();
}
} }
} }

View File

@@ -39,6 +39,9 @@ namespace OpenRA.Scripting
public ScriptPropertyGroupAttribute(string category) { Category = category; } public ScriptPropertyGroupAttribute(string category) { Category = category; }
} }
// For property groups that are safe to initialize invoke on destroyed actors
public sealed class ExposedForDestroyedActors : Attribute { }
public sealed class ScriptActorPropertyActivityAttribute : Attribute { } public sealed class ScriptActorPropertyActivityAttribute : Attribute { }
public abstract class ScriptActorProperties public abstract class ScriptActorProperties

View File

@@ -15,18 +15,14 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA.Scripting namespace OpenRA.Mods.RA.Scripting
{ {
[ExposedForDestroyedActors]
[ScriptPropertyGroup("General")] [ScriptPropertyGroup("General")]
public class GeneralProperties : ScriptActorProperties public class BaseActorProperties : ScriptActorProperties
{ {
readonly IFacing facing; // Note: This class must not make any trait queries so that this
readonly AutoTarget autotarget; // remains safe to call on dead actors.
public BaseActorProperties(ScriptContext context, Actor self)
public GeneralProperties(ScriptContext context, Actor self) : base(context, self) { }
: base(context, self)
{
facing = self.TraitOrDefault<IFacing>();
autotarget = self.TraitOrDefault<AutoTarget>();
}
[Desc("Specifies whether the actor is in the world.")] [Desc("Specifies whether the actor is in the world.")]
public bool IsInWorld public bool IsInWorld
@@ -45,21 +41,44 @@ namespace OpenRA.Mods.RA.Scripting
} }
} }
[Desc("Specifies whether the actor is alive or dead.")]
public bool IsDead { get { return self.IsDead(); } }
[Desc("Specifies whether the actor is idle (not performing any activities).")] [Desc("Specifies whether the actor is idle (not performing any activities).")]
public bool IsIdle { get { return self.IsIdle; } } public bool IsIdle { get { return self.IsIdle; } }
[Desc("The actor position in cell coordinates.")]
public CPos Location { get { return self.Location; } }
[Desc("The actor position in world coordinates.")]
public WPos CenterPosition { get { return self.CenterPosition; } }
[Desc("The player that owns the actor.")] [Desc("The player that owns the actor.")]
public Player Owner { get { return self.Owner; } } public Player Owner { get { return self.Owner; } }
[Desc("The type of the actor (e.g. \"e1\").")] [Desc("The type of the actor (e.g. \"e1\").")]
public string Type { get { return self.Info.Name; } } public string Type { get { return self.Info.Name; } }
[Desc("Test whether an actor has a specific property.")]
public bool HasProperty(string name)
{
return self.HasScriptProperty(name);
}
}
[ScriptPropertyGroup("General")]
public class GeneralProperties : ScriptActorProperties
{
readonly IFacing facing;
readonly AutoTarget autotarget;
public GeneralProperties(ScriptContext context, Actor self)
: base(context, self)
{
facing = self.TraitOrDefault<IFacing>();
autotarget = self.TraitOrDefault<AutoTarget>();
}
[Desc("The actor position in cell coordinates.")]
public CPos Location { get { return self.Location; } }
[Desc("The actor position in world coordinates.")]
public WPos CenterPosition { get { return self.CenterPosition; } }
[Desc("The direction that the actor is facing.")] [Desc("The direction that the actor is facing.")]
public int Facing public int Facing
{ {
@@ -129,11 +148,5 @@ namespace OpenRA.Mods.RA.Scripting
autotarget.Stance = stance; autotarget.Stance = stance;
} }
} }
[Desc("Test whether an actor has a specific property.")]
public bool HasProperty(string name)
{
return self.HasScriptProperty(name);
}
} }
} }

View File

@@ -32,9 +32,6 @@ namespace OpenRA.Mods.RA.Scripting
[Desc("Maximum health of the actor.")] [Desc("Maximum health of the actor.")]
public int MaxHealth { get { return health.MaxHP; } } public int MaxHealth { get { return health.MaxHP; } }
[Desc("Specifies whether the actor is alive or dead.")]
public bool IsDead { get { return health.IsDead; } }
} }
[ScriptPropertyGroup("General")] [ScriptPropertyGroup("General")]