From 7a29cfeaea9598047384ff894836dfc3cde35098 Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Thu, 4 Feb 2016 21:41:32 +0100 Subject: [PATCH 1/6] Add optional use of a rally point to ProductionFromMapEdge --- .../Traits/Buildings/ProductionFromMapEdge.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs index 6cef911902..4f296b79e9 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs @@ -22,11 +22,18 @@ namespace OpenRA.Mods.D2k.Traits public override object Create(ActorInitializer init) { return new ProductionFromMapEdge(init, this); } } - class ProductionFromMapEdge : Production + class ProductionFromMapEdge : Production, INotifyCreated { + RallyPoint rp; + public ProductionFromMapEdge(ActorInitializer init, ProductionInfo info) : base(init, info) { } + void INotifyCreated.Created(Actor self) + { + rp = self.TraitOrDefault(); + } + public override bool Produce(Actor self, ActorInfo producee, string factionVariant) { var location = self.World.Map.ChooseClosestEdgeCell(self.Location); @@ -37,7 +44,9 @@ namespace OpenRA.Mods.D2k.Traits if (aircraftInfo != null) pos += new WVec(0, 0, aircraftInfo.CruiseAltitude.Length); - var initialFacing = self.World.Map.FacingBetween(location, self.Location, 0); + var destination = rp != null ? rp.Location : self.Location; + + var initialFacing = self.World.Map.FacingBetween(location, destination, 0); self.World.AddFrameEndTask(w => { @@ -56,13 +65,13 @@ namespace OpenRA.Mods.D2k.Traits var move = newUnit.TraitOrDefault(); if (move != null) - newUnit.QueueActivity(move.MoveIntoWorld(newUnit, self.Location)); + newUnit.QueueActivity(move.MoveIntoWorld(newUnit, destination)); - newUnit.SetTargetLine(Target.FromCell(self.World, self.Location), Color.Green, false); + newUnit.SetTargetLine(Target.FromCell(self.World, destination), Color.Green, false); if (!self.IsDead) foreach (var t in self.TraitsImplementing()) - t.UnitProduced(self, newUnit, self.Location); + t.UnitProduced(self, newUnit, destination); var notifyOthers = self.World.ActorsWithTrait(); foreach (var notify in notifyOthers) From a1e6f65f8ff51cd1ac99312641ac6d5a063b4884 Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Sun, 7 Feb 2016 22:00:37 +0100 Subject: [PATCH 2/6] Use a "real" Move instead of VisualMove in ProductionFromMapEdge --- OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs index 4f296b79e9..9a0bb7cf08 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs @@ -65,7 +65,7 @@ namespace OpenRA.Mods.D2k.Traits var move = newUnit.TraitOrDefault(); if (move != null) - newUnit.QueueActivity(move.MoveIntoWorld(newUnit, destination)); + newUnit.QueueActivity(move.MoveTo(destination, 2)); newUnit.SetTargetLine(Target.FromCell(self.World, destination), Color.Green, false); From f547a4b6e9f358a6a5b4e4906fd28a67d984e401 Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Sun, 28 Feb 2016 14:15:29 +0100 Subject: [PATCH 3/6] Add ProductionSpawnLocationInit, used by ProductionFromMapEdge This enables fixed per-factory actor spawn locations. --- .../Traits/Buildings/ProductionFromMapEdge.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs index 9a0bb7cf08..9905a40e0b 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs @@ -10,6 +10,7 @@ #endregion using System.Drawing; +using OpenRA; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; using OpenRA.Traits; @@ -17,17 +18,22 @@ using OpenRA.Traits; namespace OpenRA.Mods.D2k.Traits { [Desc("Produce a unit on the closest map edge cell and move into the world.")] - class ProductionFromMapEdgeInfo : ProductionInfo + class ProductionFromMapEdgeInfo : ProductionInfo, UsesInit { public override object Create(ActorInitializer init) { return new ProductionFromMapEdge(init, this); } } class ProductionFromMapEdge : Production, INotifyCreated { + readonly CPos? spawnLocation; RallyPoint rp; public ProductionFromMapEdge(ActorInitializer init, ProductionInfo info) - : base(init, info) { } + : base(init, info) + { + if (init.Contains()) + spawnLocation = init.Get(); + } void INotifyCreated.Created(Actor self) { @@ -36,7 +42,8 @@ namespace OpenRA.Mods.D2k.Traits public override bool Produce(Actor self, ActorInfo producee, string factionVariant) { - var location = self.World.Map.ChooseClosestEdgeCell(self.Location); + var location = spawnLocation.HasValue ? spawnLocation.Value : self.World.Map.ChooseClosestEdgeCell(self.Location); + var pos = self.World.Map.CenterOfCell(location); // If aircraft, spawn at cruise altitude @@ -84,4 +91,12 @@ namespace OpenRA.Mods.D2k.Traits return true; } } + + public class ProductionSpawnLocationInit : IActorInit + { + [FieldFromYamlKey] readonly CPos value = CPos.Zero; + public ProductionSpawnLocationInit() { } + public ProductionSpawnLocationInit(CPos init) { value = init; } + public CPos Value(World world) { return value; } + } } From be3bd524742a9cca58e62332f72bb64e3f151703 Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Sat, 27 Feb 2016 23:21:27 +0100 Subject: [PATCH 4/6] Add a method returning the closest edge cell matching a condition to Map --- OpenRA.Game/Map/Map.cs | 51 ++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 33518de4ff..43b12ab426 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -250,6 +250,7 @@ namespace OpenRA [FieldLoader.Ignore] public ProjectedCellRegion ProjectedCellBounds; [FieldLoader.Ignore] public CellRegion AllCells; + public List AllEdgeCells { get; private set; } void AssertExists(string filename) { @@ -392,6 +393,8 @@ namespace OpenRA CustomTerrain = new CellLayer(this); foreach (var uv in AllCells.MapCoords) CustomTerrain[uv] = byte.MaxValue; + + AllEdgeCells = UpdateEdgeCells(); } void InitializeCellProjection() @@ -1056,20 +1059,44 @@ namespace OpenRA return edge.V == Bounds.Bottom ? unProjected.MaxBy(x => x.V) : unProjected.MinBy(x => x.V); } + public CPos ChooseClosestMatchingEdgeCell(CPos cell, Func match) + { + return AllEdgeCells.OrderBy(c => (cell - c).Length).FirstOrDefault(c => match(c)); + } + + List UpdateEdgeCells() + { + var edgeCells = new List(); + var unProjected = new List(); + var bottom = Bounds.Bottom - 1; + for (var u = Bounds.Left; u < Bounds.Right; u++) + { + unProjected = Unproject(new PPos(u, Bounds.Top)); + if (unProjected.Any()) + edgeCells.Add(unProjected.MinBy(x => x.V).ToCPos(Grid.Type)); + + unProjected = Unproject(new PPos(u, bottom)); + if (unProjected.Any()) + edgeCells.Add(unProjected.MaxBy(x => x.V).ToCPos(Grid.Type)); + } + + for (var v = Bounds.Top; v < Bounds.Bottom; v++) + { + unProjected = Unproject(new PPos(Bounds.Left, v)); + if (unProjected.Any()) + edgeCells.Add((v == bottom ? unProjected.MaxBy(x => x.V) : unProjected.MinBy(x => x.V)).ToCPos(Grid.Type)); + + unProjected = Unproject(new PPos(Bounds.Right - 1, v)); + if (unProjected.Any()) + edgeCells.Add((v == bottom ? unProjected.MaxBy(x => x.V) : unProjected.MinBy(x => x.V)).ToCPos(Grid.Type)); + } + + return edgeCells; + } + public CPos ChooseRandomEdgeCell(MersenneTwister rand) { - List cells; - do - { - var isU = rand.Next(2) == 0; - var edge = rand.Next(2) == 0; - var u = isU ? rand.Next(Bounds.Left, Bounds.Right) : (edge ? Bounds.Left : Bounds.Right); - var v = !isU ? rand.Next(Bounds.Top, Bounds.Bottom) : (edge ? Bounds.Top : Bounds.Bottom); - - cells = Unproject(new PPos(u, v)); - } while (!cells.Any()); - - return cells.Random(rand).ToCPos(Grid.Type); + return AllEdgeCells.Random(rand); } public WDist DistanceToEdge(WPos pos, WVec dir) From df7ec2b02905f46c1121adc6b33216e110203504 Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Sun, 28 Feb 2016 14:13:39 +0100 Subject: [PATCH 5/6] Fix ProductionFromMapEdge spawning ground units in inappropriate places --- .../Traits/Buildings/ProductionFromMapEdge.cs | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs index 9905a40e0b..7b31482046 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs @@ -26,11 +26,13 @@ namespace OpenRA.Mods.D2k.Traits class ProductionFromMapEdge : Production, INotifyCreated { readonly CPos? spawnLocation; + readonly DomainIndex domainIndex; RallyPoint rp; public ProductionFromMapEdge(ActorInitializer init, ProductionInfo info) : base(init, info) { + domainIndex = init.Self.World.WorldActor.Trait(); if (init.Contains()) spawnLocation = init.Get(); } @@ -42,25 +44,41 @@ namespace OpenRA.Mods.D2k.Traits public override bool Produce(Actor self, ActorInfo producee, string factionVariant) { - var location = spawnLocation.HasValue ? spawnLocation.Value : self.World.Map.ChooseClosestEdgeCell(self.Location); + var aircraftInfo = producee.TraitInfoOrDefault(); + var mobileInfo = producee.TraitInfoOrDefault(); - var pos = self.World.Map.CenterOfCell(location); + var passable = mobileInfo != null ? (uint)mobileInfo.GetMovementClass(self.World.TileSet) : 0; + var destination = rp != null ? rp.Location : self.Location; + + var location = spawnLocation; + if (!location.HasValue) + { + if (aircraftInfo != null) + location = self.World.Map.ChooseClosestEdgeCell(self.Location); + + if (mobileInfo != null) + location = self.World.Map.ChooseClosestMatchingEdgeCell(self.Location, + c => mobileInfo.CanEnterCell(self.World, null, c) && domainIndex.IsPassable(c, destination, passable)); + } + + // No suitable spawn location could be found, so production has failed. + if (!location.HasValue) + return false; + + var pos = self.World.Map.CenterOfCell(location.Value); // If aircraft, spawn at cruise altitude - var aircraftInfo = producee.TraitInfoOrDefault(); if (aircraftInfo != null) pos += new WVec(0, 0, aircraftInfo.CruiseAltitude.Length); - var destination = rp != null ? rp.Location : self.Location; - - var initialFacing = self.World.Map.FacingBetween(location, destination, 0); + var initialFacing = self.World.Map.FacingBetween(location.Value, destination, 0); self.World.AddFrameEndTask(w => { var td = new TypeDictionary { new OwnerInit(self.Owner), - new LocationInit(location), + new LocationInit(location.Value), new CenterPositionInit(pos), new FacingInit(initialFacing) }; From 0039cbc590feb7dc7ac65849a3ae75e5a1b0287e Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Sun, 28 Feb 2016 18:22:54 +0100 Subject: [PATCH 6/6] Fix indentation in ProductionFromMapEdge --- .../Traits/Buildings/ProductionFromMapEdge.cs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs index 7b31482046..d86dabcd85 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/ProductionFromMapEdge.cs @@ -74,37 +74,37 @@ namespace OpenRA.Mods.D2k.Traits var initialFacing = self.World.Map.FacingBetween(location.Value, destination, 0); self.World.AddFrameEndTask(w => + { + var td = new TypeDictionary { - var td = new TypeDictionary - { - new OwnerInit(self.Owner), - new LocationInit(location.Value), - new CenterPositionInit(pos), - new FacingInit(initialFacing) - }; + new OwnerInit(self.Owner), + new LocationInit(location.Value), + new CenterPositionInit(pos), + new FacingInit(initialFacing) + }; - if (factionVariant != null) - td.Add(new FactionInit(factionVariant)); + if (factionVariant != null) + td.Add(new FactionInit(factionVariant)); - var newUnit = self.World.CreateActor(producee.Name, td); + var newUnit = self.World.CreateActor(producee.Name, td); - var move = newUnit.TraitOrDefault(); - if (move != null) - newUnit.QueueActivity(move.MoveTo(destination, 2)); + var move = newUnit.TraitOrDefault(); + if (move != null) + newUnit.QueueActivity(move.MoveTo(destination, 2)); - newUnit.SetTargetLine(Target.FromCell(self.World, destination), Color.Green, false); + newUnit.SetTargetLine(Target.FromCell(self.World, destination), Color.Green, false); - if (!self.IsDead) - foreach (var t in self.TraitsImplementing()) - t.UnitProduced(self, newUnit, destination); + if (!self.IsDead) + foreach (var t in self.TraitsImplementing()) + t.UnitProduced(self, newUnit, destination); - var notifyOthers = self.World.ActorsWithTrait(); - foreach (var notify in notifyOthers) - notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit); + var notifyOthers = self.World.ActorsWithTrait(); + foreach (var notify in notifyOthers) + notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit); - foreach (var t in newUnit.TraitsImplementing()) - t.BuildingComplete(newUnit); - }); + foreach (var t in newUnit.TraitsImplementing()) + t.BuildingComplete(newUnit); + }); return true; }