diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index 8140facf1b..179b0fa288 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -781,6 +781,8 @@
+
+
diff --git a/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs b/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs
index 189a87e27b..bf788c406a 100644
--- a/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs
+++ b/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs
@@ -15,12 +15,13 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
- [Desc("Placeholder actor that transforms into another actor type when repaired.")]
+ [Desc("Placeholder actor used for dead segments and bridge end ramps.")]
class BridgePlaceholderInfo : ITraitInfo
{
public readonly string Type = "GroundLevelBridge";
- [FieldLoader.Require]
+ public readonly DamageState DamageState = DamageState.Undamaged;
+
[Desc("Actor type to replace with on repair.")]
[ActorReference] public readonly string ReplaceWithActor = null;
@@ -54,6 +55,9 @@ namespace OpenRA.Mods.Common.Traits
void IBridgeSegment.Repair(Actor repairer)
{
+ if (Info.ReplaceWithActor == null)
+ return;
+
self.World.AddFrameEndTask(w =>
{
self.Dispose();
@@ -68,11 +72,11 @@ namespace OpenRA.Mods.Common.Traits
void IBridgeSegment.Demolish(Actor saboteur)
{
- // Do nothing, we're already dead
+ // Do nothing
}
string IBridgeSegment.Type { get { return Info.Type; } }
- DamageState IBridgeSegment.DamageState { get { return DamageState.Dead; } }
+ DamageState IBridgeSegment.DamageState { get { return Info.DamageState; } }
bool IBridgeSegment.Valid { get { return self.IsInWorld; } }
CVec[] IBridgeSegment.NeighbourOffsets { get { return Info.NeighbourOffsets; } }
CPos IBridgeSegment.Location { get { return self.Location; } }
diff --git a/OpenRA.Mods.Common/Traits/Render/WithBridgeSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithBridgeSpriteBody.cs
new file mode 100644
index 0000000000..4ab3bbab46
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Render/WithBridgeSpriteBody.cs
@@ -0,0 +1,130 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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.Collections.Generic;
+using System.Linq;
+using OpenRA.Graphics;
+using OpenRA.Mods.Common.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits.Render
+{
+ class WithBridgeSpriteBodyInfo : WithSpriteBodyInfo
+ {
+ public readonly string Type = "GroundLevelBridge";
+
+ [Desc("Offset to search for the 'A' neighbour")]
+ public readonly CVec AOffset = CVec.Zero;
+
+ [Desc("Position to search for the 'B' neighbour")]
+ public readonly CVec BOffset = CVec.Zero;
+
+ [SequenceReference]
+ [Desc("Sequences to use when both neighbours are alive.")]
+ public readonly string[] Sequences = { "idle" };
+
+ [SequenceReference]
+ public readonly string[] ADestroyedSequences = { "adestroyed" };
+
+ [SequenceReference]
+ public readonly string[] BDestroyedSequences = { "bdestroyed" };
+
+ [SequenceReference]
+ public readonly string[] ABDestroyedSequences = { "abdestroyed" };
+
+ public override object Create(ActorInitializer init) { return new WithBridgeSpriteBody(init, this); }
+
+ public override IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
+ {
+ var anim = new Animation(init.World, image);
+ anim.PlayFetchIndex(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequences.First()), () => 0);
+
+ yield return new SpriteActorPreview(anim, () => WVec.Zero, () => 0, p, rs.Scale);
+ }
+ }
+
+ class WithBridgeSpriteBody : WithSpriteBody, INotifyRemovedFromWorld
+ {
+ readonly WithBridgeSpriteBodyInfo bridgeInfo;
+ readonly BridgeLayer bridgeLayer;
+ readonly Actor self;
+
+ public WithBridgeSpriteBody(ActorInitializer init, WithBridgeSpriteBodyInfo info)
+ : base(init, info, () => 0)
+ {
+ self = init.Self;
+ bridgeInfo = info;
+ bridgeLayer = init.World.WorldActor.Trait();
+ }
+
+ protected override void OnBuildComplete(Actor self)
+ {
+ if (bridgeInfo.AOffset != CVec.Zero)
+ UpdateNeighbour(bridgeInfo.AOffset);
+
+ if (bridgeInfo.BOffset != CVec.Zero)
+ UpdateNeighbour(bridgeInfo.BOffset);
+
+ SetDirty();
+ }
+
+ void UpdateNeighbour(CVec offset)
+ {
+ var neighbour = bridgeLayer[self.Location + offset];
+ if (neighbour == null)
+ return;
+
+ var body = neighbour.TraitOrDefault();
+ if (body != null && body.bridgeInfo.Type == bridgeInfo.Type)
+ body.SetDirty();
+ }
+
+ void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)
+ {
+ UpdateNeighbour(bridgeInfo.AOffset);
+ UpdateNeighbour(bridgeInfo.BOffset);
+ }
+
+ void SetDirty()
+ {
+ self.World.AddFrameEndTask(w =>
+ {
+ var aDestroyed = bridgeInfo.AOffset != CVec.Zero && NeighbourIsDestroyed(bridgeInfo.AOffset);
+ var bDestroyed = bridgeInfo.BOffset != CVec.Zero && NeighbourIsDestroyed(bridgeInfo.BOffset);
+
+ var sequence = DefaultAnimation.CurrentSequence.Name;
+ if (aDestroyed && bDestroyed && bridgeInfo.ABDestroyedSequences.Any())
+ sequence = bridgeInfo.ABDestroyedSequences.Random(Game.CosmeticRandom);
+ else if (aDestroyed && bridgeInfo.ADestroyedSequences.Any())
+ sequence = bridgeInfo.ADestroyedSequences.Random(Game.CosmeticRandom);
+ else if (bDestroyed && bridgeInfo.BDestroyedSequences.Any())
+ sequence = bridgeInfo.BDestroyedSequences.Random(Game.CosmeticRandom);
+ else
+ sequence = bridgeInfo.Sequences.Random(Game.CosmeticRandom);
+
+ DefaultAnimation.PlayRepeating(NormalizeSequence(self, sequence));
+ });
+ }
+
+ bool NeighbourIsDestroyed(CVec offset)
+ {
+ var neighbour = bridgeLayer[self.Location + offset];
+ if (neighbour == null)
+ return false;
+
+ var segment = neighbour.TraitOrDefault();
+ if (segment == null)
+ return false;
+
+ return segment.DamageState == DamageState.Dead;
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Render/WithDeadBridgeSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithDeadBridgeSpriteBody.cs
new file mode 100644
index 0000000000..a40e4e5f29
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Render/WithDeadBridgeSpriteBody.cs
@@ -0,0 +1,100 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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.Collections.Generic;
+using System.Linq;
+using OpenRA.Graphics;
+using OpenRA.Mods.Common.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits.Render
+{
+ class WithDeadBridgeSpriteBodyInfo : WithSpriteBodyInfo
+ {
+ [ActorReference]
+ public readonly string[] RampActors = { };
+
+ [Desc("Offset to search for the 'A' neighbour")]
+ public readonly CVec AOffset = CVec.Zero;
+
+ [Desc("Position to search for the 'B' neighbour")]
+ public readonly CVec BOffset = CVec.Zero;
+
+ [SequenceReference]
+ public readonly string[] ARampSequences = { "aramp" };
+
+ [SequenceReference]
+ public readonly string[] BRampSequences = { "bramp" };
+
+ [SequenceReference]
+ public readonly string[] ABRampSequences = { "abramp" };
+
+ [SequenceReference]
+ [Desc("Placeholder sequence to use in the map editor.")]
+ public readonly string EditorSequence = "editor";
+
+ [PaletteReference]
+ [Desc("Palette to use for the editor placeholder.")]
+ public readonly string EditorPalette = "terrainalpha";
+
+ public override object Create(ActorInitializer init) { return new WithDeadBridgeSpriteBody(init, this); }
+
+ public override IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
+ {
+ var anim = new Animation(init.World, image);
+ var sequence = init.World.Type == WorldType.Editor ? EditorSequence : Sequence;
+ var palette = init.World.Type == WorldType.Editor ? init.WorldRenderer.Palette(EditorPalette) : p;
+ anim.PlayFetchIndex(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), sequence), () => 0);
+ yield return new SpriteActorPreview(anim, () => WVec.Zero, () => 0, palette, rs.Scale);
+ }
+ }
+
+ class WithDeadBridgeSpriteBody : WithSpriteBody
+ {
+ readonly WithDeadBridgeSpriteBodyInfo bridgeInfo;
+ readonly BridgeLayer bridgeLayer;
+
+ public WithDeadBridgeSpriteBody(ActorInitializer init, WithDeadBridgeSpriteBodyInfo info)
+ : base(init, info, () => 0)
+ {
+ bridgeInfo = info;
+ bridgeLayer = init.World.WorldActor.Trait();
+ }
+
+ bool RampExists(Actor self, CVec offset)
+ {
+ var neighbour = bridgeLayer[self.Location + offset];
+ if (neighbour == null)
+ return false;
+
+ return bridgeInfo.RampActors.Contains(neighbour.Info.Name);
+ }
+
+ protected override void OnBuildComplete(Actor self)
+ {
+ self.World.AddFrameEndTask(w =>
+ {
+ var aRamp = bridgeInfo.AOffset != CVec.Zero && RampExists(self, bridgeInfo.AOffset);
+ var bRamp = bridgeInfo.BOffset != CVec.Zero && RampExists(self, bridgeInfo.BOffset);
+
+ var sequence = DefaultAnimation.CurrentSequence.Name;
+ if (aRamp && bRamp && bridgeInfo.ABRampSequences.Any())
+ sequence = bridgeInfo.ABRampSequences.Random(Game.CosmeticRandom);
+ else if (aRamp && bridgeInfo.ARampSequences.Any())
+ sequence = bridgeInfo.ARampSequences.Random(Game.CosmeticRandom);
+ else if (bRamp && bridgeInfo.BRampSequences.Any())
+ sequence = bridgeInfo.BRampSequences.Random(Game.CosmeticRandom);
+
+ DefaultAnimation.PlayRepeating(NormalizeSequence(self, sequence));
+ });
+ }
+ }
+}
diff --git a/mods/ts/rules/bridges.yaml b/mods/ts/rules/bridges.yaml
index 49bc2b6fde..98cc31f891 100644
--- a/mods/ts/rules/bridges.yaml
+++ b/mods/ts/rules/bridges.yaml
@@ -49,19 +49,32 @@ LOBRDG_A:
NeighbourOffsets: 1,-1, 1,1
SpawnActorOnDeath:
Actor: lobrdg_a_d
+ -WithSpriteBody:
+ WithBridgeSpriteBody:
+ AOffset: 1,-1
+ BOffset: 1,1
+ Sequences: idle, idle2, idle3, idle4
+ ADestroyedSequences: adead
+ BDestroyedSequences: bdead
+ ABDestroyedSequences: abdead
LOBRDG_A_D:
Inherits: LOBRDG_A
- -RenderSprites:
- RenderSpritesEditorOnly:
- Palette: terrainalpha
EditorOnlyTooltip:
Name: Dead Bridge
-GroundLevelBridge:
-AppearsOnRadar:
BridgePlaceholder:
+ DamageState: Dead
ReplaceWithActor: lobrdg_a
NeighbourOffsets: 1,-1, 1,1
+ -WithBridgeSpriteBody:
+ WithDeadBridgeSpriteBody:
+ RampActors: lobrdg_r_ne, lobrdg_r_sw
+ AOffset: 1,-1
+ BOffset: 1,1
+ CustomSelectionSize:
+ CustomBounds: 96, 48
LOBRDG_B:
Inherits: ^LowBridge
@@ -72,52 +85,69 @@ LOBRDG_B:
NeighbourOffsets: -1,1, 1,1
SpawnActorOnDeath:
Actor: lobrdg_b_d
+ -WithSpriteBody:
+ WithBridgeSpriteBody:
+ AOffset: -1,1
+ BOffset: 1,1
+ Sequences: idle, idle2, idle3, idle4
+ ADestroyedSequences: adead
+ BDestroyedSequences: bdead
+ ABDestroyedSequences: abdead
LOBRDG_B_D:
Inherits: LOBRDG_B
- -RenderSprites:
- RenderSpritesEditorOnly:
- Palette: terrainalpha
EditorOnlyTooltip:
Name: Dead Bridge
-GroundLevelBridge:
-AppearsOnRadar:
BridgePlaceholder:
+ DamageState: Dead
ReplaceWithActor: lobrdg_b
NeighbourOffsets: -1,1, 1,1
+ -WithBridgeSpriteBody:
+ WithDeadBridgeSpriteBody:
+ RampActors: lobrdg_r_nw, lobrdg_r_se
+ AOffset: 1,1
+ BOffset: -1,1
+ CustomSelectionSize:
+ CustomBounds: 96, 48
LOBRDG_R_SE:
Inherits: ^LowBridgeRamp
Building:
Footprint: _ _ _
Dimensions: 1, 3
+ BridgePlaceholder:
+ NeighbourOffsets: -1,1
EditorOnlyTooltip:
Name: Bridge Ramp
- Description: South East
LOBRDG_R_NW:
Inherits: ^LowBridgeRamp
Building:
Footprint: _ _ _
Dimensions: 1, 3
+ BridgePlaceholder:
+ NeighbourOffsets: 1,1
EditorOnlyTooltip:
Name: Bridge Ramp
- Description: North West
LOBRDG_R_NE:
Inherits: ^LowBridgeRamp
Building:
Footprint: ___
Dimensions: 3, 1
+ BridgePlaceholder:
+ NeighbourOffsets: 1,1
EditorOnlyTooltip:
Name: Bridge Ramp
- Description: North East
LOBRDG_R_SW:
Inherits: ^LowBridgeRamp
Building:
Footprint: ___
Dimensions: 3, 1
+ BridgePlaceholder:
+ NeighbourOffsets: 1,-1
EditorOnlyTooltip:
Name: Bridge Ramp
- Description: South West
diff --git a/mods/ts/sequences/bridges.yaml b/mods/ts/sequences/bridges.yaml
index 9c22f720c1..76102f50f5 100644
--- a/mods/ts/sequences/bridges.yaml
+++ b/mods/ts/sequences/bridges.yaml
@@ -8,29 +8,39 @@
lobrdg_a:
Inherits: ^bridge
- idle: lobrdg10 # lobrdg11, 12, 13
- damaged-idle: lobrdg16
- sw: lobrdg14
- damaged-sw: lobrdg17
- ne: lobrdg15
- damaged-ne: lobrdg18
+ idle: lobrdg10
+ idle2: lobrdg11
+ idle3: lobrdg12
+ idle4: lobrdg13
+ adead: lobrdg15
+ bdead: lobrdg14
+ abdead: lobrdg16
lobrdg_a_d:
Inherits: ^bridge
- idle: lobrdg10 # actually lobrdg28
+ idle: lobrdg28
+ aramp: lobrdg17
+ bramp: lobrdg18
+ abramp: lobrdg28
+ editor: lobrdg10
lobrdg_b:
Inherits: ^bridge
- idle: lobrdg01 # lobrdg02, 03, 04
- damaged-idle: lobrdg07
- se: lobrdg06
- damaged-se: lobrdg09
- nw: lobrdg05
- damaged-nw: lobrdg08
+ idle: lobrdg01
+ idle2: lobrdg02
+ idle3: lobrdg03
+ idle4: lobrdg04
+ adead: lobrdg05
+ bdead: lobrdg06
+ abdead: lobrdg07
lobrdg_b_d:
Inherits: ^bridge
- idle: lobrdg01 # actually lobrdg27
+ idle: lobrdg27
+ aramp: lobrdg08
+ bramp: lobrdg09
+ abramp: lobrdg27
+ editor: lobrdg01
lobrdg_r_se:
Inherits: ^bridge