예제 #1
0
        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);
        }
예제 #2
0
 public override int ChooseRule(PRNG r, int nRules)
 {
     return(r.NextInt() % nRules);
 }
예제 #3
0
        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());
        }
예제 #4
0
        /// <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);
        }