Implement isometric selection boxes for TS structures.

This commit is contained in:
Paul Chote
2020-03-21 16:00:52 +00:00
committed by atlimit8
parent 88cdad4189
commit 9f3254dbd1
14 changed files with 686 additions and 106 deletions

View File

@@ -0,0 +1,168 @@
#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 OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Graphics
{
public struct IsometricSelectionBarsAnnotationRenderable : IRenderable, IFinalizedRenderable
{
const int BarWidth = 3;
const int BarHeight = 4;
const int BarStride = 5;
static readonly Color EmptyColor = Color.FromArgb(160, 30, 30, 30);
static readonly Color DarkEmptyColor = Color.FromArgb(160, 15, 15, 15);
static readonly Color DarkenColor = Color.FromArgb(24, 0, 0, 0);
static readonly Color LightenColor = Color.FromArgb(24, 255, 255, 255);
readonly WPos pos;
readonly Actor actor;
readonly bool displayHealth;
readonly bool displayExtra;
readonly Polygon bounds;
public IsometricSelectionBarsAnnotationRenderable(Actor actor, Polygon bounds, bool displayHealth, bool displayExtra)
: this(actor.CenterPosition, actor, bounds)
{
this.displayHealth = displayHealth;
this.displayExtra = displayExtra;
}
public IsometricSelectionBarsAnnotationRenderable(WPos pos, Actor actor, Polygon bounds)
: this()
{
this.pos = pos;
this.actor = actor;
this.bounds = bounds;
}
public WPos Pos { get { return pos; } }
public bool DisplayHealth { get { return displayHealth; } }
public bool DisplayExtra { get { return displayExtra; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return 0; } }
public bool IsDecoration { get { return true; } }
public IRenderable WithPalette(PaletteReference newPalette) { return this; }
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(WVec vec) { return new IsometricSelectionBarsAnnotationRenderable(pos + vec, actor, bounds); }
public IRenderable AsDecoration() { return this; }
void DrawExtraBars(WorldRenderer wr)
{
var i = 1;
foreach (var extraBar in actor.TraitsImplementing<ISelectionBar>())
{
var value = extraBar.GetValue();
if (value != 0 || extraBar.DisplayWhenEmpty)
DrawBar(wr, extraBar.GetValue(), extraBar.GetColor(), i++);
}
}
void DrawBar(WorldRenderer wr, float value, Color barColor, int barNum, float? secondValue = null, Color? secondColor = null)
{
var darkColor = Color.FromArgb(barColor.A, barColor.R / 2, barColor.G / 2, barColor.B / 2);
var barAspect = new float2(1f, 0.5f);
var stepAspect = new float2(1f, -0.5f);
var offset = barNum * BarStride * barAspect - new float2(0, BarHeight + 1);
var start = wr.Viewport.WorldToViewPx(bounds.Vertices[1]).ToFloat2() + offset;
var end = wr.Viewport.WorldToViewPx(bounds.Vertices[0]).ToFloat2() + offset;
// HACK: Work around rounding errors that may cause a few-px offset in the end relative to the start
// Force the bar to take a 45 degree angle
end = new float2(end.X, start.Y - (end.X - start.X) / 2);
// Round the cut point to the nearest pixel to avoid potential off-by-one pixel offsets distorting the bar
var cutX = (int)(float2.Lerp(start.X, end.X, value) + 0.5f);
var cut = new float2(cutX, start.Y - (cutX - start.X) / 2);
var cr = Game.Renderer.RgbaColorRenderer;
var da = BarWidth * barAspect;
var db = new int2(0, BarHeight);
var dc = da + db;
// Filled bar
cr.FillRect(start + da, start + dc, cut + dc, cut + da, darkColor);
cr.FillRect(start, start + da, start + dc, start + db, darkColor);
cr.FillRect(start, start + da, cut + da, cut, barColor);
// Faint marks to break the monotony of the solid bar
var dx = BarWidth;
while (dx < cut.X - start.X)
{
var step = start + dx * stepAspect;
cr.DrawLine(step, step + da, 1, DarkenColor);
cr.DrawLine(step + da, step + dc, 1, LightenColor);
dx += BarWidth;
}
// Second bar (e.g. applied damage)
if (secondValue.HasValue && secondColor.HasValue)
{
var secondCutX = (int)(float2.Lerp(start.X, end.X, secondValue.Value) + 0.5f);
var secondCut = new float2(secondCutX, start.Y - (secondCutX - start.X) / 2);
var darkSecond = Color.FromArgb(secondColor.Value.A, secondColor.Value.R / 2, secondColor.Value.G / 2, secondColor.Value.B / 2);
cr.FillRect(cut + da, cut + dc, secondCut + dc, secondCut + da, darkSecond);
cr.FillRect(cut, cut + da, secondCut + da, secondCut, secondColor.Value);
value = secondValue.Value;
cut = secondCut;
}
// Empty bar
if (value < 1)
{
cr.FillRect(cut + da, cut + dc, end + dc, end + da, DarkEmptyColor);
cr.FillRect(cut, cut + da, end + da, end, EmptyColor);
}
}
Color GetHealthColor(IHealth health)
{
if (Game.Settings.Game.UsePlayerStanceColors)
return actor.Owner.PlayerStanceColor(actor);
return health.DamageState == DamageState.Critical ? Color.Red :
health.DamageState == DamageState.Heavy ? Color.Yellow : Color.LimeGreen;
}
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
if (!actor.IsInWorld || actor.IsDead)
return;
var health = actor.TraitOrDefault<IHealth>();
if (DisplayHealth)
{
if (health == null || health.IsDead)
return;
var displayValue = health.DisplayHP != health.HP ? (float?)health.DisplayHP / health.MaxHP : null;
DrawBar(wr, (float)health.HP / health.MaxHP, GetHealthColor(health), 0, displayValue, Color.OrangeRed);
}
if (DisplayExtra)
DrawExtraBars(wr);
}
public void RenderDebugGeometry(WorldRenderer wr) { }
public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; }
}
}

View File

@@ -0,0 +1,83 @@
#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.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Graphics
{
public struct IsometricSelectionBoxAnnotationRenderable : IRenderable, IFinalizedRenderable
{
static readonly float2 TLOffset = new float2(-12, -6);
static readonly float2 TROffset = new float2(12, -6);
static readonly float2 TOffset = new float2(0, -13);
static readonly float2[] Offsets =
{
-TROffset, -TLOffset, -TOffset,
TROffset, -TOffset, -TLOffset,
-TLOffset, TOffset, TROffset,
TLOffset, TROffset, TOffset,
-TROffset, TOffset, TLOffset,
TLOffset, -TOffset, -TROffset
};
readonly WPos pos;
readonly Polygon bounds;
readonly Color color;
public IsometricSelectionBoxAnnotationRenderable(Actor actor, Polygon bounds, Color color)
{
pos = actor.CenterPosition;
this.bounds = bounds;
this.color = color;
}
public IsometricSelectionBoxAnnotationRenderable(WPos pos, Polygon bounds, Color color)
{
this.pos = pos;
this.bounds = bounds;
this.color = color;
}
public WPos Pos { get { return pos; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return 0; } }
public bool IsDecoration { get { return true; } }
public IRenderable WithPalette(PaletteReference newPalette) { return this; }
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(WVec vec) { return new IsometricSelectionBoxAnnotationRenderable(pos + vec, bounds, color); }
public IRenderable AsDecoration() { return this; }
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
var screen = bounds.Vertices.Select(v => wr.Viewport.WorldToViewPx(v).ToFloat2()).ToArray();
var tl = new float2(-12, -6);
var tr = new float2(12, -6);
var t = new float2(0, -13);
var cr = Game.Renderer.RgbaColorRenderer;
for (var i = 0; i < 6; i++)
{
cr.DrawLine(new float3[] { screen[i] + Offsets[3 * i], screen[i], screen[i] + Offsets[3 * i + 1] }, 1, color, true);
cr.DrawLine(new float3[] { screen[i], screen[i] + Offsets[3 * i + 2] }, 1, color, true);
}
}
public void RenderDebugGeometry(WorldRenderer wr) { }
public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; }
}
}

View File

@@ -0,0 +1,142 @@
#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; } }
}
}

View File

