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