Refactor footprint cell lookups and move them to Building

Removing FootprintUtils happens in the next commit for better
reviewability.
This commit is contained in:
reaperrr
2017-06-28 14:00:50 +02:00
committed by abcdefg30
parent 801796b184
commit 46dc827d46
7 changed files with 112 additions and 85 deletions

View File

@@ -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);
}
}