206 lines
5.1 KiB
C#
206 lines
5.1 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version. For more
|
|
* information, see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace OpenRA.Traits
|
|
{
|
|
public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
|
|
public struct Target
|
|
{
|
|
public static readonly Target[] None = { };
|
|
public static readonly Target Invalid = new Target { type = TargetType.Invalid };
|
|
|
|
TargetType type;
|
|
Actor actor;
|
|
FrozenActor frozen;
|
|
WPos pos;
|
|
CPos? cell;
|
|
int generation;
|
|
|
|
public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; }
|
|
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell)
|
|
{
|
|
return new Target { pos = w.Map.CenterOfSubCell(c, subCell), cell = c, type = TargetType.Terrain };
|
|
}
|
|
|
|
/// <summary>
|
|
/// DEPRECATED: Use Order.Target instead.
|
|
/// This method is kept to maintain compatibility with legacy code that may not understand TargetType.FrozenActor.
|
|
/// </summary>
|
|
public static Target FromOrder(World w, Order o)
|
|
{
|
|
return o.TargetActor != null
|
|
? FromActor(o.TargetActor)
|
|
: FromCell(w, o.TargetLocation);
|
|
}
|
|
|
|
public static Target FromActor(Actor a)
|
|
{
|
|
if (a == null)
|
|
return Invalid;
|
|
|
|
return new Target
|
|
{
|
|
actor = a,
|
|
type = TargetType.Actor,
|
|
generation = a.Generation,
|
|
};
|
|
}
|
|
|
|
public static Target FromFrozenActor(FrozenActor a) { return new Target { frozen = a, type = TargetType.FrozenActor }; }
|
|
|
|
public Actor Actor { get { return actor; } }
|
|
public FrozenActor FrozenActor { get { return frozen; } }
|
|
|
|
public TargetType Type
|
|
{
|
|
get
|
|
{
|
|
if (type == TargetType.Actor)
|
|
{
|
|
// Actor is no longer in the world
|
|
if (!actor.IsInWorld || actor.IsDead)
|
|
return TargetType.Invalid;
|
|
|
|
// Actor generation has changed (teleported or captured)
|
|
if (actor.Generation != generation)
|
|
return TargetType.Invalid;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
}
|
|
|
|
public bool IsValidFor(Actor targeter)
|
|
{
|
|
if (targeter == null || Type == TargetType.Invalid)
|
|
return false;
|
|
|
|
if (actor != null && !actor.IsTargetableBy(targeter))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Currently all or nothing.
|
|
// TODO: either replace based on target type or put in singleton trait
|
|
public bool RequiresForceFire
|
|
{
|
|
get
|
|
{
|
|
if (actor == null)
|
|
return false;
|
|
|
|
// PERF: Avoid LINQ.
|
|
var isTargetable = false;
|
|
foreach (var targetable in actor.Targetables)
|
|
{
|
|
if (!targetable.IsTraitEnabled())
|
|
continue;
|
|
|
|
isTargetable = true;
|
|
if (!targetable.RequiresForceFire)
|
|
return false;
|
|
}
|
|
|
|
return isTargetable;
|
|
}
|
|
}
|
|
|
|
// Representative position - see Positions for the full set of targetable positions.
|
|
public WPos CenterPosition
|
|
{
|
|
get
|
|
{
|
|
switch (Type)
|
|
{
|
|
case TargetType.Actor:
|
|
return actor.CenterPosition;
|
|
case TargetType.FrozenActor:
|
|
return frozen.CenterPosition;
|
|
case TargetType.Terrain:
|
|
return pos;
|
|
default:
|
|
case TargetType.Invalid:
|
|
throw new InvalidOperationException("Attempting to query the position of an invalid Target");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Positions available to target for range checks
|
|
static readonly WPos[] NoPositions = { };
|
|
public IEnumerable<WPos> Positions
|
|
{
|
|
get
|
|
{
|
|
switch (Type)
|
|
{
|
|
case TargetType.Actor:
|
|
if (!actor.Targetables.Any(Exts.IsTraitEnabled))
|
|
return new[] { actor.CenterPosition };
|
|
|
|
var targetablePositions = actor.TraitsImplementing<ITargetablePositions>().Where(Exts.IsTraitEnabled);
|
|
if (targetablePositions.Any())
|
|
{
|
|
var target = this;
|
|
return targetablePositions.SelectMany(tp => tp.TargetablePositions(target.actor));
|
|
}
|
|
|
|
return new[] { actor.CenterPosition };
|
|
case TargetType.FrozenActor:
|
|
return new[] { frozen.CenterPosition };
|
|
case TargetType.Terrain:
|
|
return new[] { pos };
|
|
default:
|
|
case TargetType.Invalid:
|
|
return NoPositions;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsInRange(WPos origin, WDist range)
|
|
{
|
|
if (Type == TargetType.Invalid)
|
|
return false;
|
|
|
|
// Target ranges are calculated in 2D, so ignore height differences
|
|
return Positions.Any(t => (t - origin).HorizontalLengthSquared <= range.LengthSquared);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
switch (Type)
|
|
{
|
|
case TargetType.Actor:
|
|
return actor.ToString();
|
|
|
|
case TargetType.FrozenActor:
|
|
return frozen.ToString();
|
|
|
|
case TargetType.Terrain:
|
|
return pos.ToString();
|
|
|
|
default:
|
|
case TargetType.Invalid:
|
|
return "Invalid";
|
|
}
|
|
}
|
|
|
|
// Expose internal state for serialization by the orders code *only*
|
|
internal TargetType SerializableType { get { return type; } }
|
|
internal Actor SerializableActor { get { return actor; } }
|
|
internal CPos? SerializableCell { get { return cell; } }
|
|
}
|
|
}
|