@@ -0,0 +1,65 @@
#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.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
public class IsometricSelectionDecorationsInfo : SelectionDecorationsBaseInfo, Requires<IsometricSelectableInfo>
{
public override object Create(ActorInitializer init) { return new IsometricSelectionDecorations(init.Self, this); }
}
public class IsometricSelectionDecorations : SelectionDecorationsBase
{
readonly IsometricSelectable selectable;
public IsometricSelectionDecorations(Actor self, IsometricSelectionDecorationsInfo info)
: base(info)
{
selectable = self.Trait<IsometricSelectable>();
}
protected override int2 GetDecorationPosition(Actor self, WorldRenderer wr, DecorationPosition pos)
{
var bounds = selectable.DecorationBounds(self, wr);
switch (pos)
{
case DecorationPosition.TopLeft: return bounds.Vertices[1];
case DecorationPosition.TopRight: return bounds.Vertices[5];
case DecorationPosition.BottomLeft: return bounds.Vertices[2];
case DecorationPosition.BottomRight: return bounds.Vertices[4];
case DecorationPosition.Top: return new int2((bounds.Vertices[1].X + bounds.Vertices[5].X) / 2, bounds.Vertices[1].Y);
default: return bounds.BoundingRect.TopLeft + new int2(bounds.BoundingRect.Size.Width / 2, bounds.BoundingRect.Size.Height / 2);
}
}
protected override IEnumerable<IRenderable> RenderSelectionBox(Actor self, WorldRenderer wr, Color color)
{
var bounds = selectable.DecorationBounds(self, wr);
yield return new IsometricSelectionBoxAnnotationRenderable(self, bounds, color);
}
protected override IEnumerable<IRenderable> RenderSelectionBars(Actor self, WorldRenderer wr, bool displayHealth, bool displayExtra)
{
if (!displayHealth && !displayExtra)
yield break;
var bounds = selectable.DecorationBounds(self, wr);
yield return new IsometricSelectionBarsAnnotationRenderable(self, bounds, displayHealth, displayExtra);
}
}
}

View File

@@ -0,0 +1,87 @@
#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.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.Mods.Common.FileFormats;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class CopyIsometricSelectableHeight : UpdateRule
{
public override string Name { get { return "Copy IsometricSelectable.Height from art*.ini definitions."; } }
public override string Description
{
get
{
return "Reads building Height entries art.ini/artfs.ini/artmd.ini from the current working directory\n" +
"and adds IsometricSelectable definitions to matching actors.";
}
}
static readonly string[] SourceFiles = { "art.ini", "artfs.ini", "artmd.ini" };
readonly Dictionary<string, int> selectionHeight = new Dictionary<string, int>();
bool complete;
public override IEnumerable<string> BeforeUpdate(ModData modData)
{
if (complete)
yield break;
var grid = Game.ModData.Manifest.Get<MapGrid>();
foreach (var filename in SourceFiles)
{
if (!File.Exists(filename))
continue;
var file = new IniFile(File.Open(filename, FileMode.Open));
foreach (var section in file.Sections)
{
if (!section.Contains("Height"))
continue;
selectionHeight[section.Name] = (int)(float.Parse(section.GetValue("Height", "1")) * grid.TileSize.Height);
}
}
}
public override IEnumerable<string> AfterUpdate(ModData modData)
{
// Rule only applies to the default ruleset - skip maps
complete = true;
yield break;
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
{
if (complete || actorNode.LastChildMatching("IsometricSelectable") != null)
yield break;
var height = 0;
if (!selectionHeight.TryGetValue(actorNode.Key.ToLowerInvariant(), out height))
yield break;
// Don't redefine the default value
if (height == 24)
yield break;
var selection = new MiniYamlNode("IsometricSelectable", "");
selection.AddNode("Height", FieldSaver.FormatValue(height));
actorNode.AddNode(selection);
}
}
}

View File

@@ -11,8 +11,10 @@ CABHUT:
Palette: player
Targetable:
TargetTypes: C4
-Selectable:
-IsometricSelectable:
-IsometricSelectionDecorations:
Interactable:
SelectionDecorations:
-Demolishable:
-Explodes:
-FrozenUnderFog:

View File

