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.
130 lines
3.4 KiB
C#
130 lines
3.4 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2018 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 System;
|
|
using System.Linq;
|
|
using OpenRA.Activities;
|
|
using OpenRA.Mods.Common.Traits;
|
|
using OpenRA.Traits;
|
|
|
|
namespace OpenRA.Mods.Common.Activities
|
|
{
|
|
public class CaptureActor : Enter
|
|
{
|
|
readonly Actor actor;
|
|
readonly Building building;
|
|
readonly CaptureManager targetManager;
|
|
readonly CaptureManager manager;
|
|
|
|
public CaptureActor(Actor self, Actor target)
|
|
: base(self, target, EnterBehaviour.Dispose)
|
|
{
|
|
actor = target;
|
|
building = actor.TraitOrDefault<Building>();
|
|
manager = self.Trait<CaptureManager>();
|
|
targetManager = target.Trait<CaptureManager>();
|
|
}
|
|
|
|
protected override bool CanReserve(Actor self)
|
|
{
|
|
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))
|
|
return;
|
|
|
|
if (building != null && !building.Lock())
|
|
return;
|
|
|
|
self.World.AddFrameEndTask(w =>
|
|
{
|
|
if (building != null && building.Locked)
|
|
building.Unlock();
|
|
|
|
// Prioritize capturing over sabotaging
|
|
var captures = manager.ValidCapturesWithLowestSabotageThreshold(self, actor, targetManager);
|
|
if (captures == null)
|
|
return;
|
|
|
|
// Sabotage instead of capture
|
|
if (captures.Info.SabotageThreshold > 0 && !actor.Owner.NonCombatant)
|
|
{
|
|
var health = actor.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);
|
|
actor.InflictDamage(self, new Damage(damage));
|
|
|
|
self.Dispose();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Do the capture
|
|
var oldOwner = actor.Owner;
|
|
|
|
actor.ChangeOwner(self.Owner);
|
|
|
|
foreach (var t in actor.TraitsImplementing<INotifyCapture>())
|
|
t.OnCapture(actor, self, oldOwner, self.Owner);
|
|
|
|
if (building != null && building.Locked)
|
|
building.Unlock();
|
|
|
|
if (self.Owner.Stances[oldOwner].HasStance(captures.Info.PlayerExperienceStances))
|
|
{
|
|
var exp = self.Owner.PlayerActor.TraitOrDefault<PlayerExperience>();
|
|
if (exp != null)
|
|
exp.GiveExperience(captures.Info.PlayerExperience);
|
|
}
|
|
|
|
self.Dispose();
|
|
});
|
|
}
|
|
|
|
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))
|
|
Cancel(self);
|
|
|
|
return base.Tick(self);
|
|
}
|
|
}
|
|
}
|