Files
OpenRA/OpenRA.Mods.Common/Traits/HitShape.cs
RoosterDragon 8d3cec5bea When a render method has nothing to render, eagerly return.
By eagerly returning an empty enumerable in these cases, this avoids allocating an enumerable for the whole render method if nothing will be drawn.
2020-10-17 23:48:48 +02:00

140 lines
4.5 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2020 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.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.HitShapes;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Shape of actor for targeting and damage calculations.")]
public class HitShapeInfo : ConditionalTraitInfo, Requires<BodyOrientationInfo>
{
[Desc("Name of turret this shape is linked to. Leave empty to link shape to body.")]
public readonly string Turret = null;
[Desc("Create a targetable position for each offset listed here (relative to CenterPosition).")]
public readonly WVec[] TargetableOffsets = { WVec.Zero };
[Desc("Create a targetable position at the center of each occupied cell. Stacks with TargetableOffsets.")]
public readonly bool UseTargetableCellsOffsets = false;
[Desc("Defines which Armor types apply when the actor receives damage to this HitShape.",
"If none specified, all armor types the actor has are valid.")]
public readonly BitSet<ArmorType> ArmorTypes = default(BitSet<ArmorType>);
[FieldLoader.LoadUsing("LoadShape")]
[Desc("Engine comes with support for `Circle`, `Capsule`, `Polygon` and `Rectangle`. Defaults to `Circle` when left empty.")]
public readonly IHitShape Type;
static object LoadShape(MiniYaml yaml)
{
IHitShape ret;
var shapeNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Type");
var shape = shapeNode != null ? shapeNode.Value.Value : string.Empty;
if (!string.IsNullOrEmpty(shape))
{
try
{
ret = Game.CreateObject<IHitShape>(shape + "Shape");
FieldLoader.Load(ret, shapeNode.Value);
}
catch (YamlException e)
{
throw new YamlException("HitShape {0}: {1}".F(shape, e.Message));
}
}
else
ret = new CircleShape();
ret.Initialize();
return ret;
}
public override object Create(ActorInitializer init) { return new HitShape(init.Self, this); }
}
public class HitShape : ConditionalTrait<HitShapeInfo>, ITargetablePositions
{
BodyOrientation orientation;
ITargetableCells targetableCells;
Turreted turret;
public HitShape(Actor self, HitShapeInfo info)
: base(info) { }
protected override void Created(Actor self)
{
orientation = self.Trait<BodyOrientation>();
targetableCells = self.TraitOrDefault<ITargetableCells>();
turret = self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.Name == Info.Turret);
base.Created(self);
}
bool ITargetablePositions.AlwaysEnabled { get { return Info.RequiresCondition == null; } }
IEnumerable<WPos> ITargetablePositions.TargetablePositions(Actor self)
{
if (IsTraitDisabled)
return Enumerable.Empty<WPos>();
return TargetablePositions(self);
}
IEnumerable<WPos> TargetablePositions(Actor self)
{
if (Info.UseTargetableCellsOffsets && targetableCells != null)
foreach (var c in targetableCells.TargetableCells())
yield return self.World.Map.CenterOfCell(c.Cell);
foreach (var o in Info.TargetableOffsets)
{
var offset = CalculateTargetableOffset(self, o);
yield return self.CenterPosition + offset;
}
}
WVec CalculateTargetableOffset(Actor self, WVec offset)
{
var localOffset = offset;
var quantizedBodyOrientation = orientation.QuantizeOrientation(self, self.Orientation);
if (turret != null)
{
localOffset = localOffset.Rotate(turret.LocalOrientation);
localOffset += turret.Offset;
}
return orientation.LocalToWorld(localOffset.Rotate(quantizedBodyOrientation));
}
public WDist DistanceFromEdge(Actor self, WPos pos)
{
var origin = turret != null ? self.CenterPosition + turret.Position(self) : self.CenterPosition;
var orientation = turret != null ? turret.WorldOrientation : self.Orientation;
return Info.Type.DistanceFromEdge(pos, origin, orientation);
}
public IEnumerable<IRenderable> RenderDebugOverlay(Actor self, WorldRenderer wr)
{
var origin = turret != null ? self.CenterPosition + turret.Position(self) : self.CenterPosition;
var orientation = turret != null ? turret.WorldOrientation : self.Orientation;
return Info.Type.RenderDebugOverlay(wr, origin, orientation);
}
}
}