diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 733ba6259f..9928f29a32 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -803,6 +803,8 @@ + + diff --git a/OpenRA.Mods.Common/Traits/EntersTunnels.cs b/OpenRA.Mods.Common/Traits/EntersTunnels.cs new file mode 100644 index 0000000000..36480467b3 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/EntersTunnels.cs @@ -0,0 +1,132 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 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.Drawing; +using System.Linq; +using OpenRA.Mods.Common.Orders; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("This actor can interact with TunnelEntrances to move through TerrainTunnels.")] + public class EntersTunnelsInfo : ITraitInfo, Requires + { + public readonly string EnterCursor = "enter"; + public readonly string EnterBlockedCursor = "enter-blocked"; + + [VoiceReference] public readonly string Voice = "Action"; + + public object Create(ActorInitializer init) { return new EntersTunnels(init.Self, this); } + } + + public class EntersTunnels : IIssueOrder, IResolveOrder, IOrderVoice + { + readonly EntersTunnelsInfo info; + readonly IMove move; + + public EntersTunnels(Actor self, EntersTunnelsInfo info) + { + this.info = info; + move = self.Trait(); + } + + public IEnumerable Orders + { + get + { + yield return new EnterTunnelOrderTargeter(info); + } + } + + public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) + { + if (order.OrderID != "EnterTunnel") + return null; + + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID, SuppressVisualFeedback = true }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor, SuppressVisualFeedback = true }; + } + + public string VoicePhraseForOrder(Actor self, Order order) + { + return order.OrderString == "EnterTunnel" ? info.Voice : null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString != "EnterTunnel") + return; + + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + var tunnel = target.Actor.TraitOrDefault(); + if (!tunnel.Exit.HasValue) + return; + + if (!order.Queued) + self.CancelActivity(); + + self.SetTargetLine(Target.FromCell(self.World, tunnel.Exit.Value), Color.Green); + self.QueueActivity(move.MoveTo(tunnel.Entrance, tunnel.NearEnough)); + self.QueueActivity(move.MoveTo(tunnel.Exit.Value, tunnel.NearEnough)); + } + + class EnterTunnelOrderTargeter : UnitOrderTargeter + { + readonly EntersTunnelsInfo info; + + public EnterTunnelOrderTargeter(EntersTunnelsInfo info) + : base("EnterTunnel", 6, info.EnterCursor, true, true) + { + this.info = info; + } + + public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + { + if (target.IsDead) + return false; + + var tunnel = target.TraitOrDefault(); + if (tunnel == null) + return false; + + // HACK: The engine does not support HiddenUnderFog combined with buildings that use the "_" footprint + // We therefore have to use AlwaysVisible and then force-disable interacting with the entrance under shroud + var buildingInfo = target.Info.TraitInfoOrDefault(); + if (buildingInfo != null) + { + var footprint = FootprintUtils.PathableTiles(target.Info.Name, buildingInfo, target.Location); + if (footprint.All(c => self.World.ShroudObscures(c))) + return false; + } + + if (!tunnel.Exit.HasValue) + { + cursor = info.EnterBlockedCursor; + return false; + } + + cursor = info.EnterCursor; + return true; + } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + return CanTargetActor(self, target.Actor, modifiers, ref cursor); + } + } + } +} diff --git a/OpenRA.Mods.Common/Traits/TunnelEntrance.cs b/OpenRA.Mods.Common/Traits/TunnelEntrance.cs new file mode 100644 index 0000000000..8e4afcef64 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/TunnelEntrance.cs @@ -0,0 +1,74 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Provides a target for players to issue orders for units to move through a TerrainTunnel.", + "The host actor should be placed so that the Sensor position overlaps one of the TerrainTunnel portal cells.")] + public class TunnelEntranceInfo : ITraitInfo + { + [FieldLoader.Require] + [Desc("Offset to use as a staging point for actors entering or exiting the tunnel.", + "Should be at least Margin cells away from the actual entrance.")] + public readonly CVec RallyPoint = CVec.Zero; + + [Desc("Cell radius to use as a staging area around the RallyPoint.")] + public readonly int Margin = 2; + + [Desc("Offset to check for the corresponding TerrainTunnel portal cell(s).")] + public readonly CVec Sensor = CVec.Zero; + + public object Create(ActorInitializer init) { return new TunnelEntrance(init.Self, this); } + } + + public class TunnelEntrance : INotifyCreated + { + readonly TunnelEntranceInfo info; + + public readonly CPos Entrance; + public CPos? Exit { get; private set; } + public int NearEnough { get { return info.Margin; } } + + public TunnelEntrance(Actor self, TunnelEntranceInfo info) + { + this.info = info; + + Entrance = self.Location + info.RallyPoint; + } + + void INotifyCreated.Created(Actor self) + { + // Find the map tunnel associated with this entrance + var sensor = self.Location + info.Sensor; + var tunnel = self.World.WorldActor.Info.TraitInfos() + .FirstOrDefault(tti => tti.PortalCells().Contains(sensor)); + + if (tunnel != null) + { + // Find the matching entrance at the other end of the tunnel + // Run at the end of the tick to make sure that all the entrances exist in the world + self.World.AddFrameEndTask(w => + { + var portalCells = tunnel.PortalCells().ToList(); + var other = self.World.ActorsWithTrait() + .FirstOrDefault(x => x.Actor != self && portalCells.Contains(x.Actor.Location + x.Trait.info.Sensor)); + + if (other.Trait != null) + Exit = other.Trait.Entrance; + }); + } + } + } +} diff --git a/mods/ts/maps/arivruns/map.yaml b/mods/ts/maps/arivruns/map.yaml index 9f9afa2fe8..aead5c973a 100644 --- a/mods/ts/maps/arivruns/map.yaml +++ b/mods/ts/maps/arivruns/map.yaml @@ -1552,16 +1552,16 @@ Actors: Owner: Neutral Actor620: tuntop04 Owner: Neutral - Location: 145,5 + Location: 146,5 Actor621: tuntop04 Owner: Neutral - Location: 116,9 + Location: 117,9 Actor622: tuntop02 Owner: Neutral - Location: 144,-19 + Location: 146,-19 Actor623: tuntop02 Owner: Neutral - Location: 115,-15 + Location: 117,-15 Actor494: bridge1 Owner: Neutral Location: 232,0 diff --git a/mods/ts/maps/springs/map.yaml b/mods/ts/maps/springs/map.yaml index 5fb470c3ca..73d51e0f05 100644 --- a/mods/ts/maps/springs/map.yaml +++ b/mods/ts/maps/springs/map.yaml @@ -806,49 +806,49 @@ Actors: Owner: Neutral Actor219: tuntop03 Owner: Neutral - Location: 68,-5 + Location: 68,-4 Actor220: tuntop04 Owner: Neutral - Location: 73,-12 + Location: 74,-12 Actor221: tuntop04 Owner: Neutral - Location: 107,-7 + Location: 108,-7 Actor222: tuntop03 Owner: Neutral - Location: 109,4 + Location: 109,5 Actor224: tuntop04 Owner: Neutral - Location: 87,28 + Location: 88,28 Actor225: tuntop03 Owner: Neutral - Location: 73,34 + Location: 73,35 Actor227: tuntop01 Owner: Neutral - Location: 60,34 + Location: 60,35 Actor228: tuntop04 Owner: Neutral - Location: 42,13 + Location: 43,13 Actor233: tuntop03 Owner: Neutral - Location: 93,-37 + Location: 93,-36 Actor237: tuntop02 Owner: Neutral - Location: 107,-16 + Location: 108,-16 Actor238: tuntop01 Owner: Neutral - Location: 77,-37 + Location: 77,-36 Actor239: tuntop01 Owner: Neutral - Location: 95,4 + Location: 95,5 Actor240: tuntop02 Owner: Neutral Location: 73,-28 Actor234: tuntop02 Owner: Neutral - Location: 42,-2 + Location: 43,-2 Actor236: tuntop01 Owner: Neutral - Location: 51,-5 + Location: 51,-4 Actor235: tuntop02 Owner: Neutral Location: 88,13 diff --git a/mods/ts/maps/sunstroke/map.png b/mods/ts/maps/sunstroke/map.png index dbec7ee702..0bacd1cd87 100644 Binary files a/mods/ts/maps/sunstroke/map.png and b/mods/ts/maps/sunstroke/map.png differ diff --git a/mods/ts/maps/sunstroke/map.yaml b/mods/ts/maps/sunstroke/map.yaml index 0ca6d2b19f..ad61a2ff5f 100644 --- a/mods/ts/maps/sunstroke/map.yaml +++ b/mods/ts/maps/sunstroke/map.yaml @@ -2377,23 +2377,23 @@ Actors: Actor679: srock05 Location: 144,-8 Owner: Neutral -# Actor680: lobrdg_r_nw -# Location: 152,-16 -# Owner: Neutral + Actor680: lobrdg_r_nw + Location: 152,-17 + Owner: Neutral Actor681: bridge2 Location: 140,-3 Owner: Neutral -# Actor682: lobrdg_b -# Location: 153,-16 -# Owner: Neutral -# Health: 50 + Actor682: lobrdg_b + Location: 153,-17 + Owner: Neutral + Health: 50 Actor683: srock04 Location: 145,-7 Owner: Neutral -# Actor684: lobrdg_b -# Location: 154,-16 -# Owner: Neutral -# Health: 25 + Actor684: lobrdg_b + Location: 154,-17 + Owner: Neutral + Health: 25 Actor685: crat01 Location: 115,24 Owner: Neutral @@ -2487,10 +2487,10 @@ Actors: Actor715: crat03 Location: 105,50 Owner: Neutral -# Actor716: lobrdg_r_se -# Location: 171,-16 -# Owner: Neutral -# Health: 50 + Actor716: lobrdg_r_se + Location: 171,-17 + Owner: Neutral + Health: 50 Actor717: trock05 Location: 189,-34 Owner: Neutral @@ -2564,6 +2564,15 @@ Actors: Owner: Creeps Location: 58,23 Facing: 92 + Actor741: tuntop04 + Owner: Neutral + Location: 183,-43 + Actor742: lobrdg_b_d + Owner: Neutral + Location: 155,-17 + Actor743: lobrdg_b_d + Owner: Neutral + Location: 170,-17 Rules: rules.yaml diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 02422a42fb..e5db3a514e 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -381,6 +381,8 @@ ReferencePoint: Bottom, Right RequiresCondition: hospitalheal RevealOnFire: + EntersTunnels: + Voice: Move ^RegularInfantryDeath: WithDeathAnimation@normal: @@ -565,6 +567,8 @@ Carryable: RequiresCondition: !inside-tunnel RevealOnFire: + EntersTunnels: + Voice: Move ^Tank: Inherits: ^Vehicle @@ -710,6 +714,8 @@ Guardable: WithFacingSpriteBody: RevealOnFire: + EntersTunnels: + Voice: Move ^BlossomTree: Inherits@1: ^SpriteActor @@ -870,9 +876,21 @@ TerrainType: Rail ^Tunnel: - Inherits: ^TerrainOverlay + RenderSprites: + Palette: terraindecoration + WithSpriteBody: + BodyOrientation: + UseClassicPerspectiveFudge: False + QuantizedFacings: 1 + Building: + Dimensions: 3, 3 + Footprint: ___ ___ ___ CustomSelectionSize: - CustomBounds: 220,220 + CustomBounds: 144, 144 + Targetable: + AlwaysVisible: + TunnelEntrance: + Dimensions: 3, 3 ^Gate: Inherits: ^Building diff --git a/mods/ts/rules/misc.yaml b/mods/ts/rules/misc.yaml index 45d14be609..3d72f4e558 100644 --- a/mods/ts/rules/misc.yaml +++ b/mods/ts/rules/misc.yaml @@ -213,12 +213,24 @@ TRACKS16: TUNTOP01: Inherits: ^Tunnel + TunnelEntrance: + RallyPoint: -3, 1 + Sensor: 0, 1 TUNTOP02: Inherits: ^Tunnel + TunnelEntrance: + RallyPoint: 1, -3 + Sensor: 1, 0 TUNTOP03: Inherits: ^Tunnel + TunnelEntrance: + RallyPoint: 3, 1 + Sensor: 0, 1 TUNTOP04: Inherits: ^Tunnel + TunnelEntrance: + RallyPoint: 1, 3 + Sensor: 1, 0 diff --git a/mods/ts/sequences/misc.yaml b/mods/ts/sequences/misc.yaml index 555bdaca70..4ad1901fc9 100644 --- a/mods/ts/sequences/misc.yaml +++ b/mods/ts/sequences/misc.yaml @@ -614,24 +614,28 @@ tracks16: ^tuntop: idle: - Offset: 0, -13, 42 ZRamp: 1 UseTilesetExtension: true - # Disabled to avoid glitches until shadow rendering is fixed - # Appears to be unused in the original game too - # ShadowStart: 1 tuntop01: Inherits: ^tuntop + idle: + Offset: 24, -49, 36 tuntop02: Inherits: ^tuntop + idle: + Offset: -24, -49, 36 tuntop03: Inherits: ^tuntop + idle: + Offset: 24, -49, 24 tuntop04: Inherits: ^tuntop + idle: + Offset: -24, -49, 24 dig: idle: