Пример #1
0
        /// <summary>
        /// Determines which areas are neighbors of other areas.
        /// </summary>
        /// <param name="areas">A map representation where each contiguous area has the same number (i.e. the output of <see cref="GetAreaGrid"/>).</param>
        /// <returns>A distinct set of neighbor relationships as byte-tuples (with the lower-value id coming first in each tuple).</returns>
        private static HashSet <(byte, byte)> GetNeighbors(MapArray <byte> areas)
        {
            /* Neighbors are areas with bordering spaces. I'm pretty sure we don't
             * have to worry about diagonals because I don't think they come up specifically,
             * and I don't think you can move between two diagonal neighbor spaces if there
             * are no other open spaces around.
             *
             * As such, this just goes from left-to-right in each row and bottom-to-top
             * in each column and whenever the adjacent numbers are different and non-zero,
             * that's a neighboring relationship.
             */
            var neighbors = new HashSet <(byte, byte)>();

            // TODO: Get rid of code duplication
            for (var x = 0; x < areas.Size.X; x++)
            {
                for (var y = 1; y < areas.Size.Y; y++)
                {
                    if (areas[x, y - 1] != 0 && areas[x, y] != 0 && areas[x, y - 1] != areas[x, y])
                    {
                        var first  = areas[x, y - 1];
                        var second = areas[x, y];

                        // Consistently put the lesser number first to avoid duplication
                        if (first < second)
                        {
                            neighbors.Add((first, second));
                        }
                        else
                        {
                            neighbors.Add((second, first));
                        }
                    }
                }
            }

            for (var y = 0; y < areas.Size.Y; y++)
            {
                for (var x = 1; x < areas.Size.X; x++)
                {
                    if (areas[x - 1, y] != 0 && areas[x, y] != 0 && areas[x - 1, y] != areas[x, y])
                    {
                        var first  = areas[x - 1, y];
                        var second = areas[x, y];

                        // Consistently put the lesser number first to avoid duplication
                        if (first < second)
                        {
                            neighbors.Add((first, second));
                        }
                        else
                        {
                            neighbors.Add((second, first));
                        }
                    }
                }
            }

            return(neighbors);
        }
Пример #2
0
        public Map(StartRaw startingData)
        {
            this.Raw  = startingData;
            this.Size = startingData.MapSize;

            placementGridOriginal = new MapArray <byte>(startingData.PlacementGrid.Data.ToByteArray(), this.Size);
            placementGrid         = new MapArray <byte>(placementGridOriginal);
            pathingGrid           = new MapArray <byte>(startingData.PathingGrid.Data.ToByteArray(), this.Size);
            heightGrid            = new MapArray <byte>(startingData.TerrainHeight.Data.ToByteArray(), this.Size);
            creepGrid             = new MapArray <byte>(startingData.TerrainHeight.Data.ToByteArray(), this.Size);

            this.structuresAndDeposits     = new MapArray <Unit>(this.Size);
            this.structuresAndDepositsList = new List <Unit>();

            GeneratePadding(startingData);

            this.structurePadding = new MapArray <bool>(this.Size);
            this.resourcePadding  = new MapArray <bool>(this.Size);

            // Not gonna work for anything but 1-on-1 games, but it seems like
            // it doesn't give this player's location, just the enemy's.
            var enemyStart = startingData.StartLocations.Single();

            this.enemyStartLocation = new Location {
                X = (int)enemyStart.X, Y = (int)enemyStart.Y
            };
        }
Пример #3
0
        private void GeneratePadding(StartRaw startingData)
        {
            this.padding = new MapArray <bool>(this.Size);

            for (var x = 0; x < Size.X; x++)
            {
                for (var y = 0; y < Size.Y; y++)
                {
                    if (placementGrid[x, y] == 0)
                    {
                        SetAdjacentSpaces(this.padding, x, y);
                    }
                }
            }
        }
Пример #4
0
        public MapData(StartRaw startingData)
        {
            this.Raw  = startingData;
            this.Size = startingData.MapSize;

            pathingGrid   = new MapArray <byte>(startingData.PathingGrid.Data.ToByteArray(), this.Size);
            placementGrid = new MapArray <byte>(startingData.PlacementGrid.Data.ToByteArray(), this.Size);
            heightGrid    = new MapArray <byte>(startingData.TerrainHeight.Data.ToByteArray(), this.Size);

            this.structuresAndDeposits = new MapArray <Unit>(this.Size);

            GeneratePadding(startingData);

            this.areas    = GetAreas();
            this.deposits = new List <Deposit>();

            this.structurePadding = new MapArray <bool>(this.Size);
        }
