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; 13 using System.Collections.Generic; 14 using System.Linq; 15 16 namespace OpenRA.Traits 17 { 18 public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor } 19 public struct Target 20 { 21 public static readonly Target[] None = { }; 22 public static readonly Target Invalid = default(Target); 23 24 readonly TargetType type; 25 readonly Actor actor; 26 readonly FrozenActor frozen; 27 readonly WPos terrainCenterPosition; 28 readonly WPos[] terrainPositions; 29 readonly CPos? cell; 30 readonly SubCell? subCell; 31 readonly int generation; 32 TargetOpenRA.Traits.Target33 Target(WPos terrainCenterPosition, WPos[] terrainPositions = null) 34 { 35 type = TargetType.Terrain; 36 this.terrainCenterPosition = terrainCenterPosition; 37 this.terrainPositions = terrainPositions ?? new[] { terrainCenterPosition }; 38 39 actor = null; 40 frozen = null; 41 cell = null; 42 subCell = null; 43 generation = 0; 44 } 45 TargetOpenRA.Traits.Target46 Target(World w, CPos c, SubCell subCell) 47 { 48 type = TargetType.Terrain; 49 terrainCenterPosition = w.Map.CenterOfSubCell(c, subCell); 50 terrainPositions = new[] { terrainCenterPosition }; 51 cell = c; 52 this.subCell = subCell; 53 54 actor = null; 55 frozen = null; 56 generation = 0; 57 } 58 TargetOpenRA.Traits.Target59 Target(Actor a) 60 { 61 type = TargetType.Actor; 62 actor = a; 63 generation = a.Generation; 64 65 terrainCenterPosition = WPos.Zero; 66 terrainPositions = null; 67 frozen = null; 68 cell = null; 69 subCell = null; 70 } 71 TargetOpenRA.Traits.Target72 Target(FrozenActor fa) 73 { 74 type = TargetType.FrozenActor; 75 frozen = fa; 76 77 terrainCenterPosition = WPos.Zero; 78 terrainPositions = null; 79 actor = null; 80 cell = null; 81 subCell = null; 82 generation = 0; 83 } 84 FromPosOpenRA.Traits.Target85 public static Target FromPos(WPos p) { return new Target(p); } FromTargetPositionsOpenRA.Traits.Target86 public static Target FromTargetPositions(Target t) { return new Target(t.CenterPosition, t.Positions.ToArray()); } FromCellOpenRA.Traits.Target87 public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell) { return new Target(w, c, subCell); } FromActorOpenRA.Traits.Target88 public static Target FromActor(Actor a) { return a != null ? new Target(a) : Invalid; } FromFrozenActorOpenRA.Traits.Target89 public static Target FromFrozenActor(FrozenActor fa) { return new Target(fa); } 90 91 public Actor Actor { get { return actor; } } 92 public FrozenActor FrozenActor { get { return frozen; } } 93 94 public TargetType Type 95 { 96 get 97 { 98 if (type == TargetType.Actor) 99 { 100 // Actor is no longer in the world 101 if (!actor.IsInWorld || actor.IsDead) 102 return TargetType.Invalid; 103 104 // Actor generation has changed (teleported or captured) 105 if (actor.Generation != generation) 106 return TargetType.Invalid; 107 } 108 109 return type; 110 } 111 } 112 IsValidForOpenRA.Traits.Target113 public bool IsValidFor(Actor targeter) 114 { 115 if (targeter == null) 116 return false; 117 118 switch (Type) 119 { 120 case TargetType.Actor: 121 return actor.IsTargetableBy(targeter); 122 case TargetType.FrozenActor: 123 return frozen.IsValid && frozen.Visible && !frozen.Hidden; 124 case TargetType.Invalid: 125 return false; 126 default: 127 case TargetType.Terrain: 128 return true; 129 } 130 } 131 132 // Currently all or nothing. 133 // TODO: either replace based on target type or put in singleton trait 134 public bool RequiresForceFire 135 { 136 get 137 { 138 if (actor == null) 139 return false; 140 141 // PERF: Avoid LINQ. 142 var isTargetable = false; 143 foreach (var targetable in actor.Targetables) 144 { 145 if (!targetable.IsTraitEnabled()) 146 continue; 147 148 isTargetable = true; 149 if (!targetable.RequiresForceFire) 150 return false; 151 } 152 153 return isTargetable; 154 } 155 } 156 157 // Representative position - see Positions for the full set of targetable positions. 158 public WPos CenterPosition 159 { 160 get 161 { 162 switch (Type) 163 { 164 case TargetType.Actor: 165 return actor.CenterPosition; 166 case TargetType.FrozenActor: 167 return frozen.CenterPosition; 168 case TargetType.Terrain: 169 return terrainCenterPosition; 170 default: 171 case TargetType.Invalid: 172 throw new InvalidOperationException("Attempting to query the position of an invalid Target"); 173 } 174 } 175 } 176 177 // Positions available to target for range checks 178 static readonly WPos[] NoPositions = { }; 179 public IEnumerable<WPos> Positions 180 { 181 get 182 { 183 switch (Type) 184 { 185 case TargetType.Actor: 186 return actor.GetTargetablePositions(); 187 case TargetType.FrozenActor: 188 // TargetablePositions may be null if it is Invalid 189 return frozen.TargetablePositions ?? NoPositions; 190 case TargetType.Terrain: 191 return terrainPositions; 192 default: 193 case TargetType.Invalid: 194 return NoPositions; 195 } 196 } 197 } 198 IsInRangeOpenRA.Traits.Target199 public bool IsInRange(WPos origin, WDist range) 200 { 201 if (Type == TargetType.Invalid) 202 return false; 203 204 // Target ranges are calculated in 2D, so ignore height differences 205 return Positions.Any(t => (t - origin).HorizontalLengthSquared <= range.LengthSquared); 206 } 207 ToStringOpenRA.Traits.Target208 public override string ToString() 209 { 210 switch (Type) 211 { 212 case TargetType.Actor: 213 return actor.ToString(); 214 215 case TargetType.FrozenActor: 216 return frozen.ToString(); 217 218 case TargetType.Terrain: 219 return terrainCenterPosition.ToString(); 220 221 default: 222 case TargetType.Invalid: 223 return "Invalid"; 224 } 225 } 226 227 // Expose internal state for serialization by the orders code *only* 228 internal TargetType SerializableType { get { return type; } } 229 internal Actor SerializableActor { get { return actor; } } 230 internal CPos? SerializableCell { get { return cell; } } 231 internal SubCell? SerializableSubCell { get { return subCell; } } 232 internal WPos SerializablePos { get { return terrainCenterPosition; } } 233 } 234 } 235