@@ -16,6 +16,8 @@ ABAN01:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 72
ABAN02:
Inherits: ^CivBuilding
@@ -35,6 +37,8 @@ ABAN02:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 48
ABAN03:
Inherits: ^CivBuilding
@@ -54,6 +58,8 @@ ABAN03:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 48
ABAN04:
Inherits: ^CivBuilding
@@ -582,6 +588,8 @@ CA0012:
Type: light
Health:
HP: 10000
IsometricSelectable:
Height: 36
CA0013:
Inherits: ^CivBuilding
@@ -595,6 +603,8 @@ CA0013:
Type: heavy
Health:
HP: 30000
IsometricSelectable:
Height: 48
CA0014:
Inherits: ^CivBuilding
@@ -607,6 +617,8 @@ CA0014:
Type: heavy
Health:
HP: 30000
IsometricSelectable:
Height: 48
CA0015:
Inherits: ^CivBuilding
@@ -631,6 +643,8 @@ CA0016:
Type: light
Health:
HP: 30000
IsometricSelectable:
Height: 48
CA0017:
Inherits: ^CivBuilding
@@ -643,6 +657,8 @@ CA0017:
Type: heavy
Health:
HP: 30000
IsometricSelectable:
Height: 48
CA0018:
Inherits: ^CivBuilding
@@ -659,6 +675,8 @@ CA0018:
Type: light
Health:
HP: 20000
IsometricSelectable:
Height: 48
CA0019:
Inherits: ^CivBuilding
@@ -675,6 +693,8 @@ CA0019:
Type: light
Health:
HP: 20000
IsometricSelectable:
Height: 48
CA0020:
Inherits: ^CivBuilding
@@ -691,6 +711,8 @@ CA0020:
Type: light
Health:
HP: 20000
IsometricSelectable:
Height: 48
CA0021:
Inherits: ^CivBuilding
@@ -707,6 +729,8 @@ CA0021:
Type: light
Health:
HP: 20000
IsometricSelectable:
Height: 48
CAARAY:
Inherits: ^CivBuilding
@@ -734,6 +758,8 @@ CAARAY:
Pieces: 5, 7
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 72
CAARMR:
Inherits: ^CivBuilding
@@ -807,6 +833,8 @@ CAHOSP:
Pieces: 2, 4
MapEditorData:
Categories: Civilian building
IsometricSelectable:
Height: 48
CAPYR01:
Inherits: ^CivBuilding
@@ -822,6 +850,8 @@ CAPYR01:
HP: 40000
MapEditorData:
ExcludeTilesets: SNOW
IsometricSelectable:
Height: 48
CAPYR02:
Inherits: ^CivBuilding
@@ -841,6 +871,8 @@ CAPYR02:
Pieces: 6, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 4
IsometricSelectable:
Height: 96
CAPYR03:
Inherits: ^CivBuilding
@@ -860,6 +892,8 @@ CAPYR03:
Pieces: 6, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 4
IsometricSelectable:
Height: 96
CITY01:
Inherits: ^CivBuilding
@@ -879,6 +913,8 @@ CITY01:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 72
CITY02:
Inherits: ^CivBuilding
@@ -898,6 +934,8 @@ CITY02:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 72
CITY03:
Inherits: ^CivBuilding
@@ -917,6 +955,8 @@ CITY03:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 48
CITY04:
Inherits: ^CivBuilding
@@ -936,6 +976,8 @@ CITY04:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 72
CITY05:
Inherits: ^CivBuilding
@@ -955,6 +997,8 @@ CITY05:
Pieces: 5, 9
ThrowsShrapnel@LARGE:
Pieces: 2, 4
IsometricSelectable:
Height: 96
CITY06:
Inherits: ^CivBuilding
@@ -974,6 +1018,8 @@ CITY06:
Pieces: 7, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 4
IsometricSelectable:
Height: 72
CITY07:
Inherits: ^CivBuilding
@@ -993,6 +1039,8 @@ CITY07:
Pieces: 7, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 4
IsometricSelectable:
Height: 60
CITY08:
Inherits: ^CivBuilding
@@ -1023,6 +1071,8 @@ CITY09:
HP: 40000
MapEditorData:
RequireTilesets: TEMPERATE
IsometricSelectable:
Height: 48
CITY10:
Inherits: ^CivBuilding
@@ -1038,6 +1088,8 @@ CITY10:
HP: 30000
MapEditorData:
RequireTilesets: TEMPERATE
IsometricSelectable:
Height: 48
CITY11:
Inherits: ^CivBuilding
@@ -1053,6 +1105,8 @@ CITY11:
HP: 50000
MapEditorData:
RequireTilesets: TEMPERATE
IsometricSelectable:
Height: 96
CITY12:
Inherits: ^CivBuilding
@@ -1068,6 +1122,8 @@ CITY12:
HP: 60000
MapEditorData:
RequireTilesets: TEMPERATE
IsometricSelectable:
Height: 96
CITY13:
Inherits: ^CivBuilding
@@ -1083,6 +1139,8 @@ CITY13:
HP: 50000
MapEditorData:
RequireTilesets: TEMPERATE
IsometricSelectable:
Height: 96
CITY14:
Inherits: ^CivBuilding
@@ -1097,6 +1155,8 @@ CITY14:
HP: 60000
MapEditorData:
RequireTilesets: TEMPERATE
IsometricSelectable:
Height: 96
CITY15:
Inherits: ^CivBuilding
@@ -1135,6 +1195,8 @@ CITY16:
Pieces: 7, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 4
IsometricSelectable:
Height: 72
CITY17:
Inherits: ^CivBuilding
@@ -1154,6 +1216,8 @@ CITY17:
Pieces: 7, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 4
IsometricSelectable:
Height: 108
CITY18:
Inherits: ^CivBuilding
@@ -1173,6 +1237,8 @@ CITY18:
Pieces: 8, 12
ThrowsShrapnel@LARGE:
Pieces: 5, 7
IsometricSelectable:
Height: 72
CITY19:
Inherits: ^CivBuilding
@@ -1231,6 +1297,8 @@ CITY22:
HP: 10000
MapEditorData:
RequireTilesets: TEMPERATE
IsometricSelectable:
Height: 72
CTDAM:
Inherits: ^CivBuilding
@@ -1278,6 +1346,8 @@ CTVEGA:
Pieces: 7, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 4
IsometricSelectable:
Height: 72
GAKODK:
Inherits: ^CivBuilding
@@ -1313,16 +1383,22 @@ GAOLDCC2:
Name: Old Temple
Building:
Footprint: xx xX
IsometricSelectable:
Height: 36
GAOLDCC3:
Inherits: ^OldBase
Tooltip:
Name: Old Weapons Factory
IsometricSelectable:
Height: 36
GAOLDCC4:
Inherits: ^OldBase
Tooltip:
Name: Old Refinery
IsometricSelectable:
Height: 36
GAOLDCC5:
Inherits: ^OldBase
@@ -1371,9 +1447,6 @@ GASPOT:
Building:
Footprint: x
Dimensions: 1, 1
Selectable:
Bounds: 48, 30, 0, -4
DecorationBounds: 48, 82, 0, -25
Power:
Amount: -10
Armor:
@@ -1387,6 +1460,8 @@ GASPOT:
Sequence: idle-lights
MapEditorData:
Categories: Civilian building
IsometricSelectable:
Height: 72
GALITE:
Inherits: ^Building
@@ -1413,9 +1488,6 @@ GALITE:
WithIdleOverlay@LIGHTING:
Sequence: lighting
Palette: alpha
Selectable:
Bounds: 24, 24, 0, -4
DecorationBounds: 25, 35, 0, -12
-Cloak@EXTERNALCLOAK:
-ExternalCondition@CLOAKGENERATOR:
-ExternalCondition@CRATE-CLOAK:
@@ -1453,6 +1525,8 @@ GAICBM:
-WithDeathAnimation:
MapEditorData:
Categories: Vehicle
IsometricSelectable:
Height: 48
NAMNTK:
Inherits: ^CivBuilding
@@ -1470,6 +1544,8 @@ NAMNTK:
Palette: player
WithIdleOverlay@LIGHTS:
Sequence: idle-lights
IsometricSelectable:
Height: 36
NTPYRA:
Inherits: ^CivBuilding
@@ -1493,6 +1569,8 @@ NTPYRA:
Pieces: 7, 9
ThrowsShrapnel@LARGE:
Pieces: 3, 5
IsometricSelectable:
Height: 72
UFO:
Inherits: ^CivBuilding
@@ -1502,9 +1580,6 @@ UFO:
Footprint: xxxxxx xxxxxx xxxxxx xxxxxx
RenderSprites:
Palette: terraindecoration
Selectable:
Bounds: 144, 72, 0, 0
DecorationBounds: 144, 72, 0, 0
Tooltip:
Name: Scrin Ship
Health:
@@ -1517,3 +1592,5 @@ UFO:
Pieces: 9, 12
ThrowsShrapnel@LARGE:
Pieces: 6, 8
IsometricSelectable:
Height: 144

