Merge pull request #6662 from atlimit8/CaptureActor-Enter

Enter subclasses & fix for 6658
This commit is contained in:
obrakmann
2014-10-10 19:51:17 +02:00
18 changed files with 233 additions and 195 deletions

View File

@@ -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;
} }
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;
} }
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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))));
} }
} }
} }

View File

@@ -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(); } }
} }
} }

View File

@@ -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)

View File

@@ -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>())

View File

@@ -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

View File

@@ -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

View File

@@ -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));
} }
} }
} }

View File

@@ -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));
} }
} }

View File

@@ -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));
} }
} }

View File

@@ -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