From bab34252ddbff832615295a3834f83f418cb1ff4 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 6 Oct 2018 21:37:47 +0000 Subject: [PATCH] Add support for capture delays and conditions. CaptureDelay is defined per-trait, and the shortest delay will be used if multiple traits are enabled. CapturingCondition and BeingCapturedCondition are global, and are granted when any capture is in progress. The capture period is defined from when the unit reaches the cell next to the target, and either starts to wait or enter the target through to when the capture succeeds or fails. --- OpenRA.Mods.Common/Activities/CaptureActor.cs | 24 ++++++ OpenRA.Mods.Common/Activities/Enter.cs | 12 ++- OpenRA.Mods.Common/Traits/CaptureManager.cs | 82 ++++++++++++++++++- OpenRA.Mods.Common/Traits/Captures.cs | 3 + 4 files changed, 119 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.Common/Activities/CaptureActor.cs b/OpenRA.Mods.Common/Activities/CaptureActor.cs index 3f1cc78d4a..90c4fc2427 100644 --- a/OpenRA.Mods.Common/Activities/CaptureActor.cs +++ b/OpenRA.Mods.Common/Activities/CaptureActor.cs @@ -38,6 +38,13 @@ namespace OpenRA.Mods.Common.Activities return !actor.IsDead && !targetManager.BeingCaptured && targetManager.CanBeTargetedBy(actor, self, manager); } + protected override bool TryStartEnter(Actor self) + { + // CanEnter is only called when the actor is ready to start entering the target. + // We can (ab)use this as a notification that the capture is starting. + return manager.StartCapture(self, actor, targetManager); + } + protected override void OnInside(Actor self) { if (!CanReserve(self)) @@ -94,6 +101,23 @@ namespace OpenRA.Mods.Common.Activities }); } + protected override void OnLastRun(Actor self) + { + CancelCapture(self); + base.OnLastRun(self); + } + + protected override void OnActorDispose(Actor self) + { + CancelCapture(self); + base.OnActorDispose(self); + } + + void CancelCapture(Actor self) + { + manager.CancelCapture(self, actor, targetManager); + } + public override Activity Tick(Actor self) { if (!targetManager.CanBeTargetedBy(actor, self, manager)) diff --git a/OpenRA.Mods.Common/Activities/Enter.cs b/OpenRA.Mods.Common/Activities/Enter.cs index d9c3f51824..1fe061d0ca 100644 --- a/OpenRA.Mods.Common/Activities/Enter.cs +++ b/OpenRA.Mods.Common/Activities/Enter.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Activities public abstract class Enter : Activity { public enum ReserveStatus { None, TooFar, Pending, Ready } - enum EnterState { ApproachingOrEntering, Inside, Exiting, Done } + enum EnterState { ApproachingOrEntering, WaitingToEnter, Inside, Exiting, Done } readonly IMove move; readonly int maxTries = 0; @@ -58,6 +58,12 @@ namespace OpenRA.Mods.Common.Activities protected virtual void Unreserve(Actor self, bool abort) { } protected virtual void OnInside(Actor self) { } + /// + /// Called when the actor is ready to transition from approaching to entering the target. + /// Return true to start entering, or false to wait in the WaitingToEnter state. + /// + protected virtual bool TryStartEnter(Actor self) { return true; } + protected bool TryGetAlternateTargetInCircle( Actor self, WDist radius, Action update, Func primaryFilter, Func[] preferenceFilters = null) { @@ -187,6 +193,10 @@ namespace OpenRA.Mods.Common.Activities break; // Reserved target -> start entering target } + // Can we enter yet? + if (!TryStartEnter(self)) + return EnterState.WaitingToEnter; + // Entering isEnteringOrInside = true; savedPos = self.CenterPosition; // Save position of self, before entering, for returning on exit diff --git a/OpenRA.Mods.Common/Traits/CaptureManager.cs b/OpenRA.Mods.Common/Traits/CaptureManager.cs index d0aa5ddf4a..486432cc33 100644 --- a/OpenRA.Mods.Common/Traits/CaptureManager.cs +++ b/OpenRA.Mods.Common/Traits/CaptureManager.cs @@ -20,10 +20,24 @@ namespace OpenRA.Mods.Common.Traits public sealed class CaptureType { CaptureType() { } } [Desc("Manages Captures and Capturable traits on an actor.")] - public class CaptureManagerInfo : TraitInfo { } + public class CaptureManagerInfo : ITraitInfo + { + [GrantedConditionReference] + [Desc("Condition granted when capturing an actor.")] + public readonly string CapturingCondition = null; + + [GrantedConditionReference] + [Desc("Condition granted when being captured by another actor.")] + public readonly string BeingCapturedCondition = null; + + public virtual object Create(ActorInitializer init) { return new CaptureManager(this); } + } public class CaptureManager : INotifyCreated, INotifyCapture { + readonly CaptureManagerInfo info; + ConditionManager conditionManager; + BitSet allyCapturableTypes; BitSet neutralCapturableTypes; BitSet enemyCapturableTypes; @@ -32,10 +46,24 @@ namespace OpenRA.Mods.Common.Traits IEnumerable enabledCapturable; IEnumerable enabledCaptures; + // Related to a specific capture in process + Actor currentTarget; + CaptureManager currentTargetManager; + int currentTargetDelay; + int capturingToken = ConditionManager.InvalidConditionToken; + int beingCapturedToken = ConditionManager.InvalidConditionToken; + public bool BeingCaptured { get; private set; } + public CaptureManager(CaptureManagerInfo info) + { + this.info = info; + } + void INotifyCreated.Created(Actor self) { + conditionManager = self.TraitOrDefault(); + enabledCapturable = self.TraitsImplementing() .ToArray() .Where(Exts.IsTraitEnabled); @@ -121,5 +149,57 @@ namespace OpenRA.Mods.Common.Traits BeingCaptured = true; self.World.AddFrameEndTask(w => BeingCaptured = false); } + + /// + /// Called by CaptureActor when the activity is ready to enter and capture the target. + /// This method grants the capturing conditions on the captor and target and returns + /// true if the captor is able to start entering or false if it needs to wait. + /// + public bool StartCapture(Actor self, Actor target, CaptureManager targetManager) + { + if (target != currentTarget) + { + if (currentTarget != null) + CancelCapture(self, currentTarget, currentTargetManager); + + currentTarget = target; + currentTargetManager = targetManager; + currentTargetDelay = 0; + } + else + currentTargetDelay += 1; + + if (conditionManager != null && !string.IsNullOrEmpty(info.CapturingCondition) && + capturingToken == ConditionManager.InvalidConditionToken) + capturingToken = conditionManager.GrantCondition(self, info.CapturingCondition); + + if (targetManager.conditionManager != null && !string.IsNullOrEmpty(targetManager.info.BeingCapturedCondition) && + targetManager.beingCapturedToken == ConditionManager.InvalidConditionToken) + targetManager.beingCapturedToken = targetManager.conditionManager.GrantCondition(target, targetManager.info.BeingCapturedCondition); + + foreach (var c in enabledCaptures.OrderBy(c => c.Info.CaptureDelay)) + if (targetManager.CanBeTargetedBy(target, self, c) && c.Info.CaptureDelay <= currentTargetDelay) + return true; + + return false; + } + + /// + /// Called by CaptureActor when the activity finishes or is cancelled + /// This method revokes the capturing conditions on the captor and target + /// and resets any capturing progress. + /// + public void CancelCapture(Actor self, Actor target, CaptureManager targetManager) + { + if (capturingToken != ConditionManager.InvalidConditionToken) + capturingToken = conditionManager.RevokeCondition(self, capturingToken); + + if (targetManager.beingCapturedToken != ConditionManager.InvalidConditionToken) + targetManager.beingCapturedToken = targetManager.conditionManager.RevokeCondition(self, targetManager.beingCapturedToken); + + currentTarget = null; + currentTargetManager = null; + currentTargetDelay = 0; + } } } diff --git a/OpenRA.Mods.Common/Traits/Captures.cs b/OpenRA.Mods.Common/Traits/Captures.cs index c8bc5c786a..d8e6697456 100644 --- a/OpenRA.Mods.Common/Traits/Captures.cs +++ b/OpenRA.Mods.Common/Traits/Captures.cs @@ -32,6 +32,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Sabotage damage expressed as a percentage of maximum target health.")] public readonly int SabotageHPRemoval = 50; + [Desc("Delay (in ticks) that to wait next to the target before initiating the capture.")] + public readonly int CaptureDelay = 0; + [Desc("Experience granted to the capturing player.")] public readonly int PlayerExperience = 0;