From 304e3ef9f9c0fca18913c8984ec57a28c26c95df Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 25 Mar 2017 17:19:38 +0000 Subject: [PATCH] Distinguish between line build nodes and segments. --- .../Orders/PlaceBuildingOrderGenerator.cs | 2 +- .../Traits/Buildings/BuildingUtils.cs | 15 ++-- .../Traits/Buildings/LineBuild.cs | 76 ++++++++++++++++++- .../Traits/Player/PlaceBuilding.cs | 33 +++++--- 4 files changed, 107 insertions(+), 19 deletions(-) diff --git a/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs b/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs index babeb42abf..41661f75c3 100644 --- a/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs @@ -178,7 +178,7 @@ namespace OpenRA.Mods.Common.Orders if (!Game.GetModifierKeys().HasModifier(Modifiers.Shift)) foreach (var t in BuildingUtils.GetLineBuildCells(world, topLeft, building, buildingInfo)) - cells.Add(t, buildingInfo.IsCloseEnoughToBase(world, world.LocalPlayer, building, t)); + cells.Add(t.First, buildingInfo.IsCloseEnoughToBase(world, world.LocalPlayer, building, t.First)); else cells.Add(topLeft, buildingInfo.IsCloseEnoughToBase(world, world.LocalPlayer, building, topLeft)); } diff --git a/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs b/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs index 64807449b1..8bae043e68 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; +using OpenRA.Primitives; namespace OpenRA.Mods.Common.Traits { @@ -48,18 +49,20 @@ namespace OpenRA.Mods.Common.Traits world.IsCellBuildable(t, building, toIgnore)); } - public static IEnumerable GetLineBuildCells(World world, CPos location, string name, BuildingInfo bi) + public static IEnumerable> GetLineBuildCells(World world, CPos location, string name, BuildingInfo bi) { var lbi = world.Map.Rules.Actors[name].TraitInfo(); var topLeft = location; // 1x1 assumption! if (world.IsCellBuildable(topLeft, bi)) - yield return topLeft; + yield return Pair.New(topLeft, null); // Start at place location, search outwards // TODO: First make it work, then make it nice var vecs = new[] { new CVec(1, 0), new CVec(0, 1), new CVec(-1, 0), new CVec(0, -1) }; int[] dirs = { 0, 0, 0, 0 }; + Actor[] connectors = { null, null, null, null }; + for (var d = 0; d < 4; d++) { for (var i = 1; i < lbi.Range; i++) @@ -72,17 +75,17 @@ namespace OpenRA.Mods.Common.Traits continue; // Cell is empty; continue search // Cell contains an actor. Is it the type we want? - var hasConnector = world.ActorMap.GetActorsAt(cell) - .Any(a => a.Info.TraitInfos() + connectors[d] = world.ActorMap.GetActorsAt(cell) + .FirstOrDefault(a => a.Info.TraitInfos() .Any(info => info.Types.Overlaps(lbi.NodeTypes) && info.Connections.Contains(vecs[d]))); - dirs[d] = hasConnector ? i : -1; + dirs[d] = connectors[d] != null ? i : -1; } // Place intermediate-line sections if (dirs[d] > 0) for (var i = 1; i < dirs[d]; i++) - yield return topLeft + i * vecs[d]; + yield return Pair.New(topLeft + i * vecs[d], connectors[d]); } } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs b/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs index 255f0a312b..a66fed98d5 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs @@ -10,19 +10,91 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { + public class LineBuildParentInit : IActorInit + { + [FieldFromYamlKey] public readonly string[] ParentNames = new string[0]; + readonly Actor[] parents = null; + + public LineBuildParentInit() { } + public LineBuildParentInit(Actor[] init) { parents = init; } + public Actor[] Value(World world) + { + if (parents != null) + return parents; + + var sma = world.WorldActor.Trait(); + return ParentNames.Select(n => sma.Actors[n]).ToArray(); + } + } + + public interface INotifyLineBuildSegmentsChanged + { + void SegmentAdded(Actor self, Actor segment); + void SegmentRemoved(Actor self, Actor segment); + } + [Desc("Place the second actor in line to build more of the same at once (used for walls).")] - public class LineBuildInfo : TraitInfo + public class LineBuildInfo : ITraitInfo { [Desc("The maximum allowed length of the line.")] public readonly int Range = 5; [Desc("LineBuildNode 'Types' to attach to.")] public readonly HashSet NodeTypes = new HashSet { "wall" }; + + [ActorReference(typeof(LineBuildInfo))] + [Desc("Actor type for line-built segments (defaults to same actor).")] + public readonly string SegmentType = null; + + public object Create(ActorInitializer init) { return new LineBuild(init, this); } } - public class LineBuild { } + public class LineBuild : INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyLineBuildSegmentsChanged + { + readonly Actor[] parentNodes = new Actor[0]; + HashSet segments; + + public LineBuild(ActorInitializer init, LineBuildInfo info) + { + if (init.Contains()) + parentNodes = init.Get().Value(init.World); + } + + void INotifyLineBuildSegmentsChanged.SegmentAdded(Actor self, Actor segment) + { + if (segments == null) + segments = new HashSet(); + + segments.Add(segment); + } + + void INotifyLineBuildSegmentsChanged.SegmentRemoved(Actor self, Actor segment) + { + if (segments == null) + return; + + segments.Remove(segment); + } + + void INotifyAddedToWorld.AddedToWorld(Actor self) + { + foreach (var parent in parentNodes) + if (!parent.Disposed) + foreach (var n in parent.TraitsImplementing()) + n.SegmentAdded(parent, self); + } + + void INotifyRemovedFromWorld.RemovedFromWorld(Actor self) + { + foreach (var parent in parentNodes) + if (!parent.Disposed) + foreach (var n in parent.TraitsImplementing()) + n.SegmentRemoved(parent, self); + } + } } diff --git a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs index a05b643d6a..57d36c582b 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs @@ -74,21 +74,34 @@ namespace OpenRA.Mods.Common.Traits if (os == "LineBuild") { - var playSounds = true; + // Build the parent actor first + var placed = w.CreateActor(order.TargetString, new TypeDictionary + { + new LocationInit(order.TargetLocation), + new OwnerInit(order.Player), + new FactionInit(faction), + }); + + foreach (var s in buildingInfo.BuildSounds) + Game.Sound.PlayToPlayer(SoundType.World, order.Player, s, placed.CenterPosition); + + // Build the connection segments + var segmentType = unit.TraitInfo().SegmentType; + if (string.IsNullOrEmpty(segmentType)) + segmentType = order.TargetString; + foreach (var t in BuildingUtils.GetLineBuildCells(w, order.TargetLocation, order.TargetString, buildingInfo)) { - var building = w.CreateActor(order.TargetString, new TypeDictionary + if (t.First == order.TargetLocation) + continue; + + w.CreateActor(t.First == order.TargetLocation ? order.TargetString : segmentType, new TypeDictionary { - new LocationInit(t), + new LocationInit(t.First), new OwnerInit(order.Player), - new FactionInit(faction) + new FactionInit(faction), + new LineBuildParentInit(new[] { t.Second, placed }) }); - - if (playSounds) - foreach (var s in buildingInfo.BuildSounds) - Game.Sound.PlayToPlayer(SoundType.World, order.Player, s, building.CenterPosition); - - playSounds = false; } } else if (os == "PlacePlug")