View File

@@ -1348,15 +1348,20 @@
green: pip-green
yellow: pip-yellow
^SelectableCombatBuilding:
Inherits@selectiondecorations: ^Selectable
Selectable:
Priority: 4
^SelectableBuilding:
Inherits@selectiondecorations: ^Selectable
Selectable:
IsometricSelectable:
Priority: 2
IsometricSelectionDecorations:
WithTextControlGroupDecoration:
Margin: 1, 1
DrawLineToTarget:
QueuedLineWidth: 2
QueuedMarkerWidth: 3
^SelectableCombatBuilding:
Inherits@selectiondecorations: ^SelectableBuilding
IsometricSelectable:
Priority: 4
^PrimaryBuilding:
PrimaryBuilding:
@@ -1366,6 +1371,5 @@
RequiresCondition: primary
Position: Top
RequiresSelection: true
Margin: 0, 5
Text: PRIMARY
Color: E0D048

View File

@@ -29,9 +29,6 @@ GAPOWR:
RequiresCondition: !build-incomplete
PauseOnCondition: empdisable
Sequence: idle-plug
Selectable:
Bounds: 90, 48, 0, -6
DecorationBounds: 90, 84, 0, -12
Power:
Amount: 100
RequiresCondition: !empdisable
@@ -66,6 +63,8 @@ GAPOWR:
RequiresCondition: !empdisable && powrup.b
Amount: 50
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 48
GAPILE:
Inherits: ^Building
@@ -85,9 +84,6 @@ GAPILE:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 88, 48, 0, -8
DecorationBounds: 88, 56, 0, -8
Health:
HP: 80000
Armor:
@@ -174,9 +170,6 @@ GAWEAP:
Building:
Footprint: xxX+ xxX+ xxX+
Dimensions: 4,3
Selectable:
Bounds: 154, 96, -2, -12
DecorationBounds: 154, 100, -2, -12
Health:
HP: 100000
RevealsShroud:
@@ -218,6 +211,8 @@ GAWEAP:
Power:
Amount: -30
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 48
GAHPAD:
Inherits: ^Building
@@ -269,9 +264,6 @@ GAHPAD:
UseDeathTypeSuffix: false
Power:
Amount: -10
Selectable:
Bounds: 88, 66, 0, -5
DecorationBounds: 88, 66, 0, -5
ProvidesPrerequisite@buildingname:
GADEPT:
@@ -289,9 +281,6 @@ GADEPT:
Building:
Footprint: =+= x++ x+=
Dimensions: 3,3
Selectable:
Bounds: 96, 64, -6, -6
DecorationBounds: 98, 68, -6, -6
Health:
HP: 110000
RevealsShroud:
@@ -355,9 +344,6 @@ GARADR:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 96, 48, 0, -6
DecorationBounds: 96, 118, 0, -38
Health:
HP: 100000
Armor:
@@ -380,6 +366,8 @@ GARADR:
Power:
Amount: -50
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 72
GATECH:
Inherits: ^Building
@@ -399,9 +387,6 @@ GATECH:
Building:
Footprint: xxx xxx
Dimensions: 3,2
Selectable:
Bounds: 110, 60, 3, -4
DecorationBounds: 110, 60, 3, -4
Health:
HP: 50000
Armor:
@@ -415,6 +400,8 @@ GATECH:
Power:
Amount: -150
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 48
GAPLUG:
Inherits: ^Building
@@ -424,9 +411,6 @@ GAPLUG:
Cost: 1000
Tooltip:
Name: GDI Upgrade Center
Selectable:
Bounds: 115,72,0,-12
DecorationBounds: 115,104,0,-24
Buildable:
BuildPaletteOrder: 170
Prerequisites: proc, gatech, ~structures.gdi, ~techlevel.superweapons
@@ -524,3 +508,5 @@ GAPLUG:
PauseOnCondition: disabled
Sequence: idle-hunterseekerb
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 48

