diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 1cce51b4fc..e165120143 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -82,6 +82,7 @@ namespace OpenRA readonly INotifyIdle[] tickIdles; readonly ITargetablePositions[] targetablePositions; WPos[] staticTargetablePositions; + bool created; internal Actor(World world, string name, TypeDictionary initDict) { @@ -138,6 +139,35 @@ namespace OpenRA SyncHashes = TraitsImplementing().Select(sync => new SyncHash(sync)).ToArray(); } + internal void Created() + { + created = true; + + foreach (var t in TraitsImplementing()) + t.Created(this); + + // The initial activity should run before any activities queued by INotifyCreated.Created + // However, we need to know which traits are enabled (via conditions), so wait for after the calls and insert the activity as the first + ICreationActivity creationActivity = null; + foreach (var ica in TraitsImplementing()) + { + if (!ica.IsTraitEnabled()) + continue; + + if (creationActivity != null) + throw new InvalidOperationException("More than one enabled ICreationActivity trait: {0} and {1}".F(creationActivity.GetType().Name, ica.GetType().Name)); + + var activity = ica.GetCreationActivity(); + if (activity == null) + continue; + + creationActivity = ica; + + activity.Queue(CurrentActivity); + CurrentActivity = activity; + } + } + public void Tick() { var wasIdle = IsIdle; @@ -214,11 +244,15 @@ namespace OpenRA { if (!queued) CancelActivity(); + QueueActivity(nextActivity); } public void QueueActivity(Activity nextActivity) { + if (!created) + throw new InvalidOperationException("An activity was queued before the actor was created. Queue it inside the INotifyCreated.Created callback instead."); + if (CurrentActivity == null) CurrentActivity = nextActivity; else diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 3d6ed857d1..effea7e1f3 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using OpenRA.Activities; using OpenRA.FileSystem; using OpenRA.Graphics; using OpenRA.Network; @@ -538,4 +539,7 @@ namespace OpenRA.Traits [RequireExplicitImplementation] public interface IUnlocksRenderPlayer { bool RenderPlayerUnlocked { get; } } + + [RequireExplicitImplementation] + public interface ICreationActivity { Activity GetCreationActivity(); } } diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index af784bba7a..719514767f 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -330,10 +330,10 @@ namespace OpenRA public Actor CreateActor(bool addToWorld, string name, TypeDictionary initDict) { var a = new Actor(this, name, initDict); - foreach (var t in a.TraitsImplementing()) - t.Created(a); + a.Created(); if (addToWorld) Add(a); + return a; } diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 1ec7f7f1b4..12d897558b 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -190,7 +190,7 @@ namespace OpenRA.Mods.Common.Traits public class Aircraft : ITick, ISync, IFacing, IPositionable, IMove, IIssueOrder, IResolveOrder, IOrderVoice, IDeathActorInitModifier, INotifyCreated, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyActorDisposing, INotifyBecomingIdle, - IActorPreviewInitModifier, IIssueDeployOrder, IObservesVariables + IActorPreviewInitModifier, IIssueDeployOrder, IObservesVariables, ICreationActivity { static readonly Pair[] NoCells = { }; @@ -309,8 +309,6 @@ namespace OpenRA.Mods.Common.Traits notifyMoving = self.TraitsImplementing().ToArray(); positionOffsets = self.TraitsImplementing().ToArray(); overrideAircraftLanding = self.TraitOrDefault(); - - self.QueueActivity(MoveIntoWorld(self, moveIntoWorldDelay)); } void INotifyAddedToWorld.AddedToWorld(Actor self) @@ -1164,6 +1162,11 @@ namespace OpenRA.Mods.Common.Traits inits.Add(new DynamicFacingInit(() => Facing)); } + Activity ICreationActivity.GetCreationActivity() + { + return MoveIntoWorld(self, moveIntoWorldDelay); + } + public class AircraftMoveOrderTargeter : IOrderTargeter { readonly Aircraft aircraft; diff --git a/OpenRA.Mods.Common/Traits/Harvester.cs b/OpenRA.Mods.Common/Traits/Harvester.cs index f42200e438..eda886730b 100644 --- a/OpenRA.Mods.Common/Traits/Harvester.cs +++ b/OpenRA.Mods.Common/Traits/Harvester.cs @@ -130,8 +130,6 @@ namespace OpenRA.Mods.Common.Traits mobile = self.Trait(); resLayer = self.World.WorldActor.Trait(); claimLayer = self.World.WorldActor.Trait(); - - self.QueueActivity(new CallFunc(() => ChooseNewProc(self, null))); } void INotifyCreated.Created(Actor self) @@ -141,6 +139,8 @@ namespace OpenRA.Mods.Common.Traits conditionManager = self.TraitOrDefault(); UpdateCondition(self); + self.QueueActivity(new CallFunc(() => ChooseNewProc(self, null))); + // Note: This is queued in a FrameEndTask because otherwise the activity is dropped/overridden while moving out of a factory. if (Info.SearchOnCreation) self.World.AddFrameEndTask(w => self.QueueActivity(new FindAndDeliverResources(self))); diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 86204e2d31..8b3a722bc7 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -138,7 +138,7 @@ namespace OpenRA.Mods.Common.Traits } } - public class Mobile : PausableConditionalTrait, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, ITick, + public class Mobile : PausableConditionalTrait, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, ITick, ICreationActivity, IFacing, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier, INotifyBecomingIdle { readonly Actor self; @@ -256,7 +256,6 @@ namespace OpenRA.Mods.Common.Traits Locomotor = self.World.WorldActor.TraitsImplementing() .Single(l => l.Info.Name == Info.Locomotor); - self.QueueActivity(MoveIntoWorld(self, moveIntoWorldDelay)); base.Created(self); } @@ -897,6 +896,11 @@ namespace OpenRA.Mods.Common.Traits } } + Activity ICreationActivity.GetCreationActivity() + { + return MoveIntoWorld(self, moveIntoWorldDelay); + } + class MoveOrderTargeter : IOrderTargeter { readonly Mobile mobile;