public void DistributeYieldAndResources(HomelandData homelandData, IMapTemplate mapTemplate) { LuxuryDistributor.DistributeLuxuriesAcrossHomeland(homelandData); StrategicDistributor.DistributeStrategicsAcrossHomeland(homelandData); HomelandBalancer.BalanceHomelandYields(homelandData); }
private void DrawForbiddenLine( HashSet <MapSection> unassignedSections, HashSet <MapSection> forbiddenSections, GridPartition partition, IMapTemplate mapTemplate ) { int lineXCoord = Mathf.RoundToInt(Grid.CellCountX * UnityEngine.Random.Range( mapTemplate.ContinentSeparationLineXMin, mapTemplate.ContinentSeparationLineXMax )); IHexCell startCell = Grid.GetCellAtCoordinates(HexCoordinates.FromOffsetCoordinates(lineXCoord, 0)); IHexCell endCell = Grid.GetCellAtCoordinates(HexCoordinates.FromOffsetCoordinates(lineXCoord, Grid.CellCountZ - 1)); var cellLine = Grid.GetCellsInLine(startCell, endCell); var sectionsOnLine = cellLine.Select(cell => partition.GetSectionOfCell(cell)).Distinct(); var sectionsToForbid = sectionsOnLine.SelectMany(section => partition.GetNeighbors(section)) .Concat(sectionsOnLine).Distinct(); foreach (var section in sectionsToForbid) { unassignedSections.Remove(section); forbiddenSections.Add(section); } }
public void GenerateTopologyAndEcology(HomelandData homelandData, IMapTemplate mapTemplate) { var regions = homelandData.OtherRegions.ToList(); regions.Add(homelandData.StartingRegion); int riveredCells = 0; foreach (var region in regions) { var regionData = homelandData.GetDataOfRegion(region); RegionGenerator.GenerateTopology(region, regionData.Topology); RegionGenerator.PaintTerrain(region, regionData.Biome); riveredCells += Mathf.CeilToInt(region.LandCells.Count * regionData.Biome.RiverPercentage * 0.01f); } var allLandCells = regions.SelectMany(region => region.LandCells); var allWaterCells = regions.SelectMany(region => region.WaterCells); RiverGenerator.CreateRivers(allLandCells, allWaterCells, riveredCells); RegionGenerator.AssignFloodPlains(allLandCells); foreach (var region in regions) { var regionData = homelandData.GetDataOfRegion(region); VegetationPainter.PaintVegetation(region, regionData.Biome); } }
public void GenerateMap(IMapTemplate template, IMapGenerationVariables variables) { if (GenerateMapCoroutine == null) { GenerateMapCoroutine = CoroutineInvoker.StartCoroutine(GenerateMap_Perform(template, variables)); } }
private int GetBorderAvoidanceWeight(IHexCell cell, IMapTemplate template) { int distanceIntoBorderX = 0, distanceIntoBorderZ = 0; var xOffset = HexCoordinates.ToOffsetCoordinateX(cell.Coordinates); var zOffset = HexCoordinates.ToOffsetCoordinateZ(cell.Coordinates); if (xOffset < template.SoftMapBorderX) { distanceIntoBorderX = template.SoftMapBorderX - xOffset; } else if (Grid.CellCountX - xOffset < template.SoftMapBorderX) { distanceIntoBorderX = template.SoftMapBorderX - (Grid.CellCountX - xOffset); } if (zOffset < template.SoftMapBorderZ) { distanceIntoBorderZ = template.SoftMapBorderZ - zOffset; } else if (Grid.CellCountZ - zOffset < template.SoftMapBorderZ) { distanceIntoBorderZ = template.SoftMapBorderZ - (Grid.CellCountZ - zOffset); } return(-(distanceIntoBorderX + distanceIntoBorderZ) * template.SoftBorderAvoidanceWeight); }
private ExpansionWeightFunction GetExpansionWeightFunction( GridPartition partition, IMapTemplate template ) { var middleCell = Grid.GetCellAtOffset(Grid.CellCountX / 2, Grid.CellCountZ / 2); return(delegate(MapSection section, List <MapSection> chunk) { if (IsWithinHardBorder(section.CentroidCell, template)) { return 0; } int borderAvoidanceWeight = GetBorderAvoidanceWeight(section.CentroidCell, template); int neighborsInContinent = partition.GetNeighbors(section).Intersect(chunk).Count(); int neighborsInContinentWeight = neighborsInContinent * template.NeighborsInContinentWeight; int distanceFromSeedCentroid = Grid.GetDistance(chunk[0].CentroidCell, section.CentroidCell); int distanceFromSeedCentroidWeight = distanceFromSeedCentroid * template.DistanceFromSeedCentroidWeight; int distanceFromMapCenter = Grid.GetDistance(section.CentroidCell, middleCell) * template.DistanceFromMapCenterWeight; int weight = 1 + Math.Max( 0, neighborsInContinentWeight + distanceFromSeedCentroidWeight + borderAvoidanceWeight + distanceFromMapCenter ); return weight; }); }
private void PaintMap( OceanAndContinentData oceansAndContinents, IMapTemplate mapTemplate ) { var nonBarbarianCivs = CivFactory.AllCivilizations.Where(civ => !civ.Template.IsBarbaric).ToList(); foreach (var civ in nonBarbarianCivs) { var homeland = oceansAndContinents.HomelandDataForCiv[civ]; HomelandGenerator.GenerateTopologyAndEcology(homeland, mapTemplate); } OceanGenerator.GenerateTopologyAndEcology(oceansAndContinents.OceanData); WaterRationalizer.RationalizeWater(Grid.Cells); foreach (var civ in nonBarbarianCivs) { var homeland = oceansAndContinents.HomelandDataForCiv[civ]; HomelandGenerator.DistributeYieldAndResources(homeland, mapTemplate); } OceanGenerator.DistributeYieldAndResources(oceansAndContinents.OceanData); }
public OceanData GetOceanData( IEnumerable <MapSection> oceanSections, IOceanTemplate oceanTemplate, IMapTemplate mapTemplate, GridPartition partition ) { var shallowOceanSections = oceanSections.Where( section => partition.GetNeighbors(section).Any(neighbor => !oceanSections.Contains(neighbor)) ).ToList(); var midOceanSections = oceanSections.Except(shallowOceanSections).Where( section => partition.GetNeighbors(section).Any(neighbor => shallowOceanSections.Contains(neighbor)) ).ToList(); var deepOceanSections = oceanSections.Except(shallowOceanSections).Except(midOceanSections); List <MapRegion> emptyOceanRegions; List <MapRegion> archipelagoRegions; List <RegionData> archipelagoRegionData; CarveArchipelagoesFromOcean( shallowOceanSections, midOceanSections, deepOceanSections, oceanTemplate, partition, mapTemplate, out emptyOceanRegions, out archipelagoRegions, out archipelagoRegionData ); return(new OceanData(emptyOceanRegions, archipelagoRegions, archipelagoRegionData)); }
private bool IsWithinSoftBorder(IHexCell cell, IMapTemplate template) { var xOffset = HexCoordinates.ToOffsetCoordinateX(cell.Coordinates); var zOffset = HexCoordinates.ToOffsetCoordinateZ(cell.Coordinates); return(xOffset <= template.SoftMapBorderX || Grid.CellCountX - xOffset <= template.SoftMapBorderX || zOffset <= template.SoftMapBorderZ || Grid.CellCountZ - zOffset <= template.SoftMapBorderZ); }
public void Reset(IMapTemplate newTemplate) { TemperatureOfCell.Clear(); PrecipitationOfCell.Clear(); HasSetSampleRegion = false; PrecipitationTexture = newTemplate.PrecipitationTexture; }
public IRegionBiomeTemplate GetBiomeForLandRegion(MapRegion landRegion, IMapTemplate mapTemplate) { float regionTemperature = GetRegionTemperature(landRegion); float regionPrecipitation = GetRegionPrecipitation(landRegion); var templatesByPriority = new List <IRegionBiomeTemplate>(mapTemplate.RegionBiomes); var templatePriority = new Dictionary <IRegionBiomeTemplate, float>(); foreach (var regionTemplate in templatesByPriority) { templatePriority[regionTemplate] = GetBiomePriority(regionTemplate, regionTemperature, regionPrecipitation); } templatesByPriority.Sort((a, b) => templatePriority[a].CompareTo(templatePriority[b])); return(templatesByPriority.First()); }
private OceanAndContinentData GenerateOceansAndContinents(IMapTemplate mapTemplate, IMapGenerationVariables variables) { var nonBarbarianCivs = CivFactory.AllCivilizations.Where(civ => !civ.Template.IsBarbaric).ToList(); var partition = GridPartitionLogic.GetPartitionOfGrid(Grid, mapTemplate); int totalLandCells = Mathf.RoundToInt(Grid.Cells.Count * variables.ContinentalLandPercentage * 0.01f); int landCellsPerCiv = Mathf.RoundToInt((float)totalLandCells / nonBarbarianCivs.Count); HashSet <MapSection> unassignedSections = new HashSet <MapSection>(partition.Sections); List <List <MapSection> > homelandChunks = SubdivisionLogic.DivideSectionsIntoChunks( unassignedSections, partition, nonBarbarianCivs.Count, landCellsPerCiv, mapTemplate.MinStartingLocationDistance, GetExpansionWeightFunction(partition, mapTemplate), mapTemplate ); var homelandOfCivs = new Dictionary <ICivilization, HomelandData>(); for (int i = 0; i < nonBarbarianCivs.Count; i++) { var civ = nonBarbarianCivs[i]; var landSections = homelandChunks[i]; var waterSections = GetCoastForLandSection(landSections, unassignedSections, partition); var homelandTemplate = TemplateSelectionLogic.GetHomelandTemplateForCiv(civ, mapTemplate); homelandOfCivs[civ] = HomelandGenerator.GetHomelandData( civ, landSections, waterSections, partition, homelandTemplate, mapTemplate ); } var oceanTemplate = mapTemplate.OceanTemplates.Random(); var oceanData = OceanGenerator.GetOceanData(unassignedSections, oceanTemplate, mapTemplate, partition); return(new OceanAndContinentData() { HomelandDataForCiv = homelandOfCivs, OceanData = oceanData }); }
private IEnumerator GenerateMap_Perform(IMapTemplate template, IMapGenerationVariables variables) { MapComposer.ClearRuntime(false); while (MapComposer.IsProcessing) { yield return(new WaitForEndOfFrame()); } Profiler.BeginSample("GenerateMap()"); CellClimateLogic.Reset(template); var oldRandomState = SetRandomState(); Grid.Build(variables.CellCountX, variables.CellCountZ); GeneratePlayers(variables); var oceansAndContinents = GenerateOceansAndContinents(template, variables); PaintMap(oceansAndContinents, template); foreach (var civ in oceansAndContinents.HomelandDataForCiv.Keys) { var civHomeland = oceansAndContinents.HomelandDataForCiv[civ]; StartingUnitPlacementLogic.PlaceStartingUnitsInRegion( civHomeland.StartingRegion, civ, template ); } UnityEngine.Random.state = oldRandomState; GenerateMapCoroutine = null; foreach (var chunk in Grid.Chunks) { chunk.Refresh(MapRendering.TerrainRefreshType.All); } Profiler.EndSample(); }
private Func <MapSection, int> GetArchipelagoWeightFunction(IMapTemplate template) { return(delegate(MapSection section) { int centroidCellX = HexCoordinates.ToOffsetCoordinateX(section.CentroidCell.Coordinates); int centroidCellZ = HexCoordinates.ToOffsetCoordinateZ(section.CentroidCell.Coordinates); if (centroidCellX < template.HardMapBorderX || Grid.CellCountX - centroidCellX < template.HardMapBorderX || centroidCellZ < template.HardMapBorderZ || Grid.CellCountZ - centroidCellZ < template.HardMapBorderZ ) { return 0; } else { return 1; } }); }
public void PlaceStartingUnitsInRegion( MapRegion region, ICivilization owner, IMapTemplate mapTemplate ) { if (owner.Template.IsBarbaric) { return; } var centralLocation = GetBestStartingCell(region); if (centralLocation == null) { throw new InvalidOperationException("Failed to find an appropriate central location"); } UnitFactory.BuildUnit(centralLocation, mapTemplate.StartingUnits[0], owner); for (int i = 1; i < mapTemplate.StartingUnits.Count; i++) { var unitTemplate = mapTemplate.StartingUnits[i]; var location = Grid.GetCellsInRadius(centralLocation, 2).Where( cell => !UnitPositionCanon.GetPossessionsOfOwner(cell).Any() && UnitFactory.CanBuildUnit(cell, unitTemplate, owner) && cell != null ).FirstOrDefault(); if (location == null) { throw new InvalidOperationException( string.Format("Failed to place starting unit {0} for civ {1}", unitTemplate, owner) ); } else { UnitFactory.BuildUnit(location, unitTemplate, owner); } } }
private void CarveArchipelagoesFromOcean( IEnumerable <MapSection> shallowOceanSections, List <MapSection> midOceanSections, IEnumerable <MapSection> deepOceanSections, IOceanTemplate oceanTemplate, GridPartition partition, IMapTemplate mapTemplate, out List <MapRegion> emptyOceanRegions, out List <MapRegion> archipelagoRegions, out List <RegionData> archipelagoRegionData ) { int landSectionCount = Mathf.RoundToInt(oceanTemplate.DeepOceanLandPercentage * 0.01f * deepOceanSections.Count()); var deepOceanLand = MapSectionRandomSampler.SampleElementsFromSet( deepOceanSections, landSectionCount, GetArchipelagoWeightFunction(mapTemplate) ).ToList(); var deepOceanWater = deepOceanSections.Except(deepOceanLand).ToList(); List <MapSection> emptyDeepOcean; GetArchipelagoesFromLandAndWater( deepOceanLand, deepOceanWater, midOceanSections, oceanTemplate, partition, out archipelagoRegions, out emptyDeepOcean ); var emptyOceanCells = shallowOceanSections.Concat(midOceanSections).Concat(emptyDeepOcean); var emptyOcean = new MapRegion( new List <IHexCell>(), emptyOceanCells.SelectMany(section => section.Cells).ToList() ); emptyOceanRegions = new List <MapRegion>() { emptyOcean }; archipelagoRegionData = archipelagoRegions.Select(region => new RegionData( TemplateSelectionLogic.GetBiomeForLandRegion(region, mapTemplate), TemplateSelectionLogic.GetTopologyForLandRegion(region, mapTemplate), AvailableBalanceStrategies )).ToList(); }
private MapSection GetStartingSection( HashSet <MapSection> unassignedSections, IEnumerable <MapSection> startingSections, int minSeparation, IMapTemplate mapTemplate ) { var candidates = new List <MapSection>(); foreach (var unassignedSection in unassignedSections) { if (unassignedSection.Cells.Count == 0 || IsWithinSoftBorder(unassignedSection.CentroidCell, mapTemplate)) { continue; } bool unassignedIsValid = true; foreach (var startingSection in startingSections) { if (Grid.GetDistance(unassignedSection.CentroidCell, startingSection.CentroidCell) < minSeparation) { unassignedIsValid = false; break; } } if (unassignedIsValid) { candidates.Add(unassignedSection); } } if (candidates.Count == 0) { throw new InvalidOperationException("Failed to acquire a valid starting location"); } else { return(candidates.Random()); } }
public HomelandData GetHomelandData( ICivilization civ, List <MapSection> landSections, List <MapSection> waterSections, GridPartition partition, IHomelandTemplate homelandTemplate, IMapTemplate mapTemplate ) { var landChunks = new List <List <MapSection> >(); var waterChunks = new List <List <MapSection> >(); IHexCell seedCentroid = landSections[0].CentroidCell; var startingLandSections = Grid.GetCellsInRadius(seedCentroid, homelandTemplate.StartingRegionRadius) .Select(cell => partition.GetSectionOfCell(cell)) .Distinct() .Intersect(landSections); var startingWaterSections = Grid.GetCellsInRadius(seedCentroid, homelandTemplate.StartingRegionRadius) .Select(cell => partition.GetSectionOfCell(cell)) .Distinct() .Intersect(waterSections); var startingRegion = new MapRegion( startingLandSections.SelectMany(section => section.Cells).ToList(), startingWaterSections.SelectMany(section => section.Cells).ToList() ); foreach (var cell in startingRegion.Cells) { cell.SetMapData(1f); } var unassignedLand = landSections.Except(startingLandSections); var unassignedWater = waterSections.Except(startingWaterSections); DivideSectionsIntoRectangularChunks( unassignedLand, unassignedWater, homelandTemplate.RegionCount, out landChunks, out waterChunks ); var regions = new List <MapRegion>(); regions.Add(startingRegion); for (int i = 0; i < landChunks.Count; i++) { var region = new MapRegion( landChunks [i].SelectMany(section => section.Cells).ToList(), waterChunks[i].SelectMany(section => section.Cells).ToList() ); regions.Add(region); } var startingData = new RegionData( TemplateSelectionLogic.GetBiomeForLandRegion(startingRegion, mapTemplate), TemplateSelectionLogic.GetTopologyForLandRegion(startingRegion, mapTemplate), AvailableBalanceStrategies ); var otherRegions = regions.Where(region => region != startingRegion).ToList(); var otherData = otherRegions.Select(region => new RegionData( TemplateSelectionLogic.GetBiomeForLandRegion(region, mapTemplate), TemplateSelectionLogic.GetTopologyForLandRegion(region, mapTemplate), AvailableBalanceStrategies )).ToList(); return(new HomelandData( startingRegion, startingData, otherRegions, otherData, homelandTemplate.LuxuryResourceData, homelandTemplate.YieldAndResources )); }
public List <List <MapSection> > DivideSectionsIntoChunks( HashSet <MapSection> unassignedSections, GridPartition partition, int chunkCount, int maxCellsPerChunk, int minSeedSeparation, ExpansionWeightFunction weightFunction, IMapTemplate mapTemplate ) { if (chunkCount > unassignedSections.Count()) { throw new ArgumentOutOfRangeException("Not enough sections to assign at least one per chunk"); } var finishedChunks = new List <List <MapSection> >(); var unfinishedChunks = new List <List <MapSection> >(); var startingSections = new List <MapSection>(); var forbiddenSections = new HashSet <MapSection>(); if (mapTemplate.SeparateContinents) { DrawForbiddenLine(unassignedSections, forbiddenSections, partition, mapTemplate); } for (int i = 0; i < chunkCount; i++) { var startingSection = GetStartingSection( unassignedSections, startingSections, minSeedSeparation, mapTemplate ); unfinishedChunks.Add(new List <MapSection>() { startingSection }); startingSections.Add(startingSection); unassignedSections.Remove(startingSection); } int desiredContiguousCells = Mathf.RoundToInt(maxCellsPerChunk * mapTemplate.HomelandContiguousPercentage * 0.01f); while (unfinishedChunks.Count > 0 && unassignedSections.Count > 0) { var chunk = unfinishedChunks.Random(); int cellCount = chunk.Sum(section => section.Cells.Count); bool mustBeContiguous = desiredContiguousCells > cellCount; if (TryExpandChunk(chunk, unassignedSections, partition, mapTemplate, weightFunction, mustBeContiguous)) { int cellsInChunk = chunk.Sum(section => section.Cells.Count); if (cellsInChunk >= maxCellsPerChunk) { finishedChunks.Add(chunk); unfinishedChunks.Remove(chunk); } } else { Debug.LogWarning("Failed to assign a new section to a chunk"); finishedChunks.Add(chunk); unfinishedChunks.Remove(chunk); } } finishedChunks.AddRange(unfinishedChunks); foreach (var section in forbiddenSections) { unassignedSections.Add(section); } return(finishedChunks); }
public IRegionTopologyTemplate GetTopologyForLandRegion(MapRegion region, IMapTemplate mapTemplate) { return(mapTemplate.RegionTopologies.Random()); }
private bool TryExpandChunk( List <MapSection> chunk, HashSet <MapSection> unassignedSections, GridPartition partition, IMapTemplate mapTemplate, ExpansionWeightFunction weightFunction, bool mustBeContiguous ) { expansionCandidates.Clear(); foreach (MapSection section in chunk) { nearbySections.Clear(); sectionsWithinDistance.Clear(); if (mustBeContiguous) { foreach (var adjacentSection in partition.GetNeighbors(section)) { if (unassignedSections.Contains(adjacentSection)) { nearbySections.Add(adjacentSection); } } } else { foreach (IHexCell nearbyCell in Grid.GetCellsInRadius(section.CentroidCell, mapTemplate.HomelandExpansionMaxCentroidSeparation)) { MapSection sectionOfCell = partition.GetSectionOfCell(nearbyCell); if (unassignedSections.Contains(sectionOfCell)) { sectionsWithinDistance.Add(sectionOfCell); } } var sectionsSurroundedByUnassigned = sectionsWithinDistance.Where( nearby => partition.GetNeighbors(nearby).Count(neighbor => !unassignedSections.Contains(neighbor)) <= 0 ); if (sectionsSurroundedByUnassigned.Any()) { foreach (var surroundedSection in sectionsSurroundedByUnassigned) { nearbySections.Add(surroundedSection); } } else { foreach (var sectionWithin in sectionsWithinDistance) { nearbySections.Add(sectionWithin); } } } foreach (var nearby in nearbySections) { expansionCandidates.Add(nearby); } } if (expansionCandidates.Count > 0) { var newSection = MapSectionRandomSampler.SampleElementsFromSet( expansionCandidates, 1, section => weightFunction(section, chunk) ).FirstOrDefault(); if (newSection != null) { unassignedSections.Remove(newSection); chunk.Add(newSection); return(true); } } return(false); }
public IHomelandTemplate GetHomelandTemplateForCiv(ICivilization civ, IMapTemplate mapTemplate) { return(mapTemplate.HomelandTemplates.Random()); }
//Creates something akin to a Voronoi diagram of the cells, //using a process similiar to Lloyd's algorithm to make the //shapes more regular public GridPartition GetPartitionOfGrid(IHexGrid grid, IMapTemplate template) { float xMin = 0f, zMin = 0f; float xMax = (grid.CellCountX + grid.CellCountZ * 0.5f - grid.CellCountZ / 2) * RenderConfig.InnerRadius * 2f; float zMax = grid.CellCountZ * RenderConfig.OuterRadius * 1.5f; var randomPoints = new List <Vector3>(); var regionOfPoint = new Dictionary <Vector3, MapSection>(); for (int i = 0; i < template.VoronoiPointCount; i++) { var randomPoint = new Vector3( UnityEngine.Random.Range(xMin, xMax), 0f, UnityEngine.Random.Range(zMin, zMax) ); regionOfPoint[randomPoint] = new MapSection(grid); randomPoints.Add(randomPoint); } int iterationsLeft = template.VoronoiPartitionIterations; while (iterationsLeft > 0) { foreach (var cell in grid.Cells) { Vector3 nearestPoint = Vector3.zero; float shorestDistance = float.MaxValue; foreach (var voronoiPoint in regionOfPoint.Keys) { float distanceTo = Vector3.Distance(cell.GridRelativePosition, voronoiPoint); if (distanceTo < shorestDistance) { nearestPoint = voronoiPoint; shorestDistance = distanceTo; } } if (regionOfPoint.ContainsKey(nearestPoint)) { regionOfPoint[nearestPoint].AddCell(cell); } } if (--iterationsLeft > 0) { randomPoints.Clear(); var newRegionOfPoints = new Dictionary <Vector3, MapSection>(); foreach (var region in regionOfPoint.Values) { if (region.Cells.Count > 0) { randomPoints.Add(region.Centroid); newRegionOfPoints[region.Centroid] = new MapSection(grid); } } regionOfPoint = newRegionOfPoints; } } return(new GridPartition(regionOfPoint.Values, grid)); }
public void UpdateSelectedMapTemplate(int optionIndex) { SelectedMapTemplate = MapTemplates.Where( template => template.name.Equals(MapTypeDropdown.options[optionIndex].text) ).FirstOrDefault(); }