143 lines
5.4 KiB
C#
143 lines
5.4 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;
|
|
using System.Linq;
|
|
using OpenRA.Graphics;
|
|
using OpenRA.Primitives;
|
|
using OpenRA.Traits;
|
|
|
|
namespace OpenRA.Mods.Common.Traits
|
|
{
|
|
[Desc("This actor is selectable. Defines bounds of selectable area, selection class, selection priority and selection priority modifiers.")]
|
|
public class IsometricSelectableInfo : ITraitInfo, IMouseBoundsInfo, ISelectableInfo, IRulesetLoaded, Requires<BuildingInfo>
|
|
{
|
|
[Desc("Defines a custom rectangle for mouse interaction with the actor.",
|
|
"If null, the engine will guess an appropriate size based on the building's footprint.",
|
|
"The first two numbers define the width and depth of the footprint rectangle.",
|
|
"The (optional) second two numbers define an x and y offset from the actor center.")]
|
|
public readonly int[] Bounds = null;
|
|
|
|
[Desc("Height above the footprint for the top of the interaction rectangle.")]
|
|
public readonly int Height = 24;
|
|
|
|
[Desc("Defines a custom rectangle for Decorations (e.g. the selection box).",
|
|
"If null, Bounds will be used instead.")]
|
|
public readonly int[] DecorationBounds = null;
|
|
|
|
[Desc("Defines a custom height for Decorations (e.g. the selection box).",
|
|
"If < 0, Height will be used instead.",
|
|
"If Height is 0, this must be defined with a value greater than 0.")]
|
|
public readonly int DecorationHeight = -1;
|
|
|
|
public readonly int Priority = 10;
|
|
|
|
[Desc("Allow selection priority to be modified using a hotkey.",
|
|
"Valid values are None (priority is not affected by modifiers)",
|
|
"Ctrl (priority is raised when Ctrl pressed) and",
|
|
"Alt (priority is raised when Alt pressed).")]
|
|
public readonly SelectionPriorityModifiers PriorityModifiers = SelectionPriorityModifiers.None;
|
|
|
|
[Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). ",
|
|
"Defaults to the actor name when not defined or inherited.")]
|
|
public readonly string Class = null;
|
|
|
|
[VoiceReference]
|
|
public readonly string Voice = "Select";
|
|
|
|
public object Create(ActorInitializer init) { return new IsometricSelectable(init.Self, this); }
|
|
|
|
int ISelectableInfo.Priority { get { return Priority; } }
|
|
SelectionPriorityModifiers ISelectableInfo.PriorityModifiers { get { return PriorityModifiers; } }
|
|
string ISelectableInfo.Voice { get { return Voice; } }
|
|
|
|
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
|
{
|
|
var grid = Game.ModData.Manifest.Get<MapGrid>();
|
|
if (grid.Type != MapGridType.RectangularIsometric)
|
|
throw new YamlException("IsometricSelectable can only be used in mods that use the RectangularIsometric MapGrid type.");
|
|
|
|
if (Height == 0 && DecorationHeight <= 0)
|
|
throw new YamlException("DecorationHeight must be defined and greater than 0 if Height is 0.");
|
|
}
|
|
}
|
|
|
|
public class IsometricSelectable : IMouseBounds, ISelectable
|
|
{
|
|
readonly IsometricSelectableInfo info;
|
|
readonly string selectionClass = null;
|
|
readonly BuildingInfo buildingInfo;
|
|
|
|
public IsometricSelectable(Actor self, IsometricSelectableInfo info)
|
|
{
|
|
this.info = info;
|
|
selectionClass = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class;
|
|
buildingInfo = self.Info.TraitInfo<BuildingInfo>();
|
|
}
|
|
|
|
Polygon Bounds(Actor self, WorldRenderer wr, int[] bounds, int height)
|
|
{
|
|
int2 left, right, top, bottom;
|
|
if (bounds != null)
|
|
{
|
|
var offset = bounds.Length >= 4 ? new int2(bounds[2], bounds[3]) : int2.Zero;
|
|
var center = wr.ScreenPxPosition(self.CenterPosition) + offset;
|
|
left = center - new int2(bounds[0] / 2, 0);
|
|
right = left + new int2(bounds[0], 0);
|
|
top = center - new int2(0, bounds[1] / 2);
|
|
bottom = top + new int2(0, bounds[1]);
|
|
}
|
|
else
|
|
{
|
|
var xMin = int.MaxValue;
|
|
var xMax = int.MinValue;
|
|
var yMin = int.MaxValue;
|
|
var yMax = int.MinValue;
|
|
foreach (var c in buildingInfo.OccupiedTiles(self.Location))
|
|
{
|
|
xMin = Math.Min(xMin, c.X);
|
|
xMax = Math.Max(xMax, c.X);
|
|
yMin = Math.Min(yMin, c.Y);
|
|
yMax = Math.Max(yMax, c.Y);
|
|
}
|
|
|
|
left = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMin, yMax)) - new WVec(768, 0, 0));
|
|
right = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMax, yMin)) + new WVec(768, 0, 0));
|
|
top = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMin, yMin)) - new WVec(0, 768, 0));
|
|
bottom = wr.ScreenPxPosition(self.World.Map.CenterOfCell(new CPos(xMax, yMax)) + new WVec(0, 768, 0));
|
|
}
|
|
|
|
if (height == 0)
|
|
return new Polygon(new[] { top, left, bottom, right });
|
|
|
|
var h = new int2(0, height);
|
|
return new Polygon(new[] { top - h, left - h, left, bottom, right, right - h });
|
|
}
|
|
|
|
public Polygon Bounds(Actor self, WorldRenderer wr)
|
|
{
|
|
return Bounds(self, wr, info.Bounds, info.Height);
|
|
}
|
|
|
|
public Polygon DecorationBounds(Actor self, WorldRenderer wr)
|
|
{
|
|
return Bounds(self, wr, info.DecorationBounds ?? info.Bounds, info.DecorationHeight >= 0 ? info.DecorationHeight : info.Height);
|
|
}
|
|
|
|
Polygon IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
|
|
{
|
|
return Bounds(self, wr);
|
|
}
|
|
|
|
string ISelectable.Class { get { return selectionClass; } }
|
|
}
|
|
}
|