diff --git a/OpenRA.Game/Activities/Activity.cs b/OpenRA.Game/Activities/Activity.cs index 1e6245267c..1f1bcab23b 100644 --- a/OpenRA.Game/Activities/Activity.cs +++ b/OpenRA.Game/Activities/Activity.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -23,13 +24,15 @@ namespace OpenRA.Activities { public readonly Target Target; public readonly Color Color; + public readonly Sprite Tile; - public TargetLineNode(Target target, Color color) + public TargetLineNode(Target target, Color color, Sprite tile = null) { // Note: Not all activities are drawable. In that case, pass Target.Invalid as target, // if "yield break" in TargetLineNode(Actor self) is not feasible. Target = target; Color = color; + Tile = tile; } } diff --git a/OpenRA.Mods.Cnc/Activities/LayMines.cs b/OpenRA.Mods.Cnc/Activities/LayMines.cs index 62b93684c7..f1c84c8350 100644 --- a/OpenRA.Mods.Cnc/Activities/LayMines.cs +++ b/OpenRA.Mods.Cnc/Activities/LayMines.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Generic; using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Cnc.Traits; @@ -22,30 +23,43 @@ namespace OpenRA.Mods.Cnc.Activities // Assumes you have Minelayer on that unit public class LayMines : Activity { - readonly MinelayerInfo info; + readonly Minelayer minelayer; readonly AmmoPool[] ammoPools; readonly IMove movement; readonly RearmableInfo rearmableInfo; - readonly CPos[] minefield; + + List minefield; + bool returnToBase; + Actor rearmTarget; public LayMines(Actor self, CPos[] minefield) { - info = self.Info.TraitInfo(); + minelayer = self.Trait(); ammoPools = self.TraitsImplementing().ToArray(); movement = self.Trait(); rearmableInfo = self.Info.TraitInfoOrDefault(); - this.minefield = minefield; + + if (minefield != null) + this.minefield = minefield.ToList(); + } + + protected override void OnFirstRun(Actor self) + { + if (minefield == null) + minefield = new CPos[] { self.Location }.ToList(); } public override bool Tick(Actor self) { + returnToBase = false; + if (IsCanceling) return true; - if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo())) + if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == minelayer.Info.AmmoPoolName && !p.HasAmmo())) { // Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more - var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally + rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally && rearmableInfo.RearmActors.Contains(a.Info.Name)) .ClosestTo(self); @@ -56,6 +70,7 @@ namespace OpenRA.Mods.Cnc.Activities QueueChild(new MoveAdjacentTo(self, Target.FromActor(rearmTarget))); QueueChild(movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget)); QueueChild(new Resupply(self, rearmTarget, new WDist(512))); + returnToBase = true; return false; } @@ -63,10 +78,11 @@ namespace OpenRA.Mods.Cnc.Activities { LayMine(self); QueueChild(new Wait(20)); // A little wait after placing each mine, for show + minefield.Remove(self.Location); return false; } - if (minefield != null && minefield.Length > 0) + if (minefield != null && minefield.Count > 0) { // Don't get stuck forever here for (var n = 0; n < 20; n++) @@ -84,6 +100,20 @@ namespace OpenRA.Mods.Cnc.Activities return true; } + public override IEnumerable TargetLineNodes(Actor self) + { + if (returnToBase) + yield return new TargetLineNode(Target.FromActor(rearmTarget), Color.Green); + + if (minefield == null || minefield.Count == 0) + yield break; + + yield return new TargetLineNode(Target.FromCell(self.World, minefield[0]), Color.Crimson); + + foreach (var c in minefield) + yield return new TargetLineNode(Target.FromCell(self.World, c), Color.Crimson, tile: minelayer.Tile); + } + static bool ShouldLayMine(Actor self, CPos p) { // If there is no unit (other than me) here, we want to place a mine here @@ -94,14 +124,14 @@ namespace OpenRA.Mods.Cnc.Activities { if (ammoPools != null) { - var pool = ammoPools.FirstOrDefault(x => x.Info.Name == info.AmmoPoolName); + var pool = ammoPools.FirstOrDefault(x => x.Info.Name == minelayer.Info.AmmoPoolName); if (pool == null) return; pool.TakeAmmo(self, 1); } self.World.AddFrameEndTask( - w => w.CreateActor(info.Mine, new TypeDictionary + w => w.CreateActor(minelayer.Info.Mine, new TypeDictionary { new LocationInit(self.Location), new OwnerInit(self.Owner), diff --git a/OpenRA.Mods.Cnc/Traits/Minelayer.cs b/OpenRA.Mods.Cnc/Traits/Minelayer.cs index 4cba1fdc46..f112b60381 100644 --- a/OpenRA.Mods.Cnc/Traits/Minelayer.cs +++ b/OpenRA.Mods.Cnc/Traits/Minelayer.cs @@ -37,27 +37,27 @@ namespace OpenRA.Mods.Cnc.Traits public object Create(ActorInitializer init) { return new Minelayer(init.Self, this); } } - public class Minelayer : IIssueOrder, IResolveOrder, IRenderAboveShroudWhenSelected, ISync, IIssueDeployOrder, IOrderVoice + public class Minelayer : IIssueOrder, IResolveOrder, ISync, IIssueDeployOrder, IOrderVoice { - readonly MinelayerInfo info; + public readonly MinelayerInfo Info; // TODO: [Sync] when sync can cope with arrays! public CPos[] Minefield = null; - readonly Sprite tile; + public readonly Sprite Tile; [Sync] CPos minefieldStart; public Minelayer(Actor self, MinelayerInfo info) { - this.info = info; + Info = info; var tileset = self.World.Map.Tileset.ToLowerInvariant(); if (self.World.Map.Rules.Sequences.HasSequence("overlay", "build-valid-{0}".F(tileset))) - tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0); + Tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0); else - tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid").GetSprite(0); + Tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid").GetSprite(0); } IEnumerable IIssueOrder.Orders @@ -109,19 +109,18 @@ namespace OpenRA.Mods.Cnc.Traits { var movement = self.Trait(); - Minefield = GetMinefieldCells(minefieldStart, cell, info.MinefieldDepth) + Minefield = GetMinefieldCells(minefieldStart, cell, Info.MinefieldDepth) .Where(p => movement.CanEnterCell(p, null, false)).ToArray(); self.QueueActivity(order.Queued, new LayMines(self, Minefield)); - if (Minefield.Length == 1 && Minefield[0] != self.Location) - self.ShowTargetLines(); + self.ShowTargetLines(); } } string IOrderVoice.VoicePhraseForOrder(Actor self, Order order) { if (order.OrderString == "PlaceMine" || order.OrderString == "PlaceMinefield") - return info.Voice; + return Info.Voice; return null; } @@ -145,23 +144,6 @@ namespace OpenRA.Mods.Cnc.Traits yield return new CPos(i, j); } - IEnumerable IRenderAboveShroudWhenSelected.RenderAboveShroud(Actor self, WorldRenderer wr) - { - if (self.Owner != self.World.LocalPlayer || Minefield == null) - yield break; - - // Single-cell mine fields use a target line instead - if (Minefield.Length == 1) - yield break; - - var pal = wr.Palette(TileSet.TerrainPaletteInternalName); - foreach (var c in Minefield) - yield return new SpriteRenderable(tile, self.World.Map.CenterOfCell(c), - WVec.Zero, -511, pal, 1f, true); - } - - bool IRenderAboveShroudWhenSelected.SpatiallyPartitionable { get { return false; } } - class MinefieldOrderGenerator : OrderGenerator { readonly List minelayers; diff --git a/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs b/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs index ffbf93bb90..05054167b3 100644 --- a/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs +++ b/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs @@ -81,8 +81,16 @@ namespace OpenRA.Mods.Common.Traits { if (n.Target.Type != TargetType.Invalid) { - yield return new TargetLineRenderable(new[] { prev, n.Target.CenterPosition }, n.Color, info.LineWidth, info.MarkerWidth); - prev = n.Target.CenterPosition; + var pal = wr.Palette(TileSet.TerrainPaletteInternalName); + var tile = n.Tile; + var pos = n.Target.CenterPosition; + + if (tile == null) + yield return new TargetLineRenderable(new[] { prev, pos }, n.Color, info.LineWidth, info.MarkerWidth); + else + yield return new SpriteRenderable(tile, pos, WVec.Zero, -511, pal, 1f, true); + + prev = pos; } } }