From 69e3c538d9052a52c94b3ee8732892a171503d4b Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 11 Nov 2016 19:07:12 +0000 Subject: [PATCH] Implement ground-level bridge destruction and repair. --- OpenRA.Mods.Common/Activities/RepairBridge.cs | 30 ++- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 4 + .../Traits/Buildings/BridgeHut.cs | 223 ++++++++++++++++++ .../Traits/Buildings/BridgePlaceholder.cs | 80 +++++++ .../Traits/Buildings/GroundLevelBridge.cs | 119 ++++++++++ OpenRA.Mods.Common/Traits/RepairsBridges.cs | 68 ++++-- .../Traits/World/BridgeLayer.cs | 59 +++++ OpenRA.Mods.TS/OpenRA.Mods.TS.csproj | 1 - .../Traits/Buildings/GroundLevelBridge.cs | 70 ------ mods/ts/maps/tread_l/map.yaml | 8 - mods/ts/rules/bridges.yaml | 90 ++++++- mods/ts/rules/civilian-structures.yaml | 13 - mods/ts/rules/defaults.yaml | 30 --- mods/ts/rules/world.yaml | 1 + mods/ts/weapons/explosions.yaml | 8 + 15 files changed, 648 insertions(+), 156 deletions(-) create mode 100644 OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs create mode 100644 OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs create mode 100644 OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs create mode 100644 OpenRA.Mods.Common/Traits/World/BridgeLayer.cs delete mode 100644 OpenRA.Mods.TS/Traits/Buildings/GroundLevelBridge.cs diff --git a/OpenRA.Mods.Common/Activities/RepairBridge.cs b/OpenRA.Mods.Common/Activities/RepairBridge.cs index 561467b522..192a1f8691 100644 --- a/OpenRA.Mods.Common/Activities/RepairBridge.cs +++ b/OpenRA.Mods.Common/Activities/RepairBridge.cs @@ -16,27 +16,47 @@ namespace OpenRA.Mods.Common.Activities { class RepairBridge : Enter { + readonly Actor target; readonly LegacyBridgeHut legacyHut; + readonly BridgeHut hut; readonly string notification; public RepairBridge(Actor self, Actor target, EnterBehaviour enterBehaviour, string notification) : base(self, target, enterBehaviour) { - legacyHut = target.Trait(); + this.target = target; + legacyHut = target.TraitOrDefault(); + hut = target.TraitOrDefault(); this.notification = notification; } protected override bool CanReserve(Actor self) { - return legacyHut.BridgeDamageState != DamageState.Undamaged && !legacyHut.Repairing && legacyHut.Bridge.GetHut(0) != null && legacyHut.Bridge.GetHut(1) != null; + if (legacyHut != null) + return legacyHut.BridgeDamageState != DamageState.Undamaged && !legacyHut.Repairing && legacyHut.Bridge.GetHut(0) != null && legacyHut.Bridge.GetHut(1) != null; + + if (hut != null) + return hut.BridgeDamageState != DamageState.Undamaged && !hut.Repairing; + + return false; } protected override void OnInside(Actor self) { - if (legacyHut.BridgeDamageState == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.GetHut(0) == null || legacyHut.Bridge.GetHut(1) == null) - return; + if (legacyHut != null) + { + if (legacyHut.BridgeDamageState == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.GetHut(0) == null || legacyHut.Bridge.GetHut(1) == null) + return; - legacyHut.Repair(self); + legacyHut.Repair(self); + } + else if (hut != null) + { + if (hut.BridgeDamageState == DamageState.Undamaged || hut.Repairing) + return; + + hut.Repair(target, self); + } Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", notification, self.Owner.Faction.InternalName); } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 733c0e2af3..8140facf1b 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -777,6 +777,10 @@ + + + + diff --git a/OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs b/OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs new file mode 100644 index 0000000000..f18bfa6661 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs @@ -0,0 +1,223 @@ +#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.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Allows bridges to be targeted for demolition and repair.")] + class BridgeHutInfo : IDemolishableInfo, ITraitInfo + { + [Desc("Bridge types to act on")] + public readonly string[] Types = { "GroundLevelBridge" }; + + [Desc("Offsets to look for adjacent bridges to act on")] + public readonly CVec[] NeighbourOffsets = { }; + + [Desc("Delay between each segment repair step")] + public readonly int RepairPropagationDelay = 20; + + [Desc("Delay between each segment demolish step")] + public readonly int DemolishPropagationDelay = 5; + + [Desc("Hide the repair cursor if the bridge is only damaged (not destroyed)")] + public readonly bool RequireForceAttackForHeal = false; + + public bool IsValidTarget(ActorInfo actorInfo, Actor saboteur) { return false; } // TODO: bridges don't support frozen under fog + + public object Create(ActorInitializer init) { return new BridgeHut(init.World, this); } + } + + class BridgeHut : INotifyCreated, IDemolishable, ITick + { + public readonly BridgeHutInfo Info; + readonly BridgeLayer bridgeLayer; + + // Fixed at map load + readonly List segmentLocations = new List(); + + // Changes as segments are killed and repaired + readonly Dictionary segments = new Dictionary(); + readonly HashSet dirtyLocations = new HashSet(); + + // Enabled during a repair action + int repairStep; + int repairDelay; + Actor repairRepairer; + + // Enabled during a demolish action + int demolishStep; + int demolishDelay; + Actor demolishSaboteur; + + public BridgeHut(World world, BridgeHutInfo info) + { + Info = info; + bridgeLayer = world.WorldActor.Trait(); + } + + void INotifyCreated.Created(Actor self) + { + self.World.AddFrameEndTask(w => + { + // Bridge segments and huts are expected to be placed in the map + // editor or spawned during the normal actor loading + // + // The number and location of bridge segments are calculated here, + // and assumed to not change for the remaining lifetime of the world + // + // Bridge segment footprints and neighbour offsets are assumed to remain + // the same when a segment is destroyed or repaired. + var seed = Info.NeighbourOffsets.Select(v => self.Location + v); + var processed = new HashSet(); + while (true) + { + var step = NextNeighbourStep(seed, processed).ToList(); + if (!step.Any()) + break; + + foreach (var s in step) + segments[s.Location] = s; + + segmentLocations.Add(step.Select(s => s.Location).ToArray()); + seed = step.SelectMany(s => s.NeighbourOffsets.Select(n => s.Location + n)).ToList(); + } + + repairStep = demolishStep = segmentLocations.Count; + }); + } + + void ITick.Tick(Actor self) + { + // Update any dead segments + dirtyLocations.Clear(); + foreach (var kv in segments) + if (!kv.Value.Valid) + dirtyLocations.Add(kv.Key); + + foreach (var c in dirtyLocations) + segments[c] = bridgeLayer[c].TraitOrDefault(); + + if (repairStep < segmentLocations.Count && --repairDelay <= 0) + RepairStep(); + + if (demolishStep < segmentLocations.Count && --demolishDelay <= 0) + DemolishStep(); + } + + IEnumerable NextNeighbourStep(IEnumerable seed, HashSet processed) + { + foreach (var c in seed) + { + var bridge = bridgeLayer[c]; + if (bridge == null) + continue; + + var segment = bridge.TraitOrDefault(); + if (segment != null && Info.Types.Contains(segment.Type) && processed.Add(segment.Location)) + yield return segment; + } + } + + public void Repair(Actor self, Actor repairer) + { + if (Info.RepairPropagationDelay > 0) + { + repairStep = 0; + repairRepairer = repairer; + RepairStep(); + } + else + foreach (var s in segments.Values) + s.Repair(repairer); + } + + public void RepairStep() + { + // Find the next segment that needs to be repaired + while (repairStep < segmentLocations.Count) + { + var stepDamage = segmentLocations[repairStep] + .Select(c => segments[c]) + .Max(s => s.DamageState); + + if (stepDamage > DamageState.Undamaged) + break; + + repairStep++; + } + + if (repairStep < segmentLocations.Count) + foreach (var c in segmentLocations[repairStep]) + segments[c].Repair(repairRepairer); + + repairDelay = Info.RepairPropagationDelay; + } + + public void Demolish(Actor self, Actor saboteur) + { + if (Info.DemolishPropagationDelay > 0) + { + demolishStep = 0; + demolishSaboteur = saboteur; + DemolishStep(); + } + else + foreach (var s in segments.Values) + s.Demolish(saboteur); + } + + public void DemolishStep() + { + // Find the next segment to demolish + while (demolishStep < segmentLocations.Count) + { + var stepDamage = segmentLocations[demolishStep] + .Select(c => segments[c]) + .Max(s => s.DamageState); + + if (stepDamage < DamageState.Dead) + break; + + demolishStep++; + } + + if (demolishStep < segmentLocations.Count) + foreach (var c in segmentLocations[demolishStep]) + segments[c].Demolish(demolishSaboteur); + + demolishDelay = Info.DemolishPropagationDelay; + + // Always advance at least one step (prevents sticking on placeholders) + demolishStep++; + } + + public bool IsValidTarget(Actor self, Actor saboteur) + { + return true; + } + + public DamageState BridgeDamageState + { + get + { + if (!segments.Any()) + return DamageState.Undamaged; + + return segments.Values.Max(s => s.DamageState); + } + } + + public bool Repairing { get { return repairStep < segmentLocations.Count; } } + } +} diff --git a/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs b/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs new file mode 100644 index 0000000000..189a87e27b --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs @@ -0,0 +1,80 @@ +#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.Linq; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Placeholder actor that transforms into another actor type when repaired.")] + class BridgePlaceholderInfo : ITraitInfo + { + public readonly string Type = "GroundLevelBridge"; + + [FieldLoader.Require] + [Desc("Actor type to replace with on repair.")] + [ActorReference] public readonly string ReplaceWithActor = null; + + public readonly CVec[] NeighbourOffsets = { }; + + public object Create(ActorInitializer init) { return new BridgePlaceholder(init.Self, this); } + } + + class BridgePlaceholder : IBridgeSegment, INotifyAddedToWorld, INotifyRemovedFromWorld + { + public readonly BridgePlaceholderInfo Info; + readonly Actor self; + readonly BridgeLayer bridgeLayer; + + public BridgePlaceholder(Actor self, BridgePlaceholderInfo info) + { + Info = info; + this.self = self; + bridgeLayer = self.World.WorldActor.Trait(); + } + + void INotifyAddedToWorld.AddedToWorld(Actor self) + { + bridgeLayer.Add(self); + } + + void INotifyRemovedFromWorld.RemovedFromWorld(Actor self) + { + bridgeLayer.Remove(self); + } + + void IBridgeSegment.Repair(Actor repairer) + { + self.World.AddFrameEndTask(w => + { + self.Dispose(); + + w.CreateActor(Info.ReplaceWithActor, new TypeDictionary + { + new LocationInit(self.Location), + new OwnerInit(self.Owner), + }); + }); + } + + void IBridgeSegment.Demolish(Actor saboteur) + { + // Do nothing, we're already dead + } + + string IBridgeSegment.Type { get { return Info.Type; } } + DamageState IBridgeSegment.DamageState { get { return DamageState.Dead; } } + bool IBridgeSegment.Valid { get { return self.IsInWorld; } } + CVec[] IBridgeSegment.NeighbourOffsets { get { return Info.NeighbourOffsets; } } + CPos IBridgeSegment.Location { get { return self.Location; } } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs new file mode 100644 index 0000000000..2fb1d3c5f5 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs @@ -0,0 +1,119 @@ +#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.GameRules; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Bridge actor that can't be passed underneath.")] + class GroundLevelBridgeInfo : ITraitInfo, IRulesetLoaded, Requires, Requires + { + public readonly string TerrainType = "Bridge"; + + public readonly string Type = "GroundLevelBridge"; + + public readonly CVec[] NeighbourOffsets = { }; + + [Desc("The name of the weapon to use when demolishing the bridge")] + [WeaponReference] public readonly string DemolishWeapon = "Demolish"; + + public WeaponInfo DemolishWeaponInfo { get; private set; } + + public void RulesetLoaded(Ruleset rules, ActorInfo ai) { DemolishWeaponInfo = rules.Weapons[DemolishWeapon.ToLowerInvariant()]; } + + public object Create(ActorInitializer init) { return new GroundLevelBridge(init.Self, this); } + } + + class GroundLevelBridge : IBridgeSegment, INotifyAddedToWorld, INotifyRemovedFromWorld + { + public readonly GroundLevelBridgeInfo Info; + readonly Actor self; + readonly BridgeLayer bridgeLayer; + readonly IEnumerable cells; + readonly Health health; + + public GroundLevelBridge(Actor self, GroundLevelBridgeInfo info) + { + Info = info; + this.self = self; + health = self.Trait(); + + bridgeLayer = self.World.WorldActor.Trait(); + var buildingInfo = self.Info.TraitInfo(); + cells = FootprintUtils.PathableTiles(self.Info.Name, buildingInfo, self.Location); + } + + void UpdateTerrain(Actor self, byte terrainIndex) + { + foreach (var cell in cells) + self.World.Map.CustomTerrain[cell] = terrainIndex; + + var domainIndex = self.World.WorldActor.TraitOrDefault(); + if (domainIndex != null) + domainIndex.UpdateCells(self.World, cells); + } + + void INotifyAddedToWorld.AddedToWorld(Actor self) + { + bridgeLayer.Add(self); + + var tileSet = self.World.Map.Rules.TileSet; + var terrainIndex = tileSet.GetTerrainIndex(Info.TerrainType); + UpdateTerrain(self, terrainIndex); + KillInvalidActorsInFootprint(self); + } + + void INotifyRemovedFromWorld.RemovedFromWorld(Actor self) + { + bridgeLayer.Remove(self); + + UpdateTerrain(self, byte.MaxValue); + KillInvalidActorsInFootprint(self); + } + + void KillInvalidActorsInFootprint(Actor self) + { + foreach (var c in cells) + foreach (var a in self.World.ActorMap.GetActorsAt(c)) + if (a.Info.HasTraitInfo() && !a.Trait().CanEnterCell(c)) + a.Kill(self); + } + + void IBridgeSegment.Repair(Actor repairer) + { + health.InflictDamage(self, repairer, new Damage(-health.MaxHP), true); + } + + void IBridgeSegment.Demolish(Actor saboteur) + { + self.World.AddFrameEndTask(w => + { + if (self.IsDead) + return; + + // Use .FromPos since this actor is dead. Cannot use Target.FromActor + Info.DemolishWeaponInfo.Impact(Target.FromPos(self.CenterPosition), saboteur, Enumerable.Empty()); + + self.World.WorldActor.Trait().AddEffect(15, self.CenterPosition, 6); + self.Kill(saboteur); + }); + } + + string IBridgeSegment.Type { get { return Info.Type; } } + DamageState IBridgeSegment.DamageState { get { return self.GetDamageState(); } } + bool IBridgeSegment.Valid { get { return self.IsInWorld; } } + CVec[] IBridgeSegment.NeighbourOffsets { get { return Info.NeighbourOffsets; } } + CPos IBridgeSegment.Location { get { return self.Location; } } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/RepairsBridges.cs b/OpenRA.Mods.Common/Traits/RepairsBridges.cs index dc61231a35..ddf8c0011b 100644 --- a/OpenRA.Mods.Common/Traits/RepairsBridges.cs +++ b/OpenRA.Mods.Common/Traits/RepairsBridges.cs @@ -17,7 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - [Desc("Can enter a LegacyBridgeHut to trigger a repair.")] + [Desc("Can enter a BridgeHut or LegacyBridgeHut to trigger a repair.")] class RepairsBridgesInfo : ITraitInfo { [VoiceReference] public readonly string Voice = "Action"; @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits "Possible values are Exit, Suicide, Dispose.")] public readonly EnterBehaviour EnterBehaviour = EnterBehaviour.Dispose; - [Desc("Cursor to use when targeting a LegacyBridgeHut of an unrepaired bridge.")] + [Desc("Cursor to use when targeting an unrepaired bridge.")] public readonly string TargetCursor = "goldwrench"; [Desc("Cursor to use when repairing is denied.")] @@ -66,10 +66,14 @@ namespace OpenRA.Mods.Common.Traits return null; var legacyHut = order.TargetActor.TraitOrDefault(); - if (legacyHut == null) - return null; + if (legacyHut != null) + return legacyHut.BridgeDamageState == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.IsDangling ? null : info.Voice; - return legacyHut.BridgeDamageState == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.IsDangling ? null : info.Voice; + var hut = order.TargetActor.TraitOrDefault(); + if (hut != null) + return hut.BridgeDamageState == DamageState.Undamaged || hut.Repairing ? null : info.Voice; + + return null; } public void ResolveOrder(Actor self, Order order) @@ -77,10 +81,18 @@ namespace OpenRA.Mods.Common.Traits if (order.OrderString == "RepairBridge") { var legacyHut = order.TargetActor.TraitOrDefault(); - if (legacyHut == null) - return; - - if (legacyHut.BridgeDamageState == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.IsDangling) + var hut = order.TargetActor.TraitOrDefault(); + if (legacyHut != null) + { + if (legacyHut.BridgeDamageState == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.IsDangling) + return; + } + else if (hut != null) + { + if (hut.BridgeDamageState == DamageState.Undamaged || hut.Repairing) + return; + } + else return; self.SetTargetLine(Target.FromOrder(self.World, order), Color.Yellow); @@ -102,22 +114,36 @@ namespace OpenRA.Mods.Common.Traits public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - var legacyHut = target.TraitOrDefault(); - if (legacyHut == null) - return false; - - // Require force attack to heal partially damaged bridges to avoid unnecessary cursor noise - var damage = legacyHut.BridgeDamageState; - if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && damage != DamageState.Dead) - return false; - // Obey force moving onto bridges if (modifiers.HasModifier(TargetModifiers.ForceMove)) return false; - // Can't repair a bridge that is undamaged, already under repair, or dangling - if (damage == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.IsDangling) - cursor = info.TargetBlockedCursor; + var legacyHut = target.TraitOrDefault(); + var hut = target.TraitOrDefault(); + if (legacyHut != null) + { + // Require force attack to heal partially damaged bridges to avoid unnecessary cursor noise + var damage = legacyHut.BridgeDamageState; + if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && damage != DamageState.Dead) + return false; + + // Can't repair a bridge that is undamaged, already under repair, or dangling + if (damage == DamageState.Undamaged || legacyHut.Repairing || legacyHut.Bridge.IsDangling) + cursor = info.TargetBlockedCursor; + } + else if (hut != null) + { + // Require force attack to heal partially damaged bridges to avoid unnecessary cursor noise + var damage = hut.BridgeDamageState; + if (hut.Info.RequireForceAttackForHeal && !modifiers.HasModifier(TargetModifiers.ForceAttack) && damage != DamageState.Dead) + return false; + + // Can't repair a bridge that is undamaged, already under repair, or dangling + if (damage == DamageState.Undamaged || hut.Repairing) + cursor = info.TargetBlockedCursor; + } + else + return false; return true; } diff --git a/OpenRA.Mods.Common/Traits/World/BridgeLayer.cs b/OpenRA.Mods.Common/Traits/World/BridgeLayer.cs new file mode 100644 index 0000000000..a25b5ad3b8 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/World/BridgeLayer.cs @@ -0,0 +1,59 @@ +#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 OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + interface IBridgeSegment + { + void Repair(Actor repairer); + void Demolish(Actor saboteur); + + string Type { get; } + DamageState DamageState { get; } + CVec[] NeighbourOffsets { get; } + bool Valid { get; } + CPos Location { get; } + } + + class BridgeLayerInfo : ITraitInfo + { + public object Create(ActorInitializer init) { return new BridgeLayer(init.World); } + } + + class BridgeLayer + { + readonly CellLayer bridges; + + public BridgeLayer(World world) + { + bridges = new CellLayer(world.Map); + } + + public Actor this[CPos cell] { get { return bridges[cell]; } } + + public void Add(Actor b) + { + var buildingInfo = b.Info.TraitInfo(); + foreach (var c in FootprintUtils.PathableTiles(b.Info.Name, buildingInfo, b.Location)) + bridges[c] = b; + } + + public void Remove(Actor b) + { + var buildingInfo = b.Info.TraitInfo(); + foreach (var c in FootprintUtils.PathableTiles(b.Info.Name, buildingInfo, b.Location)) + if (bridges[c] == b) + bridges[c] = null; + } + } +} diff --git a/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj b/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj index c1706d9095..7bb2cea30b 100644 --- a/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj +++ b/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj @@ -56,7 +56,6 @@ - diff --git a/OpenRA.Mods.TS/Traits/Buildings/GroundLevelBridge.cs b/OpenRA.Mods.TS/Traits/Buildings/GroundLevelBridge.cs deleted file mode 100644 index eb4ddb86a0..0000000000 --- a/OpenRA.Mods.TS/Traits/Buildings/GroundLevelBridge.cs +++ /dev/null @@ -1,70 +0,0 @@ -#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 OpenRA.Mods.Common.Traits; -using OpenRA.Traits; - -namespace OpenRA.Mods.TS.Traits -{ - [Desc("Bridge actor that can't be passed underneath.")] - class GroundLevelBridgeInfo : ITraitInfo, Requires - { - public readonly string TerrainType = "Bridge"; - - public object Create(ActorInitializer init) { return new GroundLevelBridge(init.Self, this); } - } - - class GroundLevelBridge : INotifyAddedToWorld, INotifyRemovedFromWorld - { - readonly GroundLevelBridgeInfo info; - readonly IEnumerable cells; - - public GroundLevelBridge(Actor self, GroundLevelBridgeInfo info) - { - this.info = info; - - var buildingInfo = self.Info.TraitInfo(); - cells = FootprintUtils.PathableTiles(self.Info.Name, buildingInfo, self.Location); - } - - void UpdateTerrain(Actor self, byte terrainIndex) - { - foreach (var cell in cells) - self.World.Map.CustomTerrain[cell] = terrainIndex; - - var domainIndex = self.World.WorldActor.TraitOrDefault(); - if (domainIndex != null) - domainIndex.UpdateCells(self.World, cells); - } - - void INotifyAddedToWorld.AddedToWorld(Actor self) - { - var tileSet = self.World.Map.Rules.TileSet; - var terrainIndex = tileSet.GetTerrainIndex(info.TerrainType); - UpdateTerrain(self, terrainIndex); - } - - void KillUnitsOnBridge(Actor self) - { - foreach (var c in cells) - foreach (var a in self.World.ActorMap.GetActorsAt(c)) - if (a.Info.HasTraitInfo() && !a.Trait().CanEnterCell(c)) - a.Kill(self); - } - - void INotifyRemovedFromWorld.RemovedFromWorld(Actor self) - { - UpdateTerrain(self, byte.MaxValue); - KillUnitsOnBridge(self); - } - } -} diff --git a/mods/ts/maps/tread_l/map.yaml b/mods/ts/maps/tread_l/map.yaml index f4339d5323..b2d32eb01b 100644 --- a/mods/ts/maps/tread_l/map.yaml +++ b/mods/ts/maps/tread_l/map.yaml @@ -1247,11 +1247,3 @@ Rules: World: GlobalLightingPaletteEffect: Ambient: 0.75 - LOBRDG_A: - Targetable: - TargetTypes: Ground, Building - RequiresForceFire: true - Health: - HP: 1000 - Armor: - Type: Concrete diff --git a/mods/ts/rules/bridges.yaml b/mods/ts/rules/bridges.yaml index 826eb1c2d4..49bc2b6fde 100644 --- a/mods/ts/rules/bridges.yaml +++ b/mods/ts/rules/bridges.yaml @@ -1,8 +1,57 @@ +CABHUT: + Inherits: ^BasicBuilding + Tooltip: + Name: Bridge repair hut + Building: + Adjacent: 0 + Footprint: x + Dimensions: 1, 1 + BridgeHut: + NeighbourOffsets: -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 + RenderSprites: + Palette: player + Targetable: + TargetTypes: C4 + -SelectionDecorations: + -Demolishable: + +^LowBridgeRamp: + AlwaysVisible: + RenderSprites: + Palette: terraindecoration + WithSpriteBody: + AutoSelectionSize: + AppearsOnRadar: + RadarColorFromTerrain: + Terrain: Bridge + BodyOrientation: + UseClassicPerspectiveFudge: false + QuantizedFacings: 1 + Tooltip: + Name: Bridge + +^LowBridge: + Inherits: ^LowBridgeRamp + Targetable: + TargetTypes: Ground, Building + RequiresForceFire: true + Health: + HP: 500 + Armor: + Type: Concrete + LOBRDG_A: - Inherits: ^LowBridge_A + Inherits: ^LowBridge + Building: + Footprint: ___ + Dimensions: 3, 1 + GroundLevelBridge: + NeighbourOffsets: 1,-1, 1,1 + SpawnActorOnDeath: + Actor: lobrdg_a_d LOBRDG_A_D: - Inherits: ^LowBridge_A + Inherits: LOBRDG_A -RenderSprites: RenderSpritesEditorOnly: Palette: terrainalpha @@ -10,12 +59,22 @@ LOBRDG_A_D: Name: Dead Bridge -GroundLevelBridge: -AppearsOnRadar: + BridgePlaceholder: + ReplaceWithActor: lobrdg_a + NeighbourOffsets: 1,-1, 1,1 LOBRDG_B: - Inherits: ^LowBridge_B + Inherits: ^LowBridge + Building: + Footprint: _ _ _ + Dimensions: 1, 3 + GroundLevelBridge: + NeighbourOffsets: -1,1, 1,1 + SpawnActorOnDeath: + Actor: lobrdg_b_d LOBRDG_B_D: - Inherits: ^LowBridge_B + Inherits: LOBRDG_B -RenderSprites: RenderSpritesEditorOnly: Palette: terrainalpha @@ -23,27 +82,42 @@ LOBRDG_B_D: Name: Dead Bridge -GroundLevelBridge: -AppearsOnRadar: + BridgePlaceholder: + ReplaceWithActor: lobrdg_b + NeighbourOffsets: -1,1, 1,1 LOBRDG_R_SE: - Inherits: ^LowBridge_B + Inherits: ^LowBridgeRamp + Building: + Footprint: _ _ _ + Dimensions: 1, 3 EditorOnlyTooltip: Name: Bridge Ramp Description: South East LOBRDG_R_NW: - Inherits: ^LowBridge_B + Inherits: ^LowBridgeRamp + Building: + Footprint: _ _ _ + Dimensions: 1, 3 EditorOnlyTooltip: Name: Bridge Ramp Description: North West LOBRDG_R_NE: - Inherits: ^LowBridge_A + Inherits: ^LowBridgeRamp + Building: + Footprint: ___ + Dimensions: 3, 1 EditorOnlyTooltip: Name: Bridge Ramp Description: North East LOBRDG_R_SW: - Inherits: ^LowBridge_A + Inherits: ^LowBridgeRamp + Building: + Footprint: ___ + Dimensions: 3, 1 EditorOnlyTooltip: Name: Bridge Ramp Description: South West diff --git a/mods/ts/rules/civilian-structures.yaml b/mods/ts/rules/civilian-structures.yaml index 5e73693476..e6ece37203 100644 --- a/mods/ts/rules/civilian-structures.yaml +++ b/mods/ts/rules/civilian-structures.yaml @@ -645,19 +645,6 @@ CAARMR: Prerequisite: barracks.upgraded Capturable: -CABHUT: - Inherits: ^CivBuilding - Tooltip: - Name: Bridge repair hut - Building: - Adjacent: 0 - Footprint: x - Dimensions: 1, 1 - Health: - HP: 2000 - RenderSprites: - Palette: player - CACRSH01: Inherits: ^Decoration Tooltip: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 745862ee92..29ac068747 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -901,36 +901,6 @@ LineBuildNode: Connections: 0,-1, 0,1 -^LowBridge: - AlwaysVisible: - RenderSprites: - Palette: terraindecoration - WithSpriteBody: - AutoSelectionSize: - AppearsOnRadar: - RadarColorFromTerrain: - Terrain: Bridge - BodyOrientation: - UseClassicPerspectiveFudge: false - QuantizedFacings: 1 - Tooltip: - Name: Bridge - Health: - -^LowBridge_A: - Inherits: ^LowBridge - Building: - Footprint: ___ - Dimensions: 3, 1 - GroundLevelBridge: - -^LowBridge_B: - Inherits: ^LowBridge - Building: - Footprint: _ _ _ - Dimensions: 1, 3 - GroundLevelBridge: - ^HealsOnTiberium: DamagedByTerrain: Damage: -2 diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index 1da114f2ac..f17fcb7957 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -99,6 +99,7 @@ World: SmokeType: largesmoke Sequence: largecraters ResourceLayer: + BridgeLayer: CustomTerrainDebugOverlay: ResourceClaimLayer: WarheadDebugOverlay: diff --git a/mods/ts/weapons/explosions.yaml b/mods/ts/weapons/explosions.yaml index e419809df1..3b8c89ee11 100644 --- a/mods/ts/weapons/explosions.yaml +++ b/mods/ts/weapons/explosions.yaml @@ -78,3 +78,11 @@ LargeDebris: Image: dbrislg Sequences: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 Shadow: true + +Demolish: + Warhead@1Dam: SpreadDamage + DamageTypes: DefaultDeath + Warhead@2Eff: CreateEffect + Explosions: large_twlt + ExplosionPalette: effectalpha75 + ImpactSounds: expnew09.aud \ No newline at end of file