#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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;
namespace OpenRA.Mods.Common.MapGenerator
{
public static class PlayableSpace
{
public enum Playability
{
/// Area is unplayable by land/naval units.
Unplayable = 0,
///
/// Area is unplayable by land/naval units, but should count as
/// being "within" a playable region. This usually applies to random
/// rock or river tiles in largely passable templates.
///
Partial = 1,
///
/// Area is playable by either land or naval units.
///
Playable = 2,
}
///
/// Additional data for a region containing playable space.
/// The shape of a region is specified separately via a region mask.
///
public sealed class Region
{
/// Area of playable and partially playable space.
public int Area;
/// Area of fully playable space.
public int PlayableArea;
/// Region ID.
public int Id;
}
/// Sentinel indicating a position isn't assigned to a region.
public const int NullRegion = -1;
///
///
/// Analyses a given map's tiles and ActorPlans and determines the playable space within it.
///
///
/// Requires a playabilityMap which specifies whether certain tiles are considered playable
/// or not. Actors are always considered partially playable.
///
///
/// RegionMap contains the mapping of map positions to Regions. If a map position is not
/// within a region, the value is NullRegion.
///
///
public static (Region[] Regions, CellLayer RegionMap, CellLayer Playable) FindPlayableRegions(
Map map,
List actorPlans,
Dictionary playabilityMap)
{
var size = map.MapSize;
var regions = new List();
var regionMap = new CellLayer(map);
regionMap.Clear(NullRegion);
var playable = new CellLayer(map);
playable.Clear(Playability.Unplayable);
foreach (var mpos in map.AllCells.MapCoords)
if (map.Contains(mpos))
playable[mpos] = playabilityMap[map.Tiles[mpos]];
foreach (var actorPlan in actorPlans)
foreach (var cpos in actorPlan.Footprint().Keys)
if (map.AllCells.Contains(cpos))
{
var mpos = cpos.ToMPos(map);
if (playable[mpos] == Playability.Playable)
playable[mpos] = Playability.Partial;
}
void Fill(Region region, CPos start)
{
void AddToRegion(CPos cpos, bool fullyPlayable)
{
var mpos = cpos.ToMPos(map);
regionMap[mpos] = region.Id;
region.Area++;
if (fullyPlayable)
region.PlayableArea++;
}
bool? Filler(CPos cpos, bool fullyPlayable)
{
var mpos = cpos.ToMPos(map);
if (regionMap[mpos] == NullRegion)
{
if (fullyPlayable && playable[mpos] == Playability.Playable)
{
AddToRegion(cpos, true);
return true;
}
else if (playable[mpos] == Playability.Partial)
{
AddToRegion(cpos, false);
return false;
}
}
return null;
}
CellLayerUtils.FloodFill(
playable,
new[] { (start, true) },
Filler,
Direction.Spread4CVec);
}
foreach (var mpos in map.AllCells.MapCoords)
if (regionMap[mpos] == NullRegion && playable[mpos] == Playability.Playable)
{
var region = new Region()
{
Area = 0,
PlayableArea = 0,
Id = regions.Count,
};
regions.Add(region);
var cpos = mpos.ToCPos(map);
Fill(region, cpos);
}
return (regions.ToArray(), regionMap, playable);
}
}
}