Refactor footprint cell lookups and move them to Building
Removing FootprintUtils happens in the next commit for better reviewability.
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
#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;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Lint
|
||||
{
|
||||
class CheckBuildingFootprint : ILintRulesPass
|
||||
{
|
||||
public void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules)
|
||||
{
|
||||
foreach (var actorInfo in rules.Actors)
|
||||
{
|
||||
var building = actorInfo.Value.TraitInfoOrDefault<BuildingInfo>();
|
||||
if (building == null)
|
||||
continue;
|
||||
|
||||
var footprint = building.Footprint.Where(x => !char.IsWhiteSpace(x)).ToArray();
|
||||
var dimension = building.Dimensions;
|
||||
if (footprint.Length != dimension.X * dimension.Y)
|
||||
emitError("Invalid building footprint/dimension for " + actorInfo.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,6 @@
|
||||
<Compile Include="HitShapes\Rectangle.cs" />
|
||||
<Compile Include="HitShapes\Capsule.cs" />
|
||||
<Compile Include="HitShapes\Circle.cs" />
|
||||
<Compile Include="Lint\CheckBuildingFootprint.cs" />
|
||||
<Compile Include="Lint\CheckAngle.cs" />
|
||||
<Compile Include="Lint\CheckSequences.cs" />
|
||||
<Compile Include="Lint\CheckPalettes.cs" />
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace OpenRA.Mods.Common.Orders
|
||||
|
||||
var res = world.WorldActor.Trait<ResourceLayer>();
|
||||
var isCloseEnough = buildingInfo.IsCloseEnoughToBase(world, world.LocalPlayer, building, topLeft);
|
||||
foreach (var t in FootprintUtils.Tiles(rules, building, buildingInfo, topLeft))
|
||||
foreach (var t in buildingInfo.Tiles(topLeft))
|
||||
cells.Add(t, MakeCellType(isCloseEnough && world.IsCellBuildable(t, buildingInfo) && res.GetResource(t) == null));
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ namespace OpenRA.Mods.Common.Orders
|
||||
|
||||
IEnumerable<Order> ClearBlockersOrders(World world, CPos topLeft)
|
||||
{
|
||||
var allTiles = FootprintUtils.Tiles(world.Map.Rules, building, buildingInfo, topLeft).ToArray();
|
||||
var allTiles = buildingInfo.Tiles(topLeft).ToArray();
|
||||
var neightborTiles = Util.ExpandFootprint(allTiles, true).Except(allTiles)
|
||||
.Where(world.Map.Contains).ToList();
|
||||
|
||||
|
||||
@@ -23,6 +23,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public class GivesBuildableAreaInfo : TraitInfo<GivesBuildableArea> { }
|
||||
public class GivesBuildableArea { }
|
||||
|
||||
public enum FootprintCellType
|
||||
{
|
||||
Empty = '_',
|
||||
OccupiedPassable = '=',
|
||||
Blocking = 'x'
|
||||
}
|
||||
|
||||
public class BuildingInfo : ITraitInfo, IOccupySpaceInfo, IPlaceBuildingDecorationInfo, UsesInit<LocationInit>
|
||||
{
|
||||
[Desc("Where you are allowed to place the building (Water, Clear, ...)")]
|
||||
@@ -31,8 +38,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("The range to the next building it can be constructed. Set it higher for walls.")]
|
||||
public readonly int Adjacent = 2;
|
||||
|
||||
[Desc("x means space it blocks, _ is a part that is passable by actors.")]
|
||||
public readonly string Footprint = "x";
|
||||
[Desc("x means cell is blocked, = means part of the footprint but passable, ",
|
||||
"_ means completely empty.")]
|
||||
[FieldLoader.LoadUsing("LoadFootprint")]
|
||||
public readonly Dictionary<CVec, FootprintCellType> Footprint;
|
||||
|
||||
public readonly CVec Dimensions = new CVec(1, 1);
|
||||
|
||||
@@ -58,9 +67,90 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new Building(init, this); }
|
||||
|
||||
protected static object LoadFootprint(MiniYaml yaml)
|
||||
{
|
||||
var footprintYaml = yaml.Nodes.FirstOrDefault(n => n.Key == "Footprint");
|
||||
var footprintChars = footprintYaml != null ? footprintYaml.Value.Value.Where(x => !char.IsWhiteSpace(x)).ToArray() : new[] { 'x' };
|
||||
|
||||
var dimensionsYaml = yaml.Nodes.FirstOrDefault(n => n.Key == "Dimensions");
|
||||
var dim = dimensionsYaml != null ? FieldLoader.GetValue<CVec>("Dimensions", dimensionsYaml.Value.Value) : new CVec(1, 1);
|
||||
|
||||
if (footprintChars.Length != dim.X * dim.Y)
|
||||
{
|
||||
var fp = footprintYaml.Value.Value.ToString();
|
||||
var dims = dim.X + "x" + dim.Y;
|
||||
throw new YamlException("Invalid footprint: {0} does not match dimensions {1}".F(fp, dims));
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
var ret = new Dictionary<CVec, FootprintCellType>();
|
||||
for (var y = 0; y < dim.Y; y++)
|
||||
{
|
||||
for (var x = 0; x < dim.X; x++)
|
||||
{
|
||||
var c = footprintChars[index++];
|
||||
if (!Enum.IsDefined(typeof(FootprintCellType), (FootprintCellType)c))
|
||||
throw new YamlException("Invalid footprint cell type '{0}'".F(c));
|
||||
|
||||
ret[new CVec(x, y)] = (FootprintCellType)c;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public IEnumerable<CPos> FootprintTiles(CPos location, FootprintCellType type)
|
||||
{
|
||||
return Footprint.Where(kv => kv.Value == type).Select(kv => location + kv.Key);
|
||||
}
|
||||
|
||||
public IEnumerable<CPos> Tiles(CPos location)
|
||||
{
|
||||
foreach (var t in FootprintTiles(location, FootprintCellType.OccupiedPassable))
|
||||
yield return t;
|
||||
|
||||
foreach (var t in FootprintTiles(location, FootprintCellType.Blocking))
|
||||
yield return t;
|
||||
}
|
||||
|
||||
public IEnumerable<CPos> FrozenUnderFogTiles(CPos location)
|
||||
{
|
||||
foreach (var t in FootprintTiles(location, FootprintCellType.Empty))
|
||||
yield return t;
|
||||
|
||||
foreach (var t in Tiles(location))
|
||||
yield return t;
|
||||
}
|
||||
|
||||
public IEnumerable<CPos> UnpathableTiles(CPos location)
|
||||
{
|
||||
foreach (var t in FootprintTiles(location, FootprintCellType.Blocking))
|
||||
yield return t;
|
||||
}
|
||||
|
||||
public IEnumerable<CPos> PathableTiles(CPos location)
|
||||
{
|
||||
foreach (var t in FootprintTiles(location, FootprintCellType.Empty))
|
||||
yield return t;
|
||||
|
||||
foreach (var t in FootprintTiles(location, FootprintCellType.OccupiedPassable))
|
||||
yield return t;
|
||||
}
|
||||
|
||||
public CVec LocationOffset()
|
||||
{
|
||||
return new CVec(Dimensions.X / 2, Dimensions.Y > 1 ? (Dimensions.Y + 1) / 2 : 0);
|
||||
}
|
||||
|
||||
public WVec CenterOffset(World w)
|
||||
{
|
||||
var off = (w.Map.CenterOfCell(new CPos(Dimensions.X, Dimensions.Y)) - w.Map.CenterOfCell(new CPos(1, 1))) / 2;
|
||||
return (off - new WVec(0, 0, off.Z)) + LocalCenterOffset;
|
||||
}
|
||||
|
||||
public Actor FindBaseProvider(World world, Player p, CPos topLeft)
|
||||
{
|
||||
var center = world.Map.CenterOfCell(topLeft) + FootprintUtils.CenterOffset(world, this);
|
||||
var center = world.Map.CenterOfCell(topLeft) + CenterOffset(world);
|
||||
var allyBuildEnabled = world.WorldActor.Trait<MapBuildRadius>().AllyBuildRadiusEnabled;
|
||||
|
||||
foreach (var bp in world.ActorsWithTrait<BaseProvider>())
|
||||
@@ -118,7 +208,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
var buildingTiles = FootprintUtils.Tiles(world.Map.Rules, buildingName, this, topLeft).ToList();
|
||||
var buildingTiles = Tiles(topLeft).ToList();
|
||||
return nearnessCandidates
|
||||
.Any(a => buildingTiles
|
||||
.Any(b => Math.Abs(a.X - b.X) <= Adjacent
|
||||
@@ -127,7 +217,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos topLeft, SubCell subCell = SubCell.Any)
|
||||
{
|
||||
var occupied = FootprintUtils.UnpathableTiles(info.Name, this, topLeft)
|
||||
var occupied = UnpathableTiles(topLeft)
|
||||
.ToDictionary(c => c, c => SubCell.FullCell);
|
||||
|
||||
return new ReadOnlyDictionary<CPos, SubCell>(occupied);
|
||||
@@ -178,13 +268,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
topLeft = init.Get<LocationInit, CPos>();
|
||||
Info = info;
|
||||
|
||||
occupiedCells = FootprintUtils.UnpathableTiles(self.Info.Name, Info, TopLeft)
|
||||
occupiedCells = Info.UnpathableTiles(TopLeft)
|
||||
.Select(c => Pair.New(c, SubCell.FullCell)).ToArray();
|
||||
|
||||
targetableCells = FootprintUtils.UnpathableTiles(self.Info.Name, Info, TopLeft)
|
||||
targetableCells = Info.FootprintTiles(TopLeft, FootprintCellType.Blocking)
|
||||
.Select(c => Pair.New(c, SubCell.FullCell)).ToArray();
|
||||
|
||||
CenterPosition = init.World.Map.CenterOfCell(topLeft) + FootprintUtils.CenterOffset(init.World, Info);
|
||||
CenterPosition = init.World.Map.CenterOfCell(topLeft) + Info.CenterOffset(init.World);
|
||||
SkipMakeAnimation = init.Contains<SkipMakeAnimsInit>();
|
||||
}
|
||||
|
||||
@@ -258,7 +348,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var smudgeLayers = self.World.WorldActor.TraitsImplementing<SmudgeLayer>();
|
||||
|
||||
foreach (var smudgeLayer in smudgeLayers)
|
||||
foreach (var footprintTile in FootprintUtils.Tiles(self))
|
||||
foreach (var footprintTile in Info.Tiles(self.Location))
|
||||
smudgeLayer.RemoveSmudge(footprintTile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (b == null)
|
||||
return;
|
||||
|
||||
foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b, a.Location))
|
||||
foreach (var u in b.Tiles(a.Location))
|
||||
if (influence.Contains(u) && influence[u] == null)
|
||||
influence[u] = a;
|
||||
};
|
||||
@@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (b == null)
|
||||
return;
|
||||
|
||||
foreach (var u in FootprintUtils.Tiles(map.Rules, a.Info.Name, b, a.Location))
|
||||
foreach (var u in b.Tiles(a.Location))
|
||||
if (influence.Contains(u) && influence[u] == a)
|
||||
influence[u] = null;
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return true;
|
||||
|
||||
var res = world.WorldActor.Trait<ResourceLayer>();
|
||||
return FootprintUtils.Tiles(world.Map.Rules, name, building, topLeft).All(
|
||||
return building.Tiles(topLeft).All(
|
||||
t => world.Map.Contains(t) && res.GetResource(t) == null &&
|
||||
world.IsCellBuildable(t, building, toIgnore));
|
||||
}
|
||||
|
||||
@@ -17,62 +17,36 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public static class FootprintUtils
|
||||
{
|
||||
public static IEnumerable<CPos> Tiles(Ruleset rules, string name, BuildingInfo buildingInfo, CPos topLeft, bool includePassable = false)
|
||||
{
|
||||
var dim = buildingInfo.Dimensions;
|
||||
var footprint = buildingInfo.Footprint.Where(x => !char.IsWhiteSpace(x));
|
||||
|
||||
return TilesWhere(name, dim, footprint.ToArray(), a => includePassable || a != '_').Select(t => t + topLeft);
|
||||
}
|
||||
|
||||
public static IEnumerable<CPos> Tiles(Actor a)
|
||||
{
|
||||
return Tiles(a.World.Map.Rules, a.Info.Name, a.Info.TraitInfo<BuildingInfo>(), a.Location);
|
||||
var info = a.Info.TraitInfo<BuildingInfo>();
|
||||
return info.Tiles(a.Location);
|
||||
}
|
||||
|
||||
public static IEnumerable<CPos> FrozenUnderFogTiles(Actor a)
|
||||
{
|
||||
return Tiles(a.World.Map.Rules, a.Info.Name, a.Info.TraitInfo<BuildingInfo>(), a.Location, true);
|
||||
var info = a.Info.TraitInfo<BuildingInfo>();
|
||||
return info.FrozenUnderFogTiles(a);
|
||||
}
|
||||
|
||||
public static IEnumerable<CPos> UnpathableTiles(string name, BuildingInfo buildingInfo, CPos position)
|
||||
{
|
||||
var footprint = buildingInfo.Footprint.Where(x => !char.IsWhiteSpace(x)).ToArray();
|
||||
foreach (var tile in TilesWhere(name, buildingInfo.Dimensions, footprint, a => a == 'x'))
|
||||
yield return tile + position;
|
||||
return buildingInfo.UnpathableTiles(position);
|
||||
}
|
||||
|
||||
public static IEnumerable<CPos> PathableTiles(string name, BuildingInfo buildingInfo, CPos position)
|
||||
{
|
||||
var footprint = buildingInfo.Footprint.Where(x => !char.IsWhiteSpace(x)).ToArray();
|
||||
foreach (var tile in TilesWhere(name, buildingInfo.Dimensions, footprint, a => a == '_'))
|
||||
yield return tile + position;
|
||||
}
|
||||
|
||||
static IEnumerable<CVec> TilesWhere(string name, CVec dim, char[] footprint, Func<char, bool> cond)
|
||||
{
|
||||
if (footprint.Length != dim.X * dim.Y)
|
||||
throw new InvalidOperationException("Invalid footprint for " + name);
|
||||
var index = 0;
|
||||
|
||||
for (var y = 0; y < dim.Y; y++)
|
||||
for (var x = 0; x < dim.X; x++)
|
||||
if (cond(footprint[index++]))
|
||||
yield return new CVec(x, y);
|
||||
return buildingInfo.PathableTiles(position);
|
||||
}
|
||||
|
||||
public static CVec AdjustForBuildingSize(BuildingInfo buildingInfo)
|
||||
{
|
||||
var dim = buildingInfo.Dimensions;
|
||||
return new CVec(dim.X / 2, dim.Y > 1 ? (dim.Y + 1) / 2 : 0);
|
||||
return buildingInfo.AdjustForBuildingSize();
|
||||
}
|
||||
|
||||
public static WVec CenterOffset(World w, BuildingInfo buildingInfo)
|
||||
{
|
||||
var dim = buildingInfo.Dimensions;
|
||||
var localOffset = buildingInfo.LocalCenterOffset;
|
||||
var off = (w.Map.CenterOfCell(new CPos(dim.X, dim.Y)) - w.Map.CenterOfCell(new CPos(1, 1))) / 2;
|
||||
return (off - new WVec(0, 0, off.Z)) + localOffset;
|
||||
return buildingInfo.CenterOffset(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user