diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index 5388ce8430..25bb436d75 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -286,6 +286,7 @@
+
@@ -293,6 +294,7 @@
+
diff --git a/OpenRA.Mods.Common/Traits/Buildings/Building.cs b/OpenRA.Mods.Common/Traits/Buildings/Building.cs
index d099b8a155..cc846960d7 100644
--- a/OpenRA.Mods.Common/Traits/Buildings/Building.cs
+++ b/OpenRA.Mods.Common/Traits/Buildings/Building.cs
@@ -19,10 +19,6 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
- [Desc("Remove this trait to limit base-walking by cheap or defensive buildings.")]
- public class GivesBuildableAreaInfo : TraitInfo { }
- public class GivesBuildableArea { }
-
public enum FootprintCellType
{
Empty = '_',
@@ -36,10 +32,6 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Where you are allowed to place the building (Water, Clear, ...)")]
public readonly HashSet TerrainTypes = new HashSet();
- [Desc("The range to the next building it can be constructed. Set it higher for walls.",
- "Set to '-1' to disable adjacency checks.")]
- public readonly int Adjacent = 2;
-
[Desc("x means cell is blocked, capital X means blocked but not counting as targetable, ",
"= means part of the footprint but passable, _ means completely empty.")]
[FieldLoader.LoadUsing("LoadFootprint")]
@@ -175,19 +167,28 @@ namespace OpenRA.Mods.Common.Traits
return null;
}
+ bool ActorGrantsValidArea(Actor a, RequiresBuildableAreaInfo rba)
+ {
+ return rba.AreaTypes.Overlaps(a.TraitsImplementing()
+ .SelectMany(gba => gba.AreaTypes));
+ }
+
public virtual bool IsCloseEnoughToBase(World world, Player p, string buildingName, CPos topLeft)
{
+ var requiresBuildableArea = world.Map.Rules.Actors[buildingName].TraitInfoOrDefault();
var mapBuildRadius = world.WorldActor.Trait();
- if (Adjacent < 0 || p.PlayerActor.Trait().BuildAnywhere)
+
+ if (requiresBuildableArea == null || p.PlayerActor.Trait().BuildAnywhere)
return true;
if (mapBuildRadius.BuildRadiusEnabled && RequiresBaseProvider && FindBaseProvider(world, p, topLeft) == null)
return false;
+ var adjacent = requiresBuildableArea.Adjacent;
var buildingMaxBounds = Dimensions;
- var scanStart = world.Map.Clamp(topLeft - new CVec(Adjacent, Adjacent));
- var scanEnd = world.Map.Clamp(topLeft + buildingMaxBounds + new CVec(Adjacent, Adjacent));
+ var scanStart = world.Map.Clamp(topLeft - new CVec(adjacent, adjacent));
+ var scanEnd = world.Map.Clamp(topLeft + buildingMaxBounds + new CVec(adjacent, adjacent));
var nearnessCandidates = new List();
var bi = world.WorldActor.Trait();
@@ -205,12 +206,12 @@ namespace OpenRA.Mods.Common.Traits
{
var unitsAtPos = world.ActorMap.GetActorsAt(pos).Where(a => a.IsInWorld
&& (a.Owner == p || (allyBuildEnabled && a.Owner.Stances[p] == Stance.Ally))
- && a.Info.HasTraitInfo());
+ && ActorGrantsValidArea(a, requiresBuildableArea));
if (unitsAtPos.Any())
nearnessCandidates.Add(pos);
}
- else if (buildingAtPos.IsInWorld && buildingAtPos.Info.HasTraitInfo()
+ else if (buildingAtPos.IsInWorld && ActorGrantsValidArea(buildingAtPos, requiresBuildableArea)
&& (buildingAtPos.Owner == p || (allyBuildEnabled && buildingAtPos.Owner.Stances[p] == Stance.Ally)))
nearnessCandidates.Add(pos);
}
@@ -219,8 +220,8 @@ namespace OpenRA.Mods.Common.Traits
var buildingTiles = Tiles(topLeft).ToList();
return nearnessCandidates
.Any(a => buildingTiles
- .Any(b => Math.Abs(a.X - b.X) <= Adjacent
- && Math.Abs(a.Y - b.Y) <= Adjacent));
+ .Any(b => Math.Abs(a.X - b.X) <= adjacent
+ && Math.Abs(a.Y - b.Y) <= adjacent));
}
public IReadOnlyDictionary OccupiedCells(ActorInfo info, CPos topLeft, SubCell subCell = SubCell.Any)
diff --git a/OpenRA.Mods.Common/Traits/Buildings/GivesBuildableArea.cs b/OpenRA.Mods.Common/Traits/Buildings/GivesBuildableArea.cs
new file mode 100644
index 0000000000..c5b1fc746a
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Buildings/GivesBuildableArea.cs
@@ -0,0 +1,36 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2017 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 OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("This actor allows placement of other actors with 'RequiresBuildableArea' trait around it.")]
+ public class GivesBuildableAreaInfo : ConditionalTraitInfo
+ {
+ [FieldLoader.Require]
+ [Desc("Types of buildable area this actor gives.")]
+ public readonly HashSet AreaTypes = new HashSet();
+
+ public override object Create(ActorInitializer init) { return new GivesBuildableArea(this); }
+ }
+
+ public class GivesBuildableArea : ConditionalTrait
+ {
+ public GivesBuildableArea(GivesBuildableAreaInfo info)
+ : base(info) { }
+
+ readonly HashSet noAreaTypes = new HashSet();
+
+ public HashSet AreaTypes { get { return !IsTraitDisabled ? Info.AreaTypes : noAreaTypes; } }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Buildings/RequiresBuildableArea.cs b/OpenRA.Mods.Common/Traits/Buildings/RequiresBuildableArea.cs
new file mode 100644
index 0000000000..8f92b0036e
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Buildings/RequiresBuildableArea.cs
@@ -0,0 +1,29 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2017 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 OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("This actor requires another actor with 'GivesBuildableArea' trait around to be placed.")]
+ public class RequiresBuildableAreaInfo : TraitInfo, Requires
+ {
+ [FieldLoader.Require]
+ [Desc("Types of buildable are this actor requires.")]
+ public readonly HashSet AreaTypes = new HashSet();
+
+ [Desc("Maximum range from the actor with 'GivesBuildableArea' this can be placed at.")]
+ public readonly int Adjacent = 2;
+ }
+
+ public class RequiresBuildableArea { }
+}
diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
index 77877f0f9a..136f89c24f 100644
--- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
@@ -1210,6 +1210,30 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
+ if (engineVersion < 20171120)
+ {
+ // AreaTypes support is added to GivesBuildableArea and it is required.
+ var givesBuildableArea = node.Value.Nodes.FirstOrDefault(n => n.Key == "GivesBuildableArea");
+ if (givesBuildableArea != null)
+ givesBuildableArea.Value.Nodes.Add(new MiniYamlNode("AreaTypes", "building"));
+
+ // RequiresBuildableArea trait is added and Building.Adjacent is moved there.
+ var building = node.Value.Nodes.FirstOrDefault(n => n.Key == "Building");
+ if (building != null)
+ {
+ var adjacent = building.Value.Nodes.FirstOrDefault(n => n.Key == "Adjacent");
+ var areaTypes = new MiniYamlNode("AreaTypes", "building");
+ var requiresBuildableArea = new MiniYamlNode("RequiresBuildableArea", "");
+
+ requiresBuildableArea.Value.Nodes.Add(areaTypes);
+ if (adjacent != null)
+ requiresBuildableArea.Value.Nodes.Add(adjacent);
+
+ node.Value.Nodes.Add(requiresBuildableArea);
+ building.Value.Nodes.Remove(adjacent);
+ }
+ }
+
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
}
diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml
index a39a2e8cb9..6512cd4a4d 100644
--- a/mods/cnc/rules/defaults.yaml
+++ b/mods/cnc/rules/defaults.yaml
@@ -691,10 +691,12 @@
^BaseBuilding:
Inherits: ^Building
Building:
- Adjacent: 4
RequiresBaseProvider: true
BuildSounds: constru2.aud, hvydoor1.aud
TerrainTypes: Clear,Road
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 4
MustBeDestroyed:
RequiredForShortGame: true
RepairableBuilding:
@@ -705,6 +707,7 @@
DeathSequence: dead
UseDeathTypeSuffix: false
GivesBuildableArea:
+ AreaTypes: building
EmitInfantryOnSell:
ActorTypes: e6,e1,e1,e1
EngineerRepairable:
@@ -796,8 +799,10 @@
Dimensions: 1,1
Footprint: x
BuildSounds: hvydoor1.aud
- Adjacent: 7
TerrainTypes: Clear,Road
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 4
Targetable:
TargetTypes: Ground, Wall
Crushable:
diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml
index 4213ce46f3..13858b6c7b 100644
--- a/mods/d2k/rules/defaults.yaml
+++ b/mods/d2k/rules/defaults.yaml
@@ -395,8 +395,11 @@
Footprint: x
TerrainTypes: Rock, Concrete
BuildSounds: BUILD1.WAV
+ RequiresBuildableArea:
+ AreaTypes: building
Adjacent: 3
GivesBuildableArea:
+ AreaTypes: building
Capturable:
CaptureThreshold: 100
SoundOnDamageTransition:
diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml
index 599335c24f..e33d034015 100644
--- a/mods/d2k/rules/structures.yaml
+++ b/mods/d2k/rules/structures.yaml
@@ -1,10 +1,12 @@
^concrete:
AlwaysVisible:
Building:
- Adjacent: 4
TerrainTypes: Rock
BuildSounds: CHUNG.WAV
AllowInvalidPlacement: true
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 4
LaysTerrain:
Template: 88
TerrainTypes: Rock
@@ -55,7 +57,6 @@ construction_yard:
Building:
Footprint: xxx xxx ===
Dimensions: 3,3
- Adjacent: 4
LocalCenterOffset: 0,-512,0
LaysTerrain:
TerrainTypes: Rock
@@ -324,7 +325,7 @@ silo:
Cost: 120
Tooltip:
Name: Silo
- Building:
+ RequiresBuildableArea:
Adjacent: 4
-GivesBuildableArea:
Health:
@@ -677,8 +678,10 @@ wall:
AppearsOnRadar:
Building:
BuildSounds: CHUNG.WAV
- Adjacent: 7
TerrainTypes: Rock, Concrete
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 7
Health:
HP: 2000
Armor:
@@ -729,8 +732,9 @@ medium_gun_turret:
Tooltip:
Name: Gun Turret
Building:
- Adjacent: 4
BuildSounds: CHUNG.WAV
+ RequiresBuildableArea:
+ Adjacent: 4
Sellable:
SellSounds: CHUNG.WAV
Selectable:
@@ -775,8 +779,9 @@ large_gun_turret:
Tooltip:
Name: Rocket Turret
Building:
- Adjacent: 4
BuildSounds: CHUNG.WAV
+ RequiresBuildableArea:
+ Adjacent: 4
Sellable:
SellSounds: CHUNG.WAV
Selectable:
diff --git a/mods/ra/maps/bomber-john/rules.yaml b/mods/ra/maps/bomber-john/rules.yaml
index 449c8c403a..1aca12a31e 100644
--- a/mods/ra/maps/bomber-john/rules.yaml
+++ b/mods/ra/maps/bomber-john/rules.yaml
@@ -141,7 +141,6 @@ MINVV:
HiddenUnderFog:
Building:
TerrainTypes: Clear,Road
- Adjacent: -1
Buildable:
Queue: Building
BuildPaletteOrder: 10
diff --git a/mods/ra/maps/fort-lonestar/rules.yaml b/mods/ra/maps/fort-lonestar/rules.yaml
index 94372086bb..80f2db1fa1 100644
--- a/mods/ra/maps/fort-lonestar/rules.yaml
+++ b/mods/ra/maps/fort-lonestar/rules.yaml
@@ -175,6 +175,7 @@ FTUR:
Power:
Amount: 0
GivesBuildableArea:
+ AreaTypes: building
PBOX:
Buildable:
@@ -188,6 +189,7 @@ PBOX:
Power:
Amount: 0
GivesBuildableArea:
+ AreaTypes: building
DOG:
Buildable:
diff --git a/mods/ra/rules/civilian.yaml b/mods/ra/rules/civilian.yaml
index dfbaced362..aac076b050 100644
--- a/mods/ra/rules/civilian.yaml
+++ b/mods/ra/rules/civilian.yaml
@@ -85,6 +85,7 @@ FCOM:
CaptureCompleteTime: 30
ExternalCapturableBar:
GivesBuildableArea:
+ AreaTypes: building
BaseProvider:
Range: 8c0
EngineerRepairable:
diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml
index 352f8d084d..41639c62a5 100644
--- a/mods/ra/rules/defaults.yaml
+++ b/mods/ra/rules/defaults.yaml
@@ -587,6 +587,8 @@
Footprint: x
TerrainTypes: Clear,Road
RequiresBaseProvider: True
+ RequiresBuildableArea:
+ AreaTypes: building
SoundOnDamageTransition:
DamagedSounds: kaboom1.aud
DestroyedSounds: kaboom22.aud
@@ -616,6 +618,7 @@
Huntable:
UpdatesPlayerStatistics:
GivesBuildableArea:
+ AreaTypes: building
RepairableBuilding:
PlayerExperience: 25
EngineerRepairable:
@@ -662,8 +665,10 @@
Dimensions: 1,1
Footprint: x
BuildSounds: placbldg.aud
- Adjacent: 7
TerrainTypes: Clear,Road
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 7
SoundOnDamageTransition:
DamagedSounds: sandbag2.aud
DestroyedSounds: sandbag2.aud
@@ -706,12 +711,14 @@
Tooltip:
Name: Gate
Gate:
- Adjacent: 4
BlocksProjectilesHeight: 0
BuildSounds: place2.aud
OpeningSound: cashturn.aud
ClosingSound: cashturn.aud
TerrainTypes: Clear, Road
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 4
EditorTilesetFilter:
Categories: Wall
diff --git a/mods/ra/rules/fakes.yaml b/mods/ra/rules/fakes.yaml
index 12c9fcc983..6d76cf95eb 100644
--- a/mods/ra/rules/fakes.yaml
+++ b/mods/ra/rules/fakes.yaml
@@ -49,8 +49,9 @@ SYRF:
Building:
Footprint: XXX xxx XXX
Dimensions: 3,3
- Adjacent: 8
TerrainTypes: Water
+ RequiresBuildableArea:
+ Adjacent: 8
RenderSprites:
Image: SYRD
Valued:
@@ -91,8 +92,9 @@ SPEF:
Building:
Footprint: XXX xxx XXX
Dimensions: 3,3
- Adjacent: 8
TerrainTypes: Water
+ RequiresBuildableArea:
+ Adjacent: 8
RenderSprites:
Image: SPEN
Valued:
diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml
index 838e2ea60d..6a5362194b 100644
--- a/mods/ra/rules/structures.yaml
+++ b/mods/ra/rules/structures.yaml
@@ -125,8 +125,10 @@ SPEN:
Building:
Footprint: XXX xxx XXX
Dimensions: 3,3
- Adjacent: 8
TerrainTypes: Water
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 8
-GivesBuildableArea:
Health:
HP: 1000
@@ -227,8 +229,9 @@ SYRD:
Building:
Footprint: XXX xxx XXX
Dimensions: 3,3
- Adjacent: 8
TerrainTypes: Water
+ RequiresBuildableArea:
+ Adjacent: 8
-GivesBuildableArea:
Health:
HP: 1000
@@ -1154,8 +1157,6 @@ SILO:
Cost: 150
Tooltip:
Name: Silo
- Building:
- Adjacent: 2
-GivesBuildableArea:
Health:
HP: 300
@@ -1603,8 +1604,6 @@ KENN:
Cost: 100
Tooltip:
Name: Kennel
- Building:
- Adjacent: 2
-GivesBuildableArea:
Health:
HP: 300
diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml
index c5a5e5c47c..345667ee5e 100644
--- a/mods/ts/rules/defaults.yaml
+++ b/mods/ts/rules/defaults.yaml
@@ -309,6 +309,8 @@
Footprint: x
BuildSounds: place2.aud
TerrainTypes: Clear, Road, DirtRoad, Rough
+ RequiresBuildableArea:
+ AreaTypes: building
Adjacent: 4
FrozenUnderFog:
SoundOnDamageTransition:
@@ -340,6 +342,7 @@
Inherits@1: ^BasicBuilding
Inherits@2: ^EmpDisable
GivesBuildableArea:
+ AreaTypes: building
Capturable:
RepairableBuilding:
IndicatorPalette: mouse
@@ -439,8 +442,10 @@
Dimensions: 1,1
Footprint: x
BuildSounds: place2.aud
- Adjacent: 7
TerrainTypes: Clear, Rough, Road, DirtRoad, Green, Sand, Pavement
+ RequiresBuildableArea:
+ AreaTypes: building
+ Adjacent: 7
SoundOnDamageTransition:
DamagedSounds: expnew01.aud
DestroyedSounds: crmble2.aud
@@ -1134,7 +1139,6 @@
OpenSequence: open
Tooltip:
Gate:
- Adjacent: 4
BuildSounds: place2.aud
OpeningSound: gateup1.aud
ClosingSound: gatedwn1.aud
diff --git a/mods/ts/rules/nod-structures.yaml b/mods/ts/rules/nod-structures.yaml
index 31a1b58e9b..b732362bd2 100644
--- a/mods/ts/rules/nod-structures.yaml
+++ b/mods/ts/rules/nod-structures.yaml
@@ -348,9 +348,10 @@ NATMPL:
Tooltip:
Name: Temple of Nod
Building:
- Adjacent: 3
Footprint: xxxx xxxx xxxX
Dimensions: 4,3
+ RequiresBuildableArea:
+ Adjacent: 3
Selectable:
Bounds: 134, 120, 12, -12
Health: