Merge pull request #6662 from atlimit8/CaptureActor-Enter
Enter subclasses & fix for 6658
This commit is contained in:
@@ -13,36 +13,45 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
class CaptureActor : Activity
|
class CaptureActor : Enter
|
||||||
{
|
{
|
||||||
Target target;
|
readonly Actor actor;
|
||||||
|
readonly Capturable capturable;
|
||||||
|
readonly CapturesInfo capturesInfo;
|
||||||
|
|
||||||
public CaptureActor(Target target) { this.target = target; }
|
public CaptureActor(Actor self, Actor target)
|
||||||
|
: base(self, target)
|
||||||
public override Activity Tick(Actor self)
|
|
||||||
{
|
{
|
||||||
if (IsCanceled || !target.IsValidFor(self))
|
actor = target;
|
||||||
return NextActivity;
|
capturesInfo = self.Info.Traits.Get<CapturesInfo>();
|
||||||
|
capturable = target.Trait<Capturable>();
|
||||||
|
}
|
||||||
|
|
||||||
if (target.Type != TargetType.Actor)
|
protected override bool CanReserve(Actor self)
|
||||||
return NextActivity;
|
{
|
||||||
|
return !capturable.BeingCaptured && capturable.Info.CanBeTargetedBy(self, actor.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInside(Actor self)
|
||||||
|
{
|
||||||
|
if (actor.IsDead() || capturable.BeingCaptured)
|
||||||
|
return;
|
||||||
|
|
||||||
var actor = target.Actor;
|
|
||||||
var b = actor.TraitOrDefault<Building>();
|
var b = actor.TraitOrDefault<Building>();
|
||||||
if (b != null && b.Locked)
|
if (b != null && !b.Lock())
|
||||||
return NextActivity;
|
return;
|
||||||
|
|
||||||
var capturesInfo = self.Info.Traits.Get<CapturesInfo>();
|
|
||||||
var capturableInfo = actor.Info.Traits.Get<CapturableInfo>();
|
|
||||||
|
|
||||||
var health = actor.Trait<Health>();
|
|
||||||
|
|
||||||
self.World.AddFrameEndTask(w =>
|
self.World.AddFrameEndTask(w =>
|
||||||
{
|
{
|
||||||
if (actor.IsDead())
|
if (b != null && b.Locked)
|
||||||
|
b.Unlock();
|
||||||
|
|
||||||
|
if (actor.IsDead() || capturable.BeingCaptured)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var lowEnoughHealth = health.HP <= capturableInfo.CaptureThreshold * health.MaxHP;
|
var health = actor.Trait<Health>();
|
||||||
|
|
||||||
|
var lowEnoughHealth = health.HP <= capturable.Info.CaptureThreshold * health.MaxHP;
|
||||||
if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant)
|
if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant)
|
||||||
{
|
{
|
||||||
var oldOwner = actor.Owner;
|
var oldOwner = actor.Owner;
|
||||||
@@ -63,8 +72,6 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
|
|
||||||
self.Destroy();
|
self.Destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,28 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Effects;
|
using OpenRA.Effects;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
class Demolish : Activity
|
class Demolish : Enter
|
||||||
{
|
{
|
||||||
readonly Target target;
|
readonly Actor target;
|
||||||
|
readonly IEnumerable<IDemolishable> demolishables;
|
||||||
readonly int delay;
|
readonly int delay;
|
||||||
readonly int flashes;
|
readonly int flashes;
|
||||||
readonly int flashesDelay;
|
readonly int flashesDelay;
|
||||||
readonly int flashInterval;
|
readonly int flashInterval;
|
||||||
readonly int flashDuration;
|
readonly int flashDuration;
|
||||||
|
|
||||||
public Demolish(Actor target, int delay, int flashes, int flashesDelay, int flashInterval, int flashDuration)
|
public Demolish(Actor self, Actor target, int delay, int flashes, int flashesDelay, int flashInterval, int flashDuration)
|
||||||
|
: base(self, target)
|
||||||
{
|
{
|
||||||
this.target = Target.FromActor(target);
|
this.target = target;
|
||||||
|
demolishables = target.TraitsImplementing<IDemolishable>();
|
||||||
this.delay = delay;
|
this.delay = delay;
|
||||||
this.flashes = flashes;
|
this.flashes = flashes;
|
||||||
this.flashesDelay = flashesDelay;
|
this.flashesDelay = flashesDelay;
|
||||||
@@ -33,39 +37,35 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
this.flashDuration = flashDuration;
|
this.flashDuration = flashDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
protected override bool CanReserve(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled || !target.IsValidFor(self))
|
return demolishables.Any(i => i.IsValidTarget(target, self));
|
||||||
return NextActivity;
|
}
|
||||||
|
|
||||||
|
protected override void OnInside(Actor self)
|
||||||
|
{
|
||||||
self.World.AddFrameEndTask(w =>
|
self.World.AddFrameEndTask(w =>
|
||||||
{
|
{
|
||||||
|
if (target.IsDead())
|
||||||
|
return;
|
||||||
|
|
||||||
for (var f = 0; f < flashes; f++)
|
for (var f = 0; f < flashes; f++)
|
||||||
w.Add(new DelayedAction(flashesDelay + f * flashInterval, () =>
|
w.Add(new DelayedAction(flashesDelay + f * flashInterval, () =>
|
||||||
w.Add(new FlashTarget(target.Actor, ticks: flashDuration))));
|
w.Add(new FlashTarget(target, ticks: flashDuration))));
|
||||||
|
|
||||||
w.Add(new DelayedAction(delay, () =>
|
w.Add(new DelayedAction(delay, () =>
|
||||||
{
|
{
|
||||||
// Can't demolish an already dead actor
|
if (target.IsDead())
|
||||||
if (target.Type != TargetType.Actor)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var modifiers = target.TraitsImplementing<IDamageModifier>()
|
||||||
|
|
||||||
var demolishable = target.Actor.TraitOrDefault<IDemolishable>();
|
|
||||||
if (demolishable == null || !demolishable.IsValidTarget(target.Actor, self))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var modifiers = target.Actor.TraitsImplementing<IDamageModifier>()
|
|
||||||
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
|
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
|
||||||
.Select(t => t.GetDamageModifier(self, null));
|
.Select(t => t.GetDamageModifier(self, null));
|
||||||
|
|
||||||
if (Util.ApplyPercentageModifiers(100, modifiers) > 0)
|
if (Util.ApplyPercentageModifiers(100, modifiers) > 0)
|
||||||
demolishable.Demolish(target.Actor, self);
|
demolishables.Do(d => d.Demolish(target, self));
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextActivity;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,33 +13,28 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
class DonateSupplies : Activity
|
class DonateSupplies : Enter
|
||||||
{
|
{
|
||||||
Target target;
|
readonly Actor target;
|
||||||
int payload;
|
readonly int payload;
|
||||||
|
|
||||||
public DonateSupplies(Actor target, int payload)
|
public DonateSupplies(Actor self, Actor target, int payload)
|
||||||
|
: base(self, target)
|
||||||
{
|
{
|
||||||
this.target = Target.FromActor(target);
|
this.target = target;
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
protected override void OnInside(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled || !target.IsValidFor(self))
|
if (target.IsDead())
|
||||||
return NextActivity;
|
return;
|
||||||
|
|
||||||
if (target.Type != TargetType.Actor)
|
target.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
|
||||||
return NextActivity;
|
|
||||||
|
|
||||||
var targetActor = target.Actor;
|
|
||||||
targetActor.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
|
|
||||||
self.Destroy();
|
self.Destroy();
|
||||||
|
|
||||||
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||||
self.World.AddFrameEndTask(w => w.Add(new FloatingText(targetActor.CenterPosition, targetActor.Owner.Color.RGB, FloatingText.FormatCashTick(payload), 30)));
|
self.World.AddFrameEndTask(w => w.Add(new FloatingText(target.CenterPosition, target.Owner.Color.RGB, FloatingText.FormatCashTick(payload), 30)));
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,15 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
public class Enter : Activity
|
public abstract class Enter : Activity
|
||||||
{
|
{
|
||||||
public enum ReserveStatus { None, TooFar, Pending, Ready }
|
public enum ReserveStatus { None, TooFar, Pending, Ready }
|
||||||
enum State { ApproachingOrEntering, Inside, Exiting, Done }
|
enum State { ApproachingOrEntering, Inside, Exiting, Done }
|
||||||
|
|
||||||
readonly Activity inside;
|
|
||||||
readonly IMove move;
|
readonly IMove move;
|
||||||
readonly int maxTries = 0;
|
readonly int maxTries = 0;
|
||||||
|
readonly bool targetCenter;
|
||||||
|
public Target Target { get { return target; } }
|
||||||
Target target;
|
Target target;
|
||||||
State nextState = State.ApproachingOrEntering; // Hint/starting point for next state
|
State nextState = State.ApproachingOrEntering; // Hint/starting point for next state
|
||||||
bool isEnteringOrInside = false; // Used to know if exiting should be used
|
bool isEnteringOrInside = false; // Used to know if exiting should be used
|
||||||
@@ -31,17 +32,12 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
Activity inner;
|
Activity inner;
|
||||||
bool firstApproach = true;
|
bool firstApproach = true;
|
||||||
|
|
||||||
protected Enter(Actor self, Actor target, int maxTries = 1)
|
protected Enter(Actor self, Actor target, int maxTries = 1, bool targetCenter = false)
|
||||||
: this(self, target, null)
|
|
||||||
{
|
|
||||||
this.maxTries = maxTries;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enter(Actor self, Actor target, Activity inside)
|
|
||||||
{
|
{
|
||||||
this.move = self.Trait<IMove>();
|
this.move = self.Trait<IMove>();
|
||||||
this.target = Target.FromActor(target);
|
this.target = Target.FromActor(target);
|
||||||
this.inside = inside;
|
this.maxTries = maxTries;
|
||||||
|
this.targetCenter = targetCenter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanEnter(target) should to be true; othwise, Enter may abort.
|
// CanEnter(target) should to be true; othwise, Enter may abort.
|
||||||
@@ -83,7 +79,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called when inner activity is this and returns inner activity for next tick.
|
// Called when inner activity is this and returns inner activity for next tick.
|
||||||
protected virtual Activity InsideTick(Actor self) { return Util.RunActivity(self, inside); }
|
protected virtual Activity InsideTick(Actor self) { return null; }
|
||||||
|
|
||||||
// Abort entering and/or leave if necessary
|
// Abort entering and/or leave if necessary
|
||||||
protected virtual void AbortOrExit(Actor self)
|
protected virtual void AbortOrExit(Actor self)
|
||||||
@@ -97,7 +93,6 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
inner.Cancel(self);
|
inner.Cancel(self);
|
||||||
if (isEnteringOrInside)
|
if (isEnteringOrInside)
|
||||||
Unreserve(self, true);
|
Unreserve(self, true);
|
||||||
isEnteringOrInside = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel inner activity and mark as done unless already leaving or done
|
// Cancel inner activity and mark as done unless already leaving or done
|
||||||
@@ -167,7 +162,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
case ReserveStatus.None:
|
case ReserveStatus.None:
|
||||||
return State.Done; // No available target -> abort to next activity
|
return State.Done; // No available target -> abort to next activity
|
||||||
case ReserveStatus.TooFar:
|
case ReserveStatus.TooFar:
|
||||||
inner = move.MoveToTarget(self, Target.FromPos(target.CenterPosition)); // Approach
|
inner = move.MoveToTarget(self, targetCenter ? Target.FromPos(target.CenterPosition) : target); // Approach
|
||||||
return State.ApproachingOrEntering;
|
return State.ApproachingOrEntering;
|
||||||
case ReserveStatus.Pending:
|
case ReserveStatus.Pending:
|
||||||
return State.ApproachingOrEntering; // Retry next tick
|
return State.ApproachingOrEntering; // Retry next tick
|
||||||
@@ -178,6 +173,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
// Entering
|
// Entering
|
||||||
isEnteringOrInside = true;
|
isEnteringOrInside = true;
|
||||||
savedPos = self.CenterPosition; // Save position of self, before entering, for returning on exit
|
savedPos = self.CenterPosition; // Save position of self, before entering, for returning on exit
|
||||||
|
|
||||||
inner = move.MoveIntoTarget(self, target); // Enter
|
inner = move.MoveIntoTarget(self, target); // Enter
|
||||||
|
|
||||||
if (inner != null)
|
if (inner != null)
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
readonly int maxTries;
|
readonly int maxTries;
|
||||||
Cargo cargo;
|
Cargo cargo;
|
||||||
|
|
||||||
public EnterTransport(Actor self, Actor transport, int maxTries = 0)
|
public EnterTransport(Actor self, Actor transport, int maxTries = 0, bool targetCenter = false)
|
||||||
: base(self, transport, maxTries)
|
: base(self, transport, maxTries, targetCenter)
|
||||||
{
|
{
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.maxTries = maxTries;
|
this.maxTries = maxTries;
|
||||||
|
|||||||
@@ -13,25 +13,27 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
class Infiltrate : Activity
|
class Infiltrate : Enter
|
||||||
{
|
{
|
||||||
Target target;
|
readonly Actor target;
|
||||||
public Infiltrate(Actor target) { this.target = Target.FromActor(target); }
|
public Infiltrate(Actor self, Actor target)
|
||||||
|
: base(self, target)
|
||||||
public override Activity Tick(Actor self)
|
|
||||||
{
|
{
|
||||||
if (IsCanceled || target.Type != TargetType.Actor || target.Actor.Owner == self.Owner)
|
this.target = target;
|
||||||
return NextActivity;
|
}
|
||||||
|
|
||||||
foreach (var t in target.Actor.TraitsImplementing<INotifyInfiltrated>())
|
protected override void OnInside(Actor self)
|
||||||
t.Infiltrated(target.Actor, self);
|
{
|
||||||
|
if (target.IsDead() || target.Owner == self.Owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var t in target.TraitsImplementing<INotifyInfiltrated>())
|
||||||
|
t.Infiltrated(target, self);
|
||||||
|
|
||||||
self.Destroy();
|
self.Destroy();
|
||||||
|
|
||||||
if (target.Actor.HasTrait<Building>())
|
if (target.HasTrait<Building>())
|
||||||
Sound.PlayToPlayer(self.Owner, "bldginf1.aud");
|
Sound.PlayToPlayer(self.Owner, "bldginf1.aud");
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,25 +12,27 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
class RepairBridge : Activity
|
class RepairBridge : Enter
|
||||||
{
|
{
|
||||||
Target target;
|
readonly BridgeHut hut;
|
||||||
|
|
||||||
public RepairBridge(Actor target) { this.target = Target.FromActor(target); }
|
public RepairBridge(Actor self, Actor target)
|
||||||
|
: base(self, target)
|
||||||
public override Activity Tick(Actor self)
|
|
||||||
{
|
{
|
||||||
if (IsCanceled || target.Type != TargetType.Actor)
|
hut = target.Trait<BridgeHut>();
|
||||||
return NextActivity;
|
}
|
||||||
|
|
||||||
var hut = target.Actor.Trait<BridgeHut>();
|
protected override bool CanReserve(Actor self)
|
||||||
if (hut.BridgeDamageState == DamageState.Undamaged)
|
{
|
||||||
return NextActivity;
|
return hut.BridgeDamageState != DamageState.Undamaged && !hut.Repairing && hut.Bridge.GetHut(0) != null && hut.Bridge.GetHut(1) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInside(Actor self)
|
||||||
|
{
|
||||||
|
if (hut.BridgeDamageState == DamageState.Undamaged || hut.Repairing || hut.Bridge.GetHut(0) == null || hut.Bridge.GetHut(1) == null)
|
||||||
|
return;
|
||||||
hut.Repair(self);
|
hut.Repair(self);
|
||||||
self.Destroy();
|
self.Destroy();
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,25 +12,29 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
class RepairBuilding : Activity
|
class RepairBuilding : Enter
|
||||||
{
|
{
|
||||||
Target target;
|
readonly Actor target;
|
||||||
|
readonly Health health;
|
||||||
|
|
||||||
public RepairBuilding(Actor target) { this.target = Target.FromActor(target); }
|
public RepairBuilding(Actor self, Actor target)
|
||||||
|
: base(self, target)
|
||||||
public override Activity Tick(Actor self)
|
|
||||||
{
|
{
|
||||||
if (IsCanceled || target.Type != TargetType.Actor)
|
this.target = target;
|
||||||
return NextActivity;
|
health = target.Trait<Health>();
|
||||||
|
}
|
||||||
|
|
||||||
var health = target.Actor.Trait<Health>();
|
protected override bool CanReserve(Actor self)
|
||||||
|
{
|
||||||
|
return health.DamageState != DamageState.Undamaged;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInside(Actor self)
|
||||||
|
{
|
||||||
if (health.DamageState == DamageState.Undamaged)
|
if (health.DamageState == DamageState.Undamaged)
|
||||||
return NextActivity;
|
return;
|
||||||
|
target.InflictDamage(self, -health.MaxHP, null);
|
||||||
target.Actor.InflictDamage(self, -health.MaxHP, null);
|
|
||||||
self.Destroy();
|
self.Destroy();
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Effects;
|
using OpenRA.Effects;
|
||||||
@@ -34,7 +35,7 @@ namespace OpenRA.Mods.RA
|
|||||||
public readonly ushort DestroyedPlusSouthTemplate = 0;
|
public readonly ushort DestroyedPlusSouthTemplate = 0;
|
||||||
public readonly ushort DestroyedPlusBothTemplate = 0;
|
public readonly ushort DestroyedPlusBothTemplate = 0;
|
||||||
|
|
||||||
public readonly string[] ShorePieces = {"br1", "br2"};
|
public readonly string[] ShorePieces = { "br1", "br2" };
|
||||||
public readonly int[] NorthOffset = null;
|
public readonly int[] NorthOffset = null;
|
||||||
public readonly int[] SouthOffset = null;
|
public readonly int[] SouthOffset = null;
|
||||||
|
|
||||||
@@ -68,16 +69,19 @@ namespace OpenRA.Mods.RA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Bridge: IRender, INotifyDamageStateChanged
|
class Bridge : IRender, INotifyDamageStateChanged
|
||||||
{
|
{
|
||||||
|
readonly Bridge[] neighbours = new Bridge[2];
|
||||||
|
readonly BridgeHut[] huts = new BridgeHut[2]; // Huts before this / first & after this / last
|
||||||
|
public readonly Health Health;
|
||||||
|
|
||||||
ushort template;
|
ushort template;
|
||||||
Dictionary<CPos, byte> footprint;
|
Dictionary<CPos, byte> footprint;
|
||||||
|
|
||||||
Actor self;
|
Actor self;
|
||||||
BridgeInfo Info;
|
|
||||||
|
public BridgeInfo Info;
|
||||||
public string Type;
|
public string Type;
|
||||||
Bridge northNeighbour, southNeighbour;
|
public BridgeHut Hut { get; internal set; }
|
||||||
Health Health;
|
|
||||||
|
|
||||||
public Bridge(Actor self, BridgeInfo info)
|
public Bridge(Actor self, BridgeInfo info)
|
||||||
{
|
{
|
||||||
@@ -88,6 +92,21 @@ namespace OpenRA.Mods.RA
|
|||||||
this.Type = self.Info.Name;
|
this.Type = self.Info.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bridge Neighbour(int direction) { return neighbours[direction]; }
|
||||||
|
public IEnumerable<Bridge> Enumerate(int direction, bool includeSelf = false)
|
||||||
|
{
|
||||||
|
for (var b = includeSelf ? this : neighbours[direction]; b != null; b = b.neighbours[direction])
|
||||||
|
yield return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Do(Action<Bridge, int> action)
|
||||||
|
{
|
||||||
|
action(this, -1);
|
||||||
|
for (var d = 0; d <= 1; d++)
|
||||||
|
if (neighbours[d] != null)
|
||||||
|
action(neighbours[d], d);
|
||||||
|
}
|
||||||
|
|
||||||
public void Create(ushort template, Dictionary<CPos, byte> footprint)
|
public void Create(ushort template, Dictionary<CPos, byte> footprint)
|
||||||
{
|
{
|
||||||
this.template = template;
|
this.template = template;
|
||||||
@@ -107,11 +126,31 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public void LinkNeighbouringBridges(World world, BridgeLayer bridges)
|
public void LinkNeighbouringBridges(World world, BridgeLayer bridges)
|
||||||
{
|
{
|
||||||
// go looking for our neighbors if this is a long bridge.
|
for (var d = 0; d <= 1; d++)
|
||||||
if (Info.NorthOffset != null)
|
{
|
||||||
northNeighbour = GetNeighbor(Info.NorthOffset, bridges);
|
if (neighbours[d] != null)
|
||||||
if (Info.SouthOffset != null)
|
continue; // Already linked by reverse lookup
|
||||||
southNeighbour = GetNeighbor(Info.SouthOffset, bridges);
|
|
||||||
|
var offset = d == 0 ? Info.NorthOffset : Info.SouthOffset;
|
||||||
|
if (offset == null)
|
||||||
|
continue; // End piece type
|
||||||
|
|
||||||
|
neighbours[d] = GetNeighbor(offset, bridges);
|
||||||
|
if (neighbours[d] != null)
|
||||||
|
neighbours[d].neighbours[1 - d] = this; // Save reverse lookup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BridgeHut GetHut(int index)
|
||||||
|
{
|
||||||
|
if (huts[index] != null)
|
||||||
|
return huts[index]; // Already found
|
||||||
|
|
||||||
|
var n = neighbours[index];
|
||||||
|
if (n == null)
|
||||||
|
return huts[index] = Hut; // End piece
|
||||||
|
|
||||||
|
return huts[index] = n.Hut ?? n.GetHut(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bridge GetNeighbor(int[] offset, BridgeLayer bridges)
|
public Bridge GetNeighbor(int[] offset, BridgeLayer bridges)
|
||||||
@@ -166,10 +205,7 @@ namespace OpenRA.Mods.RA
|
|||||||
if (!Info.Long)
|
if (!Info.Long)
|
||||||
return Health.IsDead;
|
return Health.IsDead;
|
||||||
|
|
||||||
if (NeighbourIsDeadShore(northNeighbour))
|
if (NeighbourIsDeadShore(neighbours[0]) || NeighbourIsDeadShore(neighbours[1]))
|
||||||
return true;
|
|
||||||
|
|
||||||
if (NeighbourIsDeadShore(southNeighbour))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return Health.IsDead;
|
return Health.IsDead;
|
||||||
@@ -180,13 +216,13 @@ namespace OpenRA.Mods.RA
|
|||||||
if (Info.Long && LongBridgeSegmentIsDead())
|
if (Info.Long && LongBridgeSegmentIsDead())
|
||||||
{
|
{
|
||||||
// Long bridges have custom art for multiple segments being destroyed
|
// Long bridges have custom art for multiple segments being destroyed
|
||||||
var northIsDead = northNeighbour != null && northNeighbour.LongBridgeSegmentIsDead();
|
var previousIsDead = neighbours[0] != null && neighbours[0].LongBridgeSegmentIsDead();
|
||||||
var southIsDead = southNeighbour != null && southNeighbour.LongBridgeSegmentIsDead();
|
var nextIsDead = neighbours[1] != null && neighbours[1].LongBridgeSegmentIsDead();
|
||||||
if (northIsDead && southIsDead)
|
if (previousIsDead && nextIsDead)
|
||||||
return Info.DestroyedPlusBothTemplate;
|
return Info.DestroyedPlusBothTemplate;
|
||||||
if (northIsDead)
|
if (previousIsDead)
|
||||||
return Info.DestroyedPlusNorthTemplate;
|
return Info.DestroyedPlusNorthTemplate;
|
||||||
if (southIsDead)
|
if (nextIsDead)
|
||||||
return Info.DestroyedPlusSouthTemplate;
|
return Info.DestroyedPlusSouthTemplate;
|
||||||
|
|
||||||
return Info.DestroyedTemplate;
|
return Info.DestroyedTemplate;
|
||||||
@@ -223,7 +259,7 @@ namespace OpenRA.Mods.RA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Repair(Actor repairer, bool continueNorth, bool continueSouth)
|
public void Repair(Actor repairer, int direction, Action onComplete)
|
||||||
{
|
{
|
||||||
// Repair self
|
// Repair self
|
||||||
var initialDamage = Health.DamageState;
|
var initialDamage = Health.DamageState;
|
||||||
@@ -237,68 +273,56 @@ namespace OpenRA.Mods.RA
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
Health.InflictDamage(self, repairer, -Health.MaxHP, null, true);
|
Health.InflictDamage(self, repairer, -Health.MaxHP, null, true);
|
||||||
|
if (direction < 0 ? neighbours[0] == null && neighbours[1] == null : Hut != null || neighbours[direction] == null)
|
||||||
|
onComplete(); // Done if single or reached other hut
|
||||||
});
|
});
|
||||||
|
|
||||||
// Repair adjacent spans (long bridges)
|
// Repair adjacent spans onto next hut or end
|
||||||
if (continueNorth && northNeighbour != null)
|
if (direction >= 0 && Hut == null && neighbours[direction] != null)
|
||||||
{
|
{
|
||||||
var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(northNeighbour) ?
|
var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(neighbours[direction]) ?
|
||||||
0 : Info.RepairPropagationDelay;
|
0 : Info.RepairPropagationDelay;
|
||||||
|
|
||||||
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
||||||
northNeighbour.Repair(repairer, true, false))));
|
neighbours[direction].Repair(repairer, direction, onComplete))));
|
||||||
}
|
|
||||||
|
|
||||||
if (continueSouth && southNeighbour != null)
|
|
||||||
{
|
|
||||||
var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(southNeighbour) ?
|
|
||||||
0 : Info.RepairPropagationDelay;
|
|
||||||
|
|
||||||
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
|
||||||
southNeighbour.Repair(repairer, false, true))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DamageStateChanged(Actor self, AttackInfo e)
|
public void DamageStateChanged(Actor self, AttackInfo e)
|
||||||
{
|
{
|
||||||
UpdateState();
|
Do((b, d) => b.UpdateState());
|
||||||
if (northNeighbour != null)
|
|
||||||
northNeighbour.UpdateState();
|
|
||||||
if (southNeighbour != null)
|
|
||||||
southNeighbour.UpdateState();
|
|
||||||
|
|
||||||
// Need to update the neighbours neighbour to correctly
|
// Need to update the neighbours neighbour to correctly
|
||||||
// display the broken shore hack
|
// display the broken shore hack
|
||||||
if (Info.ShorePieces.Contains(Type))
|
if (Info.ShorePieces.Contains(Type))
|
||||||
{
|
for (var d = 0; d <= 1; d++)
|
||||||
if (northNeighbour != null && northNeighbour.northNeighbour != null)
|
if (neighbours[d] != null && neighbours[d].neighbours[d] != null)
|
||||||
northNeighbour.northNeighbour.UpdateState();
|
neighbours[d].neighbours[d].UpdateState();
|
||||||
if (southNeighbour != null && southNeighbour.southNeighbour != null)
|
|
||||||
southNeighbour.southNeighbour.UpdateState();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AggregateDamageState(Bridge b, int d, ref DamageState damage)
|
||||||
|
{
|
||||||
|
if (b.Health.DamageState > damage)
|
||||||
|
damage = b.Health.DamageState;
|
||||||
|
if (b.Hut == null && b.neighbours[d] != null)
|
||||||
|
AggregateDamageState(b.neighbours[d], d, ref damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the worst span damage before other hut
|
||||||
public DamageState AggregateDamageState()
|
public DamageState AggregateDamageState()
|
||||||
{
|
{
|
||||||
// Find the worst span damage in the entire bridge
|
|
||||||
var br = this;
|
|
||||||
while (br.northNeighbour != null)
|
|
||||||
br = br.northNeighbour;
|
|
||||||
|
|
||||||
var damage = Health.DamageState;
|
var damage = Health.DamageState;
|
||||||
for (var b = br; b != null; b = b.southNeighbour)
|
Do((b, d) => AggregateDamageState(b, d, ref damage));
|
||||||
if (b.Health.DamageState > damage)
|
|
||||||
damage = b.Health.DamageState;
|
|
||||||
|
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Demolish(Actor saboteur, bool continueNorth, bool continueSouth)
|
public void Demolish(Actor saboteur, int direction)
|
||||||
{
|
{
|
||||||
var initialDamage = Health.DamageState;
|
var initialDamage = Health.DamageState;
|
||||||
self.World.AddFrameEndTask(w =>
|
self.World.AddFrameEndTask(w =>
|
||||||
{
|
{
|
||||||
var weapon = saboteur.World.Map.Rules.Weapons[Info.DemolishWeapon.ToLowerInvariant()];
|
var weapon = saboteur.World.Map.Rules.Weapons[Info.DemolishWeapon.ToLowerInvariant()];
|
||||||
|
|
||||||
// Use .FromPos since this actor is killed. Cannot use Target.FromActor
|
// Use .FromPos since this actor is killed. Cannot use Target.FromActor
|
||||||
weapon.Impact(Target.FromPos(self.CenterPosition), saboteur, Enumerable.Empty<int>());
|
weapon.Impact(Target.FromPos(self.CenterPosition), saboteur, Enumerable.Empty<int>());
|
||||||
|
|
||||||
@@ -306,23 +330,14 @@ namespace OpenRA.Mods.RA
|
|||||||
self.Kill(saboteur);
|
self.Kill(saboteur);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Destroy adjacent spans (long bridges)
|
// Destroy adjacent spans between (including) huts
|
||||||
if (continueNorth && northNeighbour != null)
|
if (direction >= 0 && Hut == null && neighbours[direction] != null)
|
||||||
{
|
{
|
||||||
var delay = initialDamage == DamageState.Dead || NeighbourIsDeadShore(northNeighbour) ?
|
var delay = initialDamage == DamageState.Dead || NeighbourIsDeadShore(neighbours[direction]) ?
|
||||||
0 : Info.RepairPropagationDelay;
|
0 : Info.RepairPropagationDelay;
|
||||||
|
|
||||||
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
||||||
northNeighbour.Demolish(saboteur, true, false))));
|
neighbours[direction].Demolish(saboteur, direction))));
|
||||||
}
|
|
||||||
|
|
||||||
if (continueSouth && southNeighbour != null)
|
|
||||||
{
|
|
||||||
var delay = initialDamage == DamageState.Dead || NeighbourIsDeadShore(southNeighbour) ?
|
|
||||||
0 : Info.RepairPropagationDelay;
|
|
||||||
|
|
||||||
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
|
||||||
southNeighbour.Demolish(saboteur, false, true))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA
|
namespace OpenRA.Mods.RA
|
||||||
@@ -21,28 +23,34 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
class BridgeHut : IDemolishable
|
class BridgeHut : IDemolishable
|
||||||
{
|
{
|
||||||
public Bridge bridge;
|
Lazy<Bridge> firstBridge;
|
||||||
|
int repairDirections = 0;
|
||||||
|
public readonly Bridge Bridge;
|
||||||
|
public Bridge FirstBridge { get { return firstBridge.Value; } }
|
||||||
|
public DamageState BridgeDamageState { get { return Bridge.AggregateDamageState(); } }
|
||||||
|
public bool Repairing { get { return repairDirections > 0; } }
|
||||||
|
|
||||||
public BridgeHut(ActorInitializer init)
|
public BridgeHut(ActorInitializer init)
|
||||||
{
|
{
|
||||||
bridge = init.Get<ParentActorInit>().value.Trait<Bridge>();
|
Bridge = init.Get<ParentActorInit>().value.Trait<Bridge>();
|
||||||
|
Bridge.Hut = this;
|
||||||
|
firstBridge = new Lazy<Bridge>(() => Bridge.Enumerate(0, true).Last());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Repair(Actor repairer)
|
public void Repair(Actor repairer)
|
||||||
{
|
{
|
||||||
bridge.Repair(repairer, true, true);
|
repairDirections = Bridge.GetHut(0) != this && Bridge.GetHut(1) != this ? 2 : 1;
|
||||||
|
Bridge.Do((b, d) => b.Repair(repairer, d, () => repairDirections--));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Demolish(Actor self, Actor saboteur)
|
public void Demolish(Actor self, Actor saboteur)
|
||||||
{
|
{
|
||||||
bridge.Demolish(saboteur, true, true);
|
Bridge.Do((b, d) => b.Demolish(saboteur, d));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsValidTarget(Actor self, Actor saboteur)
|
public bool IsValidTarget(Actor self, Actor saboteur)
|
||||||
{
|
{
|
||||||
return BridgeDamageState != DamageState.Dead;
|
return BridgeDamageState != DamageState.Dead;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DamageState BridgeDamageState { get { return bridge.AggregateDamageState(); } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Red);
|
self.SetTargetLine(target, Color.Red);
|
||||||
self.QueueActivity(new Enter(self, target.Actor, new Demolish(
|
self.QueueActivity(new Demolish(self,
|
||||||
target.Actor, info.C4Delay, info.Flashes, info.FlashesDelay, info.FlashInterval, info.FlashDuration)));
|
target.Actor, info.C4Delay, info.Flashes, info.FlashesDelay, info.FlashInterval, info.FlashDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string VoicePhraseForOrder(Actor self, Order order)
|
public string VoicePhraseForOrder(Actor self, Order order)
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ using OpenRA.Traits;
|
|||||||
namespace OpenRA.Mods.RA
|
namespace OpenRA.Mods.RA
|
||||||
{
|
{
|
||||||
[Desc("This actor can be captured by a unit with Captures: trait.")]
|
[Desc("This actor can be captured by a unit with Captures: trait.")]
|
||||||
class CapturableInfo : TraitInfo<Capturable>
|
class CapturableInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
[Desc("Type of actor (the Captures: trait defines what Types it can capture).")]
|
[Desc("Type listed under Types in Captures: trait of actors that can capture this).")]
|
||||||
public readonly string Type = "building";
|
public readonly string Type = "building";
|
||||||
public readonly bool AllowAllies = false;
|
public readonly bool AllowAllies = false;
|
||||||
public readonly bool AllowNeutral = true;
|
public readonly bool AllowNeutral = true;
|
||||||
@@ -25,6 +25,8 @@ namespace OpenRA.Mods.RA
|
|||||||
public readonly float CaptureThreshold = 0.5f;
|
public readonly float CaptureThreshold = 0.5f;
|
||||||
public readonly bool CancelActivity = false;
|
public readonly bool CancelActivity = false;
|
||||||
|
|
||||||
|
public object Create(ActorInitializer init) { return new Capturable(this); }
|
||||||
|
|
||||||
public bool CanBeTargetedBy(Actor captor, Player owner)
|
public bool CanBeTargetedBy(Actor captor, Player owner)
|
||||||
{
|
{
|
||||||
var c = captor.TraitOrDefault<Captures>();
|
var c = captor.TraitOrDefault<Captures>();
|
||||||
@@ -50,10 +52,16 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
class Capturable : INotifyCapture
|
class Capturable : INotifyCapture
|
||||||
{
|
{
|
||||||
|
public readonly CapturableInfo Info;
|
||||||
|
public bool BeingCaptured { get; private set; }
|
||||||
|
public Capturable(CapturableInfo info) { Info = info; }
|
||||||
|
|
||||||
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
|
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
|
||||||
{
|
{
|
||||||
var info = self.Info.Traits.Get<CapturableInfo>();
|
BeingCaptured = true;
|
||||||
if (info.CancelActivity)
|
self.World.AddFrameEndTask(w => BeingCaptured = false);
|
||||||
|
|
||||||
|
if (Info.CancelActivity)
|
||||||
{
|
{
|
||||||
var stop = new Order("Stop", self, false);
|
var stop = new Order("Stop", self, false);
|
||||||
foreach (var t in self.TraitsImplementing<IResolveOrder>())
|
foreach (var t in self.TraitsImplementing<IResolveOrder>())
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Red);
|
self.SetTargetLine(target, Color.Red);
|
||||||
self.QueueActivity(new Enter(self, target.Actor, new CaptureActor(target)));
|
self.QueueActivity(new CaptureActor(self, target.Actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
class CaptureOrderTargeter : UnitOrderTargeter
|
class CaptureOrderTargeter : UnitOrderTargeter
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Yellow);
|
self.SetTargetLine(target, Color.Yellow);
|
||||||
self.QueueActivity(new Enter(self, target.Actor, new RepairBuilding(target.Actor)));
|
self.QueueActivity(new RepairBuilding(self, target.Actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
class EngineerRepairOrderTargeter : UnitOrderTargeter
|
class EngineerRepairOrderTargeter : UnitOrderTargeter
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ namespace OpenRA.Mods.RA.Infiltration
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Red);
|
self.SetTargetLine(target, Color.Red);
|
||||||
self.QueueActivity(new Enter(self, target.Actor, new Infiltrate(target.Actor)));
|
self.QueueActivity(new Infiltrate(self, target.Actor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,8 @@ namespace OpenRA.Mods.RA
|
|||||||
self.SetTargetLine(target, Color.Green);
|
self.SetTargetLine(target, Color.Green);
|
||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new EnterTransport(self, order.TargetActor, order.OrderString == "EnterTransport" ? 0 : Info.MaxAlternateTransportAttempts));
|
var transports = order.OrderString == "EnterTransports";
|
||||||
|
self.QueueActivity(new EnterTransport(self, order.TargetActor, transports ? Info.MaxAlternateTransportAttempts : 0, transports));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.SetTargetLine(Target.FromOrder(self.World, order), Color.Yellow);
|
self.SetTargetLine(Target.FromOrder(self.World, order), Color.Yellow);
|
||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new Enter(self, order.TargetActor, new RepairBridge(order.TargetActor)));
|
self.QueueActivity(new RepairBridge(self, order.TargetActor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Yellow);
|
self.SetTargetLine(target, Color.Yellow);
|
||||||
self.QueueActivity(new Enter(self, target.Actor, new DonateSupplies(target.Actor, info.Payload)));
|
self.QueueActivity(new DonateSupplies(self, target.Actor, info.Payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
class SupplyTruckOrderTargeter : UnitOrderTargeter
|
class SupplyTruckOrderTargeter : UnitOrderTargeter
|
||||||
|
|||||||
Reference in New Issue
Block a user