1 #region Copyright & License Information 2 /* 3 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) 4 * This file is part of OpenRA, which is free software. It is made 5 * available to you under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, either version 3 of 7 * the License, or (at your option) any later version. For more 8 * information, see COPYING. 9 */ 10 #endregion 11 12 using System.Collections.Generic; 13 using System.Linq; 14 using OpenRA.Graphics; 15 using OpenRA.Mods.Common.Orders; 16 using OpenRA.Traits; 17 18 namespace OpenRA.Mods.Common.Traits 19 { 20 [Desc("Renders target lines between order waypoints.")] 21 public class DrawLineToTargetInfo : ITraitInfo 22 { 23 [Desc("Delay (in ticks) before the target lines disappear.")] 24 public readonly int Delay = 60; 25 26 [Desc("Width (in pixels) of the target lines.")] 27 public readonly int LineWidth = 1; 28 29 [Desc("Width (in pixels) of the queued target lines.")] 30 public readonly int QueuedLineWidth = 1; 31 32 [Desc("Width (in pixels) of the end node markers.")] 33 public readonly int MarkerWidth = 2; 34 35 [Desc("Width (in pixels) of the queued end node markers.")] 36 public readonly int QueuedMarkerWidth = 2; 37 Create(ActorInitializer init)38 public virtual object Create(ActorInitializer init) { return new DrawLineToTarget(init.Self, this); } 39 } 40 41 public class DrawLineToTarget : IRenderAboveShroud, IRenderAnnotationsWhenSelected, INotifySelected 42 { 43 readonly DrawLineToTargetInfo info; 44 readonly List<IRenderable> renderableCache = new List<IRenderable>(); 45 int lifetime; 46 DrawLineToTarget(Actor self, DrawLineToTargetInfo info)47 public DrawLineToTarget(Actor self, DrawLineToTargetInfo info) 48 { 49 this.info = info; 50 } 51 ShowTargetLines(Actor self)52 public void ShowTargetLines(Actor self) 53 { 54 if (Game.Settings.Game.TargetLines < TargetLinesType.Automatic || self.IsIdle) 55 return; 56 57 // Reset the order line timeout. 58 lifetime = info.Delay; 59 } 60 INotifySelected.Selected(Actor self)61 void INotifySelected.Selected(Actor self) 62 { 63 ShowTargetLines(self); 64 } 65 IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr)66 IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr) 67 { 68 if (!self.Owner.IsAlliedWith(self.World.LocalPlayer) || Game.Settings.Game.TargetLines == TargetLinesType.Disabled) 69 yield break; 70 71 // Players want to see the lines when in waypoint mode. 72 var force = Game.GetModifierKeys().HasModifier(Modifiers.Shift) || self.World.OrderGenerator is ForceModifiersOrderGenerator; 73 74 if (--lifetime <= 0 && !force) 75 yield break; 76 77 var pal = wr.Palette(TileSet.TerrainPaletteInternalName); 78 var a = self.CurrentActivity; 79 for (; a != null; a = a.NextActivity) 80 if (!a.IsCanceling) 81 foreach (var n in a.TargetLineNodes(self)) 82 if (n.Tile != null && n.Target.Type != TargetType.Invalid) 83 yield return new SpriteRenderable(n.Tile, n.Target.CenterPosition, WVec.Zero, -511, pal, 1f, true); 84 } 85 86 bool IRenderAboveShroud.SpatiallyPartitionable { get { return false; } } 87 IRenderAnnotationsWhenSelected.RenderAnnotations(Actor self, WorldRenderer wr)88 IEnumerable<IRenderable> IRenderAnnotationsWhenSelected.RenderAnnotations(Actor self, WorldRenderer wr) 89 { 90 if (!self.Owner.IsAlliedWith(self.World.LocalPlayer) || Game.Settings.Game.TargetLines == TargetLinesType.Disabled) 91 return Enumerable.Empty<IRenderable>(); 92 93 // Players want to see the lines when in waypoint mode. 94 var force = Game.GetModifierKeys().HasModifier(Modifiers.Shift) || self.World.OrderGenerator is ForceModifiersOrderGenerator; 95 96 if (--lifetime <= 0 && !force) 97 return Enumerable.Empty<IRenderable>(); 98 99 renderableCache.Clear(); 100 var prev = self.CenterPosition; 101 var a = self.CurrentActivity; 102 for (; a != null; a = a.NextActivity) 103 { 104 if (a.IsCanceling) 105 continue; 106 107 foreach (var n in a.TargetLineNodes(self)) 108 { 109 if (n.Target.Type != TargetType.Invalid && n.Tile == null) 110 { 111 var lineWidth = renderableCache.Any() ? info.QueuedLineWidth : info.LineWidth; 112 var markerWidth = renderableCache.Any() ? info.QueuedMarkerWidth : info.MarkerWidth; 113 114 var pos = n.Target.CenterPosition; 115 renderableCache.Add(new TargetLineRenderable(new[] { prev, pos }, n.Color, lineWidth, markerWidth)); 116 prev = pos; 117 } 118 } 119 } 120 121 // Reverse draw order so target markers are drawn on top of the next line 122 renderableCache.Reverse(); 123 return renderableCache; 124 } 125 126 bool IRenderAnnotationsWhenSelected.SpatiallyPartitionable { get { return false; } } 127 } 128 129 public static class LineTargetExts 130 { ShowTargetLines(this Actor self)131 public static void ShowTargetLines(this Actor self) 132 { 133 // Target lines are only automatically shown for the owning player 134 // Spectators and allies must use the force-display modifier 135 if (self.Owner != self.World.LocalPlayer) 136 return; 137 138 // Draw after frame end so that all the queueing of activities are done before drawing. 139 var line = self.TraitOrDefault<DrawLineToTarget>(); 140 if (line != null) 141 self.World.AddFrameEndTask(w => line.ShowTargetLines(self)); 142 } 143 } 144 } 145