public List <Room> Generate(BiomeTile[,] tiles, int nThreads, int seed) { List <Room> outRooms = new List <Room>(); //Generate the initial rooms as rectangles, // then modify them from there with cellular automata. //Calculate the size of each initial room. Vector2i roomCellSize = tiles.SizeXY() / NRooms; Vector2i roomSize = roomCellSize - RoomSpacing; float minCircleLerp = Mathf.Lerp(0.5f, 0.0f, CirclePosVariance), maxCircleLerp = Mathf.Lerp(0.5f, 1.0f, CirclePosVariance); //Create the initial rooms. Vector2i max = tiles.SizeXY() + 1; for (int yStart = 0; (yStart + roomCellSize.y) < max.y; yStart += roomCellSize.y) { for (int xStart = 0; (xStart + roomCellSize.x) < max.x; xStart += roomCellSize.x) { Vector2i start = new Vector2i(xStart, yStart); //For the room's biome, get the biome of its center tile. Vector2i center = start + (roomCellSize / 2); PRNG prng = new PRNG(center.x, center.y, seed); Vector2i roomMin = center - (roomSize / 2), roomMax = center + (roomSize / 2); outRooms.Add(new Room(tiles.Get(center), new RectI(roomMin, roomMax))); //Carve circular spaces out of the room. int nCircles = MinCirclesPerRoom + (prng.NextInt() % (MaxCirclesPerRoom - MinCirclesPerRoom + 1)); for (int circI = 0; circI < nCircles; ++circI) { //Choose a random position for the circle centered around the origin. Vector2 posLerp = new Vector2(Mathf.Lerp(minCircleLerp, maxCircleLerp, prng.NextFloat()), Mathf.Lerp(minCircleLerp, maxCircleLerp, prng.NextFloat())); Vector2 circlePos = new Vector2(Mathf.Lerp(xStart, xStart + roomCellSize.x, posLerp.x), Mathf.Lerp(yStart, yStart + roomCellSize.y, posLerp.y)); float circleRadius = Mathf.Lerp(CircleMinRadius * roomSize.x, CircleMaxRadius * roomSize.y, prng.NextFloat()); CarveCircle(outRooms[outRooms.Count - 1], circlePos, circleRadius); } } } return(outRooms); }
public override int ChooseRule(PRNG r, int nRules) { return(r.NextInt() % nRules); }
public static string GenerateName(Genders gender, int seed) { //Basic idea: Take a bunch of syllables and smash 'em together. //Every syllable has a beginning and ending letter. //These beginnings and endings can be split into three groups: // 1. Vowels -- can follow any consonant, but should usually not follow a vowel // 2. Ending consonant -- shouldn't follow an ending consonant, MIGHT follow a continuing consonant, can follow any vowel // 3. Continuing consonant -- can follow any continuing consonants or vowels, but not ending consonants. //These syllables are hard-coded and stored in the below "syllables" collection. PRNG rng = new PRNG(seed); const int minSyllables = 2, maxSyllables = 4; const float chance_VowelToVowel = 0.05f, chance_ContinuingConsonantToEndingConsonant = 0.5f; int nSyllables = rng.NextInt(minSyllables, maxSyllables + 1); //Start with a completely random syllable. System.Text.StringBuilder sb = new System.Text.StringBuilder(); Syllable firstSyllable = syllables.ElementAt(rng.NextInt(0, syllables.Count)); sb.Append(firstSyllable.Value); //Add successive syllables making sure they don't conflict with what came before. HashSet <Syllable> acceptableSyllables = new HashSet <Syllable>(); Syllable lastSyllable = firstSyllable; for (int i = 1; i < nSyllables; ++i) { //Get all acceptable syllables. acceptableSyllables.Clear(); foreach (Syllable syllable in syllables) { float chanceOfAccepting; switch (syllable.StartType) { case Syllable.Types.Vowel: switch (lastSyllable.EndType) { case Syllable.Types.Vowel: chanceOfAccepting = chance_VowelToVowel; break; case Syllable.Types.ContinuingConsonant: case Syllable.Types.EndingConsonant: chanceOfAccepting = 1.0f; break; default: throw new NotImplementedException(lastSyllable.EndType.ToString()); } break; case Syllable.Types.EndingConsonant: switch (lastSyllable.EndType) { case Syllable.Types.Vowel: chanceOfAccepting = 1.0f; break; case Syllable.Types.ContinuingConsonant: chanceOfAccepting = chance_ContinuingConsonantToEndingConsonant; break; case Syllable.Types.EndingConsonant: chanceOfAccepting = 0.0f; break; default: throw new NotImplementedException(lastSyllable.EndType.ToString()); } break; case Syllable.Types.ContinuingConsonant: switch (lastSyllable.EndType) { case Syllable.Types.Vowel: case Syllable.Types.ContinuingConsonant: chanceOfAccepting = 1.0f; break; case Syllable.Types.EndingConsonant: chanceOfAccepting = 0.0f; break; default: throw new NotImplementedException(lastSyllable.EndType.ToString()); } break; default: throw new NotImplementedException(syllable.StartType.ToString()); } if (chanceOfAccepting <= 0.0f || syllable == lastSyllable) { continue; } if (chanceOfAccepting >= 1.0f || rng.NextFloat() < chanceOfAccepting) { acceptableSyllables.Add(syllable); } } //Pick one randomly. var nextSyllable = acceptableSyllables.ElementAt(rng.NextInt(0, acceptableSyllables.Count)); lastSyllable = nextSyllable; sb.Append(nextSyllable.Value); } sb[0] = char.ToUpper(sb[0]); return(sb.ToString()); }
/// <summary> /// Runs the generator and returns the spaces in the generated entrance. /// </summary> /// <param name="rooms"> /// The rooms that were generated. Used to choose a place to place an entrance. /// </param> /// <param name="theMap"> /// The map that new units are being generated into. /// </param> /// <param name="unitsToKeep"> /// The units to keep alive in the map, indexed by their ID's. /// Pass "null" if new units should be generated from scratch. /// </param> public List <Vector2i> Generate(Map theMap, EtMGame.WorldSettings genSettings, List <Room> rooms, int nThreads, int seed, UlongSet unitsToKeep = null) { //Choose a room and place the level's "entrance" into the middle of it. List <Vector2i> entranceSpaces = new List <Vector2i>(); { PRNG roomPlacer = new PRNG(unchecked (seed * 8957)); Vector2i entrance = rooms[roomPlacer.NextInt() % rooms.Count].OriginalBounds.Center; entrance = new Vector2i(Mathf.Clamp(entrance.x, 1, genSettings.Size - 2), Mathf.Clamp(entrance.y, 1, genSettings.Size - 2)); //Carve a small circle out of the map for the entrance. const float entranceRadius = 1.75f, entranceRadiusSqr = entranceRadius * entranceRadius; int entranceRadiusCeil = Mathf.CeilToInt(entranceRadius); Vector2i entranceRegionMin = entrance - new Vector2i(entranceRadiusCeil, entranceRadiusCeil), entranceRegionMax = entrance + new Vector2i(entranceRadiusCeil, entranceRadiusCeil); entranceRegionMin = new Vector2i(Mathf.Clamp(entranceRegionMin.x, 0, genSettings.Size - 1), Mathf.Clamp(entranceRegionMin.y, 0, genSettings.Size - 1)); entranceRegionMax = new Vector2i(Mathf.Clamp(entranceRegionMax.x, 0, genSettings.Size - 1), Mathf.Clamp(entranceRegionMax.y, 0, genSettings.Size - 1)); foreach (Vector2i entranceSpace in new Vector2i.Iterator(entranceRegionMin, entranceRegionMax + 1)) { if (entrance.DistanceSqr(entranceSpace) < entranceRadiusSqr) { entranceSpaces.Add(entranceSpace); theMap.Tiles[entranceSpace] = GameLogic.TileTypes.Empty; } } } //If we weren't given any units to place, generate some. if (unitsToKeep == null) { unitsToKeep = new UlongSet(); //Find or create the PlayerGroup. var playerGroup = GameLogic.Groups.PlayerGroup.Get(theMap); //Generate a certain number of player units. PRNG prng = new PRNG(seed); for (int i = 0; i < NStartingChars; ++i) { //PlayerChar's have 3 stats to randomize. //Distribute "points" among them randomly. //Prefer energy, then food, then strength. float totalPoints = StartingStatAbilities; float p = prng.NextFloat() * Math.Min(1.0f, totalPoints); float energy = p; totalPoints -= p; p = prng.NextFloat() * Math.Min(1.0f, totalPoints); float food = p; totalPoints -= p; float strength = totalPoints; energy = Mathf.Lerp(PlayerConsts.MinStart_Energy, PlayerConsts.MaxStart_Energy, energy); food = Mathf.Lerp(PlayerConsts.MinStart_Food, PlayerConsts.MaxStart_Food, food); strength = Mathf.Lerp(PlayerConsts.MinStart_Strength, PlayerConsts.MaxStart_Strength, strength); var gender = (i % 2 == 0) ? GameLogic.Units.Player_Char.Personality.Genders.Male : GameLogic.Units.Player_Char.Personality.Genders.Female; PlayerChar chr = new PlayerChar( theMap, playerGroup.ID, food, energy, strength, 1.0f, GameLogic.Units.Player_Char.Personality.GenerateName(gender, prng.NextInt()), gender); theMap.AddUnit(chr); unitsToKeep.Add(chr.ID); } } //Position the units. UnityEngine.Assertions.Assert.IsTrue(entranceSpaces.Count > 0); int unitsPerSpace = unitsToKeep.Count / entranceSpaces.Count; int unitI = 0, posI = 0; foreach (Unit unit in unitsToKeep.Select(id => theMap.GetUnit(id))) { unit.Pos.Value = entranceSpaces[posI]; unitI += 1; if (unitI >= unitsPerSpace) { unitI = 0; posI = (posI + 1) % entranceSpaces.Count; } } return(entranceSpaces); }