public IEnumerator Generate() { yield return(StartCoroutine(UpdateProgress(0f, "Seeding..."))); int tries = 0; do { Reseed(); yield return(StartCoroutine(UpdateProgress(0.05f, "Cleaning Old Data..."))); tries++; // Clear the existing tiles Tiles.Clear(); foreach (Transform t in transform.GetComponentInChildren <Transform>()) { if (t != transform) { Destroy(t.gameObject); } } yield return(StartCoroutine(UpdateProgress(0.1f, "Creating Mountain Ranges..."))); // Generate a number of mountain ranges from which to propagate the rest of the landmass // Get the number of ranges to generate int numToGenerate = Mathf.RoundToInt(Math.RandomNormal(meanInitialTileCount, stdDevInitialTileCount, Seed, 2, 5)); List <HexAddress> used = new List <HexAddress>(); List <HexTile> initialHexes = new List <HexTile>(); List <HexTile> currentRange = new List <HexTile>(); islandMap = new DataMap(Width, Height); for (int i = 0; i < numToGenerate; i++) { // Reset the current range. currentRange.Clear(); // Generate two mountains as the start and end of the range. for (int j = 0; j < 2; j++) { HexAddress a = new HexAddress(Random.Range(Width / 4, Width - (Width / 4)), Random.Range(Height / 4, Height - (Height / 4))); do { a = new HexAddress(Random.Range(Width / 4, Width - (Width / 4)), Random.Range(Height / 4, Height - (Height / 4))); } while (used.Contains(a)); HexTile h = GenerateTile(a, TileType.TALL_MOUNTAIN); used.Add(a); initialHexes.Add(h); currentRange.Add(h); } HexAddress currentAddress = currentRange[0].Address; HexAddress targetAddress = currentRange[1].Address; islandMap.FocalPoints.Add(currentAddress); islandMap.FocalPoints.Add(targetAddress); // While the range is not complete... while (currentAddress != targetAddress) { // Get the desired direction toward the address of currentRange[1]. Vector2Int d = new Vector2Int(); // If the end tile is above this one, add 1 to y, if it is below subtract 1 from y, else do nothing. if (targetAddress.X > currentAddress.X) { d.x++; } else if (targetAddress.X < currentAddress.X) { d.x--; } // If the end tile is to the right of this one, add 1 to x, if it is to the left subrtact 1, else do nothing. if (targetAddress.Y > currentAddress.Y) { d.y++; } else if (targetAddress.Y < currentAddress.Y) { d.y--; } // Make sure we stay adjacent to the current address if (d.y == d.x && d.x + d.y != 0) { if (Random.Range(0f, 1.0f) >= 0.5f) { d.y = 0; } else { d.x = 0; } } // Set currentAddress to the adjacent tile in the desired direction, plus a random factor. for (int v = 0; v < 6; v++) { if (HexFunctions.Instance.NeighborIndices[v].Equals(d)) { int index = (v + Random.Range(-1, 2)); index = (index % 6 + 6) % 6; d = HexFunctions.Instance.NeighborIndices[index]; break; } } // The random factor can slightly alter the direction. currentAddress += new HexAddress(d.x, d.y); if (currentAddress.Equals(targetAddress)) { break; } if (Tiles.ContainsKey(currentAddress)) { continue; } // Generate a tall mountain or regular mountain tile at currentAddress. HexTile h = GenerateTile( currentAddress, Random.Range(0f, 1.0f) < tallMountainChance ? TileType.TALL_MOUNTAIN : TileType.MOUNTAIN ); used.Add(currentAddress); initialHexes.Add(h); // Add the generated tile to currentRange. currentRange.Add(h); if (used.Count % 3 == 0) { islandMap.FocalPoints.Add(h.Address); } } // Have a chance to delete one of the tiles in the range. if (Random.Range(0f, 1f) <= rangeGapChance) { int index = Random.Range(0, currentRange.Count); Destroy(Tiles[currentRange[index].Address].gameObject); Tiles.Remove(currentRange[index].Address); } } yield return(StartCoroutine(UpdateProgress(0.3f, "Generating Coastline..."))); // Generate the coastline islandMap.GenerateCoastMap(); yield return(StartCoroutine(UpdateProgress(0.5f, "Blocking Out Island..."))); // Fill out the coast with grassland to start foreach (KeyValuePair <HexAddress, float> kvp in islandMap.Data) { if (islandMap.Data[kvp.Key] >= MapCutoff && !Tiles.ContainsKey(kvp.Key)) { GenerateTile(kvp.Key, TileType.GRASSLAND); } } } while (tries < GenerationRetryMax && ((float)Tiles.Count / (float)(Width * Height)) < MapSizeTarget); // iterate over the tiles, choosing a new type based on the surroundings of each // At this point in generation, Tiles contains all the tiles the island will have. // Generate forests. yield return(StartCoroutine(UpdateProgress(0.6f, "Generating Forests..."))); vegetationMap = new DataMap(Width, Height); vegetationMap.GenerateVegetationMap(); foreach (KeyValuePair <HexAddress, float> kvp in vegetationMap.Data) { if (Tiles.ContainsKey(kvp.Key) && (Tiles[kvp.Key].Type != TileType.MOUNTAIN && Tiles[kvp.Key].Type != TileType.TALL_MOUNTAIN)) { if (vegetationMap.Data[kvp.Key] >= VegetationDenseCutoff) { Tiles[kvp.Key].Type = TileType.DENSE_FOREST; } else if (vegetationMap.Data[kvp.Key] >= VegetationNormalCutoff) { Tiles[kvp.Key].Type = TileType.FOREST; } else if (vegetationMap.Data[kvp.Key] >= VegetationSparseCutoff) { Tiles[kvp.Key].Type = TileType.SPARSE_FOREST; } } } // Generate forested areas. yield return(StartCoroutine(UpdateProgress(0.7f, "Generating Highlands..."))); foreach (KeyValuePair <HexAddress, HexTile> kvp in Tiles) { int n = HexFunctions.Instance.AdjacentTilesOfTypes(kvp.Value.Address, TileType.MOUNTAIN, TileType.TALL_MOUNTAIN, TileType.HIGHLAND); if ((n > 0)) { if (Random.Range(0f, 1.0f) < n / HighlandDenominator && HexFunctions.Instance.IsViableForFarm(kvp.Key)) { Tiles[kvp.Key].Type = TileType.HIGHLAND; } } } // Generate oceans, lakes, and small islands thereupon. yield return(StartCoroutine(UpdateProgress(0.8f, "Generating Ocean..."))); oceanMap = new DataMap(Width, Height); oceanMap.GenerateOceanMap(); foreach (KeyValuePair <HexAddress, float> kvp in oceanMap.Data) { // Debug.Log(kvp.Value); if (!Tiles.ContainsKey(kvp.Key)) { if (kvp.Value == 1.0f) { if (Random.Range(0f, 1f) < IslandRate) { GenerateTile(kvp.Key, TileType.OCEAN_ISLAND); } else { GenerateTile(kvp.Key, TileType.OCEAN); } } } } // Generate special areas where people live. yield return(StartCoroutine(UpdateProgress(0.85f, "Generating Civilization..."))); // Spawn the stronghold with some farms foreach (KeyValuePair <HexAddress, HexTile> kvp in Tiles) { if (HexFunctions.Instance.IsViableForFarm(kvp.Value.Address)) { // Spawn the stronghold, farms, and break kvp.Value.Type = TileType.STRONGHOLD; foreach (MercenaryData merc in MercenaryController.Instance.Mercenaries) { merc.SetLocation(kvp.Value); } MercenaryController.Instance.UpdateLocationPins(); foreach (Vector2Int n in HexFunctions.Instance.NeighborIndices) { if (farmlandRate > Random.Range(0f, 1f) && Tiles.ContainsKey(n + kvp.Value.Address) && HexFunctions.Instance.IsViableForFarm(n + kvp.Value.Address)) { Tiles[n + kvp.Value.Address].Type = TileType.FARMLAND; } } break; } } // Spawn some villages with farms foreach (KeyValuePair <HexAddress, HexTile> kvp in Tiles) { if (Random.Range(0f, 1f) < villageRate && HexFunctions.Instance.IsViableForFarm(kvp.Value.Address)) { // Spawn the stronghold and break kvp.Value.Type = TileType.VILLAGE; foreach (Vector2Int n in HexFunctions.Instance.NeighborIndices) { if (farmlandRate > Random.Range(0f, 1f) && Tiles.ContainsKey(n + kvp.Value.Address) && HexFunctions.Instance.IsViableForFarm(n + kvp.Value.Address)) { Tiles[n + kvp.Value.Address].Type = TileType.FARMLAND; } } break; } } // Generate the dungeons int numDungeons = Mathf.RoundToInt(Math.RandomHalfNormal(dungeonCountMean, dungeonCountStdDev, Seed)); while (numDungeons > 0) { foreach (KeyValuePair <HexAddress, HexTile> kvp in Tiles) { if (Random.Range(0f, 1f) < dungeonRate && HexFunctions.Instance.IsLand(kvp.Value)) { // Spawn the dungeon Sprite s = HexFunctions.Instance.GetDungeonSprite(kvp.Value.Type); kvp.Value.Type = TileType.DUNGEON; kvp.Value.HexRenderer.sprite = s; numDungeons--; } } } // Generate the hex data layer foreach (KeyValuePair <HexAddress, HexTile> kvp in Tiles) { Sprite s = null; foreach (HexSprite h in HexFunctions.Instance.HexSpriteLibrary) { if (h.Type == kvp.Value.Type) { s = h.SketchedSprite; break; } } kvp.Value.Data = new HexData(s, HexFunctions.Instance.TileTypeToString(kvp.Value.Type), new Resource[0]); kvp.Value.Data.PathNode = new HexPathNode(kvp.Value); } yield return(StartCoroutine(UpdateProgress(0.9f, "Prettyifying..."))); // Additional sprite picking/deobfuscation in special cases. foreach (KeyValuePair <HexAddress, HexTile> kvp in Tiles) { Sprite s = null; switch (kvp.Value.Type) { case TileType.STRONGHOLD: Tiles[kvp.Key].SetTileEffectState(2, true); kvp.Value.HexRenderer.sortingOrder = 1; DeobfuscateRadius(3, kvp.Value); TileSelector.Instance.SetTarget(kvp.Value); kvp.Value.SetTileEffectState(4, true); break; case TileType.VILLAGE: kvp.Value.SetTileEffectState(2, true); break; case TileType.DUNGEON: // BlightRadius(2, kvp.Value); break; case TileType.OCEAN: kvp.Value.SetTileEffectState(1, false); // Change ocean tiles adjacent to land into water tiles. List <HexAddress> toWater = new List <HexAddress>(); if (HexFunctions.Instance.AdjacentLandTiles(kvp.Key) > 0) { toWater.Add(kvp.Key); } foreach (HexAddress a in toWater) { Tiles[a].Type = Random.Range(0f, 1.0f) < IslandRate ? TileType.ISLAND : TileType.WATER; if (Tiles[a].Type == TileType.ISLAND) { Tiles[a].SetTileEffectState(4, true); } else if (Random.Range(0f, 1f) < 0.075f) { kvp.Value.SetTileEffectState(6, true); } } if (kvp.Value.Type == TileType.OCEAN) { if (Random.Range(0f, 1f) < 0.075f) { kvp.Value.SetTileEffectState(6, true); } } break; case TileType.OCEAN_ISLAND: if (HexFunctions.Instance.AdjacentLandTiles(kvp.Key) > 0) { kvp.Value.Type = TileType.ISLAND; kvp.Value.SetTileEffectState(4, true); } break; case TileType.MOUNTAIN: if (HexFunctions.Instance.AdjacentTilesOfTypes(kvp.Key, TileType.MOUNTAIN, TileType.TALL_MOUNTAIN) < 4) { int sp = GetMountainSprite(kvp.Key); if (HexFunctions.Instance.AdjacentTilesOfTypes(kvp.Key, TileType.FOREST, TileType.SPARSE_FOREST, TileType.DENSE_FOREST) > 1) { if (sp != -1) { s = HexFunctions.Instance.AlternateSpriteLibrary[1].Sprites[sp]; } } else { if (sp != -1) { s = HexFunctions.Instance.AlternateSpriteLibrary[0].Sprites[sp]; } } } else if (HexFunctions.Instance.AdjacentLandTiles(kvp.Key) < 6) { s = HexFunctions.Instance.AlternateSpriteLibrary[2].GetSprite(); } kvp.Value.HexRenderer.enabled = true; break; case TileType.TALL_MOUNTAIN: if (HexFunctions.Instance.AdjacentLandTiles(kvp.Key) < 6) { s = HexFunctions.Instance.AlternateSpriteLibrary[3].GetSprite(); } kvp.Value.SetTileEffectState(3, true); kvp.Value.SetTileEffectState(1, false); break; case TileType.NULL: kvp.Value.SetTileEffectState(1, false); break; } if (deobfuscateOnGeneration) { kvp.Value.SetTileEffectState(1, false); } if (s) { kvp.Value.HexRenderer.sprite = s; } if (Random.Range(0f, 1f) < birdSpawnRate) { kvp.Value.SetTileEffectState(4, true); } } yield return(StartCoroutine(UpdateProgress(1.0f, "Done."))); GetComponent <MapScrollEffect>().GetTileList(); // Test the pathfinding // List<HexTile> ts = Enumerable.ToList(Tiles.Values); // // foreach(HexPathNode node in HexFunctions.Instance.GetPathFromTo(ts[Random.Range(0, ts.Count)].Data.PathNode, ts[Random.Range(0, ts.Count)].Data.PathNode)) { // node.Tile.HexRenderer.color = new Color(1, 0, 0); // } yield return(null); }
public HexPathNode(HexTile pTile) { Tile = pTile; }
public float DisplacementX(HexTile tile) { return(tile.Address.ToUnity.x + ((Math.Map(Cam.transform.position.x, Cam.PanBoundsX.x, Cam.PanBoundsX.y, -1f, 1f) * tile.Address.Y * xMagnitude) * Math.Map(Cam.Zoom, Cam.ZoomBounds.x, Cam.ZoomBounds.y, 0f, 1f))); }
public bool IsLand(HexTile tile) { return(LandTypes.Contains(tile.Type)); }
public float DisplacementY(HexTile tile) { return(tile.Address.ToUnity.y - Mathf.Clamp((yMagnitude * (tile.DisplayPosition.y) * tile.Address.Y), 0, float.MaxValue) * Math.Map(Cam.Zoom, Cam.ZoomBounds.x, Cam.ZoomBounds.y, 0f, 1f)); }