View File

@@ -57,9 +57,6 @@ GACTWR:
BuildPaletteOrder: 70
Prerequisites: gapile, ~structures.gdi, ~techlevel.low
Description: Modular tower for base defenses.
Selectable:
Bounds: 48, 36, 0, -6
DecorationBounds: 48, 48, 0, -12
Health:
HP: 50000
Armor:
@@ -139,6 +136,8 @@ GACTWR:
ProvidesPrerequisite@buildingname:
Replacement:
ReplaceableTypes: GDITower
IsometricSelectable:
Height: 48
GAVULC:
Inherits: ^BuildingPlug

View File

@@ -15,9 +15,6 @@ NAPOWR:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 88, 48, 2, -6
DecorationBounds: 88, 80, 2, -12
Health:
HP: 75000
Armor:
@@ -35,6 +32,8 @@ NAPOWR:
TargetTypes: Ground, Building, C4, SpyInfiltrate
ScalePowerWithHealth:
PowerTooltip:
IsometricSelectable:
Height: 48
NAAPWR:
Inherits: ^Building
@@ -53,9 +52,6 @@ NAAPWR:
Building:
Footprint: xxx xxx
Dimensions: 2,3
Selectable:
Bounds: 100, 54, 0, -4
DecorationBounds: 100, 74, 0, -12
Health:
HP: 75000
Armor:
@@ -74,6 +70,8 @@ NAAPWR:
ScalePowerWithHealth:
PowerTooltip:
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 48
NAHAND:
Inherits: ^Building
@@ -93,9 +91,6 @@ NAHAND:
Building:
Footprint: xxx xxx
Dimensions: 3,2
Selectable:
Bounds: 116, 60, 3, -6
DecorationBounds: 116, 78, 3, -8
Health:
HP: 80000
Armor:
@@ -185,9 +180,6 @@ NAWEAP:
Building:
Footprint: xxX+ xxX+ xxX+
Dimensions: 4,3
Selectable:
Bounds: 149, 80, -3, -10
DecorationBounds: 149, 116, -3, -20
Health:
HP: 100000
Armor:
@@ -223,6 +215,8 @@ NAWEAP:
Power:
Amount: -30
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 48
NAHPAD:
Inherits: ^Building
@@ -274,10 +268,9 @@ NAHPAD:
UseDeathTypeSuffix: false
Power:
Amount: -10
Selectable:
Bounds: 78, 48, 0, -6
DecorationBounds: 78, 54, 0, -8
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 36
NARADR:
Inherits: ^Building
@@ -297,9 +290,6 @@ NARADR:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 96, 48, 0, -6
DecorationBounds: 96, 72, 0, -12
Health:
HP: 100000
Armor:
@@ -322,6 +312,8 @@ NARADR:
Power:
Amount: -40
ProvidesPrerequisite@buildingname:
IsometricSelectable:
Height: 72
NATECH:
Inherits: ^Building
@@ -341,9 +333,6 @@ NATECH:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 86, 48, 0, -4
DecorationBounds: 86, 58, 0, -4
Health:
HP: 50000
Armor:
@@ -379,8 +368,6 @@ NATMPL:
Dimensions: 4,3
RequiresBuildableArea:
Adjacent: 3
Selectable:
Bounds: 134, 120, 12, -12
Health:
HP: 100000
Armor:
@@ -407,3 +394,5 @@ NATMPL:
PauseOnCondition: empdisable
Exit@1:
ExitsDebugOverlay:
IsometricSelectable:
Height: 60

View File

@@ -63,8 +63,6 @@ NAPOST:
NodeTypes: laserfencenode
SegmentType: nafnce
SegmentsRequireNode: true
Selectable:
Bounds: 42, 44, 0, -12
LineBuildNode:
Types: laserfencenode
Power:
@@ -156,9 +154,6 @@ NALASR:
Prerequisites: nahand, ~structures.nod, ~techlevel.low
BuildPaletteOrder: 90
Description: Basic base defense.\nRequires power to operate.\n Strong vs Ground units\n Weak vs Aircraft
Selectable:
Bounds: 40, 30, -8, -6
DecorationBounds: 40, 36, -8, -8
Health:
HP: 50000
Armor:
@@ -202,9 +197,6 @@ NAOBEL:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 88, 42, 0, -6
DecorationBounds: 88, 72, 0, -12
Health:
HP: 72500
Armor:
@@ -246,9 +238,6 @@ NASAM:
Prerequisites: naradr, ~structures.nod, ~techlevel.medium
BuildPaletteOrder: 100
Description: Nod Anti-Air base defense.\nRequires power to operate.\n Strong vs Aircraft\n Weak vs Ground units
Selectable:
Bounds: 40, 30, -3, -8
DecorationBounds: 40, 36, -3, -8
Health:
HP: 60000
Armor:
@@ -312,9 +301,6 @@ NASTLH:
EnableSound: cloak5.aud
DisableSound: cloak5.aud
AffectsParent: true
Selectable:
Bounds: 106, 48, 8, -6
DecorationBounds: 106, 60, 8, -15
NAMISL:
Inherits: ^Building
@@ -335,9 +321,6 @@ NAMISL:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 75,48
DecorationBounds: 75,48
Health:
HP: 100000
Armor:
@@ -397,9 +380,6 @@ NAWAST:
Building:
Footprint: Xx= xx= Xx=
Dimensions: 3,3
Selectable:
Bounds: 100, 60, 5, -5
DecorationBounds: 100, 60, 5, -5
Health:
HP: 40000
RevealsShroud:
@@ -429,7 +409,9 @@ NAWAST:
WithResourceStoragePipsDecoration:
Position: BottomLeft
RequiresSelection: true
Margin: 5, 2
Margin: 8, 2
FullSequence: pip-red-building
EmptySequence: pip-empty-building
PipStride: 4, 2
PipCount: 15
FullSequence: pip-red
Palette: pips

View File

@@ -75,15 +75,14 @@ GACNST:
RequiresCondition: !build-incomplete
Power:
Amount: 0
Selectable:
Bounds: 144, 60, 0, -6
DecorationBounds: 144, 80, 0, -12
ProvidesPrerequisite@gdi:
Factions: gdi
Prerequisite: structures.gdi
ProvidesPrerequisite@nod:
Factions: nod
Prerequisite: structures.nod
IsometricSelectable:
Height: 36
PROC:
Inherits: ^Building
@@ -100,9 +99,6 @@ PROC:
Building:
Footprint: xxX+ xx++ xxX+
Dimensions: 4,3
Selectable:
Bounds: 134, 96, 0, -12
DecorationBounds: 134, 122, 0, -18
Health:
HP: 90000
RevealsShroud:
@@ -148,8 +144,11 @@ PROC:
WithResourceStoragePipsDecoration:
Position: BottomLeft
RequiresSelection: true
Margin: 5, 2
PipCount: 10
Margin: 8, 2
FullSequence: pip-green-building
EmptySequence: pip-empty-building
PipStride: 4, 2
PipCount: 25
Palette: pips
GASILO:
@@ -167,9 +166,6 @@ GASILO:
Building:
Footprint: xx xx
Dimensions: 2, 2
Selectable:
Bounds: 80, 48, -5, 0
DecorationBounds: 80, 48, -5, 0
-GivesBuildableArea:
Health:
HP: 30000
@@ -203,8 +199,11 @@ GASILO:
WithResourceStoragePipsDecoration:
Position: BottomLeft
RequiresSelection: true
Margin: 5, 2
PipCount: 5
Margin: 8, 2
FullSequence: pip-green-building
EmptySequence: pip-empty-building
PipStride: 4, 2
PipCount: 12
Palette: pips
ANYPOWER:

View File

@@ -14,9 +14,6 @@ NAPULS:
Building:
Footprint: xx xx
Dimensions: 2,2
Selectable:
Bounds: 78, 54, 0, -12
DecorationBounds: 78, 54, 0, -12
Health:
HP: 50000
Armor: