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 }; } }
// Note: includes natural structures, which is why the argument type is 'Unit'. // Also the 'structure' arg can be optional for unbuilt structures, so a separate type is required. private void StoreBuildingLocation(Location origin, Size2DI size, Unit structure, BuildingType type) { for (var x = origin.X; x < origin.X + size.X; x++) { for (var y = origin.Y; y < origin.Y + size.Y; y++) { structuresAndDeposits[x, y] = structure; SetAdjacentSpaces(structurePadding, x, y); if (structure != null && (structure.IsMineralDeposit || structure.IsVespeneGeyser || structure.IsVespeneBuilding)) { SetAdjacentSpaces(resourcePadding, x, y, 3); } // Reserve space for Tech Labs if (type == TerranBuildingType.Barracks || type == TerranBuildingType.Factory || type == TerranBuildingType.Starport) { // Note: I'm not sure if this will come up, but this could theoretically go off the edge of the map StoreBuildingLocation(new Location { X = origin.X + 3, Y = origin.Y }, new Size2DI { X = 2, Y = 2 }, null, TerranBuildingType.TechLab); } } } }