Пример #5
0
        private void SetAdjacentSpaces(MapArray <bool> targetArray, int x, int y, int size = 1)
        {
            var xVals = new List <int> {
                x - size, x, x + size
            };

            xVals.RemoveAll(n => n < 0 || n >= Size.X);

            var yVals = new List <int> {
                y - size, y, y + size
            };

            yVals.RemoveAll(n => n < 0 || n >= Size.Y);

            foreach (var xVal in xVals)
            {
                foreach (var yVal in yVals)
                {
                    targetArray[xVal, yVal] = true;
                }
            }
        }
Пример #6
0
        public MapData(MapData prior, List <Unit> units, Translator translator, Dictionary <uint, UnitTypeData> unitTypes)
        {
            this.Raw           = prior.Raw;
            this.Size          = prior.Size;
            this.placementGrid = prior.placementGrid;
            this.pathingGrid   = prior.pathingGrid;
            this.heightGrid    = prior.heightGrid;
            this.padding       = prior.padding;
            this.areaGrid      = prior.areaGrid;
            this.areas         = prior.areas;

            this.deposits = GetDeposits(units);

            this.structuresAndDeposits = new MapArray <Unit>(this.Size);
            this.structurePadding      = new MapArray <bool>(this.Size);

            foreach (var unit in units)
            {
                var unitType = unitTypes[unit.Raw.UnitType];
                if (unitType.Attributes.Contains(Proto.Attribute.Structure))
                {
                    var structureSize = translator.GetStructureSize(unit);
                    var originX       = (int)Math.Round(unit.X - structureSize.X * 0.5f);
                    var originY       = (int)Math.Round(unit.Y - structureSize.Y * 0.5f);

                    for (var x = originX; x < originX + structureSize.X; x++)
                    {
                        for (var y = originY; y < originY + structureSize.Y; y++)
                        {
                            structuresAndDeposits[x, y] = unit;
                            SetAdjacentSpaces(structurePadding, x, y);
                        }
                    }
                }
            }
        }
Пример #7
0
        private void SetAdjacentSpaces(MapArray <bool> targetArray, int x, int y)
        {
            var xVals = new List <int> {
                x - 1, x, x + 1
            };

            xVals.Remove(-1);
            xVals.Remove(Size.X);

            var yVals = new List <int> {
                y - 1, y, y + 1
            };

            yVals.Remove(-1);
            yVals.Remove(Size.Y);

            foreach (var xVal in xVals)
            {
                foreach (var yVal in yVals)
                {
                    targetArray[xVal, yVal] = true;
                }
            }
        }
Пример #8
0
 public MapArray(MapArray <T> other)
 {
     this.size = new Size2DI(other.size);
     this.data = (T[])other.data.Clone();
 }
Пример #9
0
 private void AddAdjacentLocations(Location location, HashSet <Location> locations, MapArray <byte> areas)
 {
     foreach (var adjacentLocation in AdjacentLocations(location))
     {
         if (areas[adjacentLocation] == 0 &&
             (this.CanBuild(adjacentLocation) || this.CanTraverse(adjacentLocation)))
         {
             locations.Add(adjacentLocation);
         }
     }
 }
Пример #10
0
        /// <summary>
        /// Builds a representation of the map where each distinct area has a different value.
        /// </summary>
        /// <param name="mapData">The representation of the map. (Only the size, pathing grid, and placement grid are used.)</param>
        /// <returns>A represntation of the map where all spaces that are the same 'area' have the same numeric value.
        /// Locations that are not accessible to ground units have a value of 0.</returns>
        private MapArray <byte> GetAreaGrid()
        {
            /* Pick a starting location and fan out to adjacent locations.
             *
             * If it's a buildable zone, find all spots that are also buildable,
             * adjacent, and at the same height. This will be a 'mesa'.
             *
             * If it's not buildable but it can be traversed then it's a ramp,
             * find all adjacent spots that can also be traversed and are not buildable.
             *
             *  We can probably use the starting locations on the map as safe places to begin,
             *  since they'll be base locations.
             */

            // TODO: Add support for "islands" - this mechanism will only capture areas connected to the starting location

            // Resulting array of areas
            MapArray <byte> areas = new MapArray <byte>(this.Size);

            // Area id - will be incremented as we move to other areas
            // (and before assigning the first area as well)
            byte currentId = 0;

            var locations          = new HashSet <Location>();
            var otherAreaLocations = new HashSet <Location>();

            var startPosition = this.Raw.StartLocations[0];

            otherAreaLocations.Add(new Location {
                X = (int)startPosition.X, Y = (int)startPosition.Y
            });

            while (otherAreaLocations.Count > 0)
            {
                var lastLocation = otherAreaLocations.First();
                otherAreaLocations.Remove(lastLocation);
                currentId++;
                areas[lastLocation] = currentId;

                AddAdjacentLocations(lastLocation, locations, areas);

                while (locations.Count > 0)
                {
                    var location = locations.First();
                    locations.Remove(location);
                    otherAreaLocations.Remove(location);

                    if (CanBuild(lastLocation) == CanBuild(location))
                    {
                        areas[location] = currentId;
                        AddAdjacentLocations(location, locations, areas);
                    }
                    else
                    {
                        otherAreaLocations.Add(location);
                    }
                }
            }

            return(areas);
        }
Пример #11
0
        /// <summary>
        /// Builds a list of <see cref="Area"/>s that have references to their neighbors.
        /// </summary>
        public List <Area> GetAreas() // TODO: Break up this function
        {
            this.areaGrid = GetAreaGrid();
            var neighbors = GetNeighbors(areaGrid);

            var mesas = new Dictionary <byte, Mesa>();
            var ramps = new Dictionary <byte, Ramp>();
            var edges = new Dictionary <byte, Edge>();

            var maxAreaId = areaGrid.Data.Max();

            // Need to get the center of each area. To avoid translation confusion,
            // I'm just going to create arrays with an extra space even though there's
            // no relevant area 0 (which instead represents impassible terrain).
            var centers       = new Location[maxAreaId + 1];
            var locationLists = new List <Location> [maxAreaId + 1];
            var xTotals       = new int[maxAreaId + 1];
            var yTotals       = new int[maxAreaId + 1];
            var counts        = new int[maxAreaId + 1];

            for (var x = 0; x < this.Size.X; x++)
            {
                for (var y = 0; y < this.Size.Y; y++)
                {
                    var areaId = areaGrid[x, y];
                    if (areaId != 0)
                    {
                        xTotals[areaId] += x;
                        yTotals[areaId] += y;
                        counts[areaId]  += 1;

                        locationLists[areaId] = locationLists[areaId] ?? new List <Location>();
                        locationLists[areaId].Add(new Location {
                            X = x, Y = y
                        });
                    }
                }
            }

            // Build mesas first - ramps need mesa info in their constructor,
            // and for convenience mesas can add neighbors post-construction
            for (byte areaId = 1; areaId <= maxAreaId; areaId++)
            {
                // This part can really be done once for both ramps and mesas
                var center = new Location
                {
                    X = xTotals[areaId] / counts[areaId],
                    Y = yTotals[areaId] / counts[areaId]
                };

                if (locationLists[areaId].Contains(center))
                {
                    centers[areaId] = center;
                }
                else
                {
                    centers[areaId] = center.GetClosest(locationLists[areaId]);
                }

                if (CanBuild(locationLists[areaId][0]))
                {
                    var height = this.HeightGrid[locationLists[areaId][0]];

                    mesas[areaId] = new Mesa(areaId, locationLists[areaId], centers[areaId], height);
                }
            }

            for (byte areaId = 1; areaId <= maxAreaId; areaId++)
            {
                if (!CanBuild(locationLists[areaId][0]))
                {
                    var topAndBottomNeighborIds =
                        neighbors
                        .Where(pair => pair.Item1 == areaId || pair.Item2 == areaId)
                        .Select(pair => pair.Item1 == areaId ? pair.Item2 : pair.Item1).ToList();

                    if (topAndBottomNeighborIds.Count != 2)
                    {
                        // This isn't really a ramp. Using the 'Edge' class for miscellaneous non-buildable
                        // areas for now. This does assume that an Edge won't connect to a Ramp.
                        var neighborMesas = topAndBottomNeighborIds.Select(id => mesas[id]).ToArray();
                        edges[areaId] = new Edge(areaId, locationLists[areaId], centers[areaId], neighborMesas);

                        foreach (var neighborMesa in neighborMesas)
                        {
                            neighborMesa.AddNeighbor(edges[areaId]);
                        }
                    }
                    else
                    {
                        var topAndBottomNeighbors = topAndBottomNeighborIds.Select(id => mesas[id]).OrderBy(mesa => mesa.Height).ToArray();

                        ramps[areaId] = new Ramp(areaId, locationLists[areaId], centers[areaId], topAndBottomNeighbors[1], topAndBottomNeighbors[0]);
                        mesas[topAndBottomNeighborIds[0]].AddNeighbor(ramps[areaId]);
                        mesas[topAndBottomNeighborIds[1]].AddNeighbor(ramps[areaId]);
                    }
                }
            }

            // At some point there might be 'mesas' that are adjacent, because we want to
            // subdivide large areas with multiple mining locations even if there's no ramp.
            foreach (var neighbor in neighbors)
            {
                if (mesas.ContainsKey(neighbor.Item1) && mesas.ContainsKey(neighbor.Item2))
                {
                    mesas[neighbor.Item1].AddNeighbor(mesas[neighbor.Item2]);
                    mesas[neighbor.Item2].AddNeighbor(mesas[neighbor.Item1]);
                }
            }

            return(mesas.Values.Concat <Area>(ramps.Values).ToList());
        }
Пример #12
0
        public Map(Map prior, List <Unit> units, Translator translator, Dictionary <uint, UnitTypeData> unitTypes, ImageData creep)
        {
            this.playerStartLocation = prior.playerStartLocation;
            this.enemyStartLocation  = prior.enemyStartLocation;
            this.Raw  = prior.Raw;
            this.Size = prior.Size;
            this.placementGridOriginal = prior.placementGridOriginal;
            this.placementGrid         = new MapArray <byte>(this.placementGridOriginal);
            this.pathingGrid           = prior.pathingGrid;
            this.heightGrid            = prior.heightGrid;
            this.padding   = prior.padding;
            this.creepGrid = new MapArray <byte>(creep.Data.ToByteArray(), this.Size);

            this.structuresAndDeposits     = new MapArray <Unit>(this.Size);
            this.structuresAndDepositsList = new List <Unit>();
            this.structurePadding          = new MapArray <bool>(this.Size);
            this.resourcePadding           = new MapArray <bool>(this.Size);

            foreach (var unit in units)
            {
                var unitType = unitTypes[unit.Raw.UnitType];
                if (unitType.Attributes.Contains(Proto.Attribute.Structure))
                {
                    var structureSize = translator.GetStructureSize(unit);
                    var originX       = (int)Math.Round(unit.X - structureSize.X * 0.5f);
                    var originY       = (int)Math.Round(unit.Y - structureSize.Y * 0.5f);
                    var origin        = new Location {
                        X = originX, Y = originY
                    };

                    BuildingType buildingType = (unit.Type?.IsBuildingType ?? false) ? (BuildingType)unit.Type : null;

                    this.structuresAndDepositsList.Add(unit);
                    StoreBuildingLocation(origin, structureSize, unit, buildingType);
                }

                // Check unit orders to find planned buildings
                if (unit.IsWorker)
                {
                    var currentlyBuilding = translator.CurrentlyBuilding(unit);

                    if (currentlyBuilding != null &&
                        currentlyBuilding.IsBuildingType &&
                        currentlyBuilding != TerranBuildingType.Refinery &&
                        currentlyBuilding != ProtossBuildingType.Assimilator &&
                        currentlyBuilding != ZergBuildingType.Extractor)
                    {
                        var building     = (BuildingType)currentlyBuilding;
                        var buildingSize = translator.GetBuildingSize(building);
                        var targetX      = (int)Math.Round(unit.Raw.Orders[0].TargetWorldSpacePos.X - (buildingSize.X * 0.5f));
                        var targetY      = (int)Math.Round(unit.Raw.Orders[0].TargetWorldSpacePos.Y - (buildingSize.Y * 0.5f));

                        var targetLocation = new Location {
                            X = targetX, Y = targetY
                        };

                        StoreBuildingLocation(targetLocation, buildingSize, null, building);
                    }
                }
            }

            if (!this.playerStartLocation.HasValue)
            {
                var mainBase = units.Single(u => u.Raw.Alliance == Alliance.Self && u.IsMainBase);
                this.playerStartLocation = new Location {
                    X = (int)mainBase.X, Y = (int)mainBase.Y
                };
            }
        }