159 lines
4.6 KiB
C#
159 lines
4.6 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version. For more
|
|
* information, see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using OpenRA.Mods.Common.Traits;
|
|
using OpenRA.Primitives;
|
|
using OpenRA.Traits;
|
|
|
|
namespace OpenRA.Mods.Common.Activities
|
|
{
|
|
public class CaptureActor : Enter
|
|
{
|
|
readonly CaptureManager manager;
|
|
|
|
Actor enterActor;
|
|
CaptureManager enterCaptureManager;
|
|
|
|
public CaptureActor(Actor self, in Target target)
|
|
: base(self, target, Color.Crimson)
|
|
{
|
|
manager = self.Trait<CaptureManager>();
|
|
}
|
|
|
|
protected override void TickInner(Actor self, in Target target, bool targetIsDeadOrHiddenActor)
|
|
{
|
|
if (target.Type == TargetType.Actor && enterActor != target.Actor)
|
|
{
|
|
enterActor = target.Actor;
|
|
enterCaptureManager = target.Actor.TraitOrDefault<CaptureManager>();
|
|
}
|
|
|
|
if (!targetIsDeadOrHiddenActor && target.Type != TargetType.FrozenActor &&
|
|
(enterCaptureManager == null || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager)))
|
|
Cancel(self, true);
|
|
}
|
|
|
|
protected override bool TryStartEnter(Actor self, Actor targetActor)
|
|
{
|
|
if (enterActor != targetActor)
|
|
{
|
|
enterActor = targetActor;
|
|
enterCaptureManager = targetActor.TraitOrDefault<CaptureManager>();
|
|
}
|
|
|
|
// Make sure we can still capture the target before entering
|
|
// (but not before, because this may stop the actor in the middle of nowhere)
|
|
if (enterCaptureManager == null || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager))
|
|
{
|
|
Cancel(self, true);
|
|
return false;
|
|
}
|
|
|
|
// StartCapture returns false when a capture delay is enabled
|
|
// We wait until it returns true before allowing entering the target
|
|
if (!manager.StartCapture(self, enterActor, enterCaptureManager, out var captures))
|
|
return false;
|
|
|
|
if (!captures.Info.ConsumedByCapture)
|
|
{
|
|
// Immediately capture without entering or disposing the actor
|
|
DoCapture(self, captures);
|
|
Cancel(self, true);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected override void OnEnterComplete(Actor self, Actor targetActor)
|
|
{
|
|
// Make sure the target hasn't changed while entering
|
|
// OnEnterComplete is only called if targetActor is alive
|
|
if (enterActor != targetActor)
|
|
return;
|
|
|
|
if (enterCaptureManager.BeingCaptured || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager))
|
|
return;
|
|
|
|
// Prioritize capturing over sabotaging
|
|
var captures = manager.ValidCapturesWithLowestSabotageThreshold(self, enterActor, enterCaptureManager);
|
|
if (captures == null)
|
|
return;
|
|
|
|
DoCapture(self, captures);
|
|
}
|
|
|
|
void DoCapture(Actor self, Captures captures)
|
|
{
|
|
var oldOwner = enterActor.Owner;
|
|
self.World.AddFrameEndTask(w =>
|
|
{
|
|
// The target died or was already captured during this tick
|
|
if (enterActor.IsDead || oldOwner != enterActor.Owner)
|
|
return;
|
|
|
|
// Sabotage instead of capture
|
|
if (captures.Info.SabotageThreshold > 0 && !enterActor.Owner.NonCombatant)
|
|
{
|
|
var health = enterActor.Trait<IHealth>();
|
|
|
|
// Cast to long to avoid overflow when multiplying by the health
|
|
if (100 * (long)health.HP > captures.Info.SabotageThreshold * (long)health.MaxHP)
|
|
{
|
|
var damage = (int)((long)health.MaxHP * captures.Info.SabotageHPRemoval / 100);
|
|
enterActor.InflictDamage(self, new Damage(damage, captures.Info.SabotageDamageTypes));
|
|
|
|
if (captures.Info.ConsumedByCapture)
|
|
self.Dispose();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Do the capture
|
|
enterActor.ChangeOwnerSync(self.Owner);
|
|
|
|
foreach (var t in enterActor.TraitsImplementing<INotifyCapture>())
|
|
t.OnCapture(enterActor, self, oldOwner, self.Owner, captures.Info.CaptureTypes);
|
|
|
|
if (self.Owner.Stances[oldOwner].HasStance(captures.Info.PlayerExperienceStances))
|
|
self.Owner.PlayerActor.TraitOrDefault<PlayerExperience>()?.GiveExperience(captures.Info.PlayerExperience);
|
|
|
|
if (captures.Info.ConsumedByCapture)
|
|
self.Dispose();
|
|
});
|
|
}
|
|
|
|
protected override void OnLastRun(Actor self)
|
|
{
|
|
CancelCapture(self);
|
|
base.OnLastRun(self);
|
|
}
|
|
|
|
protected override void OnActorDispose(Actor self)
|
|
{
|
|
CancelCapture(self);
|
|
base.OnActorDispose(self);
|
|
}
|
|
|
|
public override void Cancel(Actor self, bool keepQueue = false)
|
|
{
|
|
CancelCapture(self);
|
|
base.Cancel(self, keepQueue);
|
|
}
|
|
|
|
void CancelCapture(Actor self)
|
|
{
|
|
manager.CancelCapture(self, enterActor, enterCaptureManager);
|
|
}
|
|
}
|
|
}
|