Example #1
0
        public static Board CreateDungeon(bool forTravel, string name)
        {
            var host = NoxicoGame.HostForm;
            var nox  = host.Noxico;

            new UIPNGBackground(Mix.GetBitmap("makecave.png")).Draw();
            host.Write("Generating dungeon. Please wait.", Color.Silver, Color.Transparent, 1, 2);
            host.Draw();

            var dunGen  = new StoneDungeonGenerator();
            var caveGen = new CaveGenerator();

            Func <Board, Warp> findWarpSpot = (b) =>
            {
                var eX       = 0;
                var eY       = 0;
                var attempts = 0;
                var minSides = 1;
                while (true)
                {
                    attempts++;
                    if (attempts == 10)
                    {
                        minSides = 0;
                    }

                    eX = Random.Next(1, b.Width - 1);
                    eY = Random.Next(1, b.Height - 1);

                    //2013-03-07: prevent placing warps on same tile as clutter
                    //<Ragath> Kawa, this is bad
                    //<Ragath> that should be a .Any() call
                    //if (b.Entities.FirstOrDefault(e => e.XPosition == eX && e.YPosition == eY) != null)
                    if (b.Entities.Any(e => e.XPosition == eX && e.YPosition == eY))
                    {
                        Program.WriteLine("Tried to place a warp below an entity -- rerolling...");
                        continue;
                    }

                    var sides = 0;
                    if (b.IsSolid(eY - 1, eX))
                    {
                        sides++;
                    }
                    if (b.IsSolid(eY + 1, eX))
                    {
                        sides++;
                    }
                    if (b.IsSolid(eY, eX - 1))
                    {
                        sides++;
                    }
                    if (b.IsSolid(eY, eX + 1))
                    {
                        sides++;
                    }
                    if (sides < 3 && sides >= minSides)
                    {
                        break;
                    }
                }
                return(new Warp()
                {
                    XPosition = eX, YPosition = eY
                });
            };

            Warp originalExit = null;

            BiomeData.LoadBiomes();
            var biomeData = BiomeData.Biomes[DungeonGeneratorBiome];

            /* Step 1 - Randomize jagged array, make boards for each entry.
             * ------------------------------------------------------------
             * ("goal" board is boss/treasure room, picked at random from bottom floor set.)
             * [EXIT] [ 01 ] [ 02 ]
             * [ 03 ] [ 04 ]
             * [ 05 ] [ 06 ] [ 07 ] [ 08 ]
             * [ 09 ] [ 10 ] [ 11 ]
             * [GOAL] [ 13 ]
             */
            var levels = new List <List <Board> >();
            var depth  = Random.Next(3, 6);

            var boardWidths  = new[] { 80, 80, 80, 40, 160, 120 };
            var boardHeights = new[] { 50, 50, 50, 25, 100, 75 };

            for (var i = 0; i < depth; i++)
            {
                levels.Add(new List <Board>());
                var length = Random.Next(2, 5);
                for (var j = 0; j < length; j++)
                {
                    var board = new Board(boardWidths.PickOne(), boardHeights.PickOne());
                    board.AllowTravel = false;
                    board.Clear(DungeonGeneratorBiome);
                    board.BoardNum   = nox.Boards.Count;
                    board.Coordinate = nox.Player.ParentBoard.Coordinate;
                    if (i > 0)
                    {
                        board.AddToken("dark");
                    }
                    nox.Boards.Add(board);
                    levels[i].Add(board);
                }
            }

            //Decide which boards are the exit and goal
            var entranceBoard = levels[0].PickOne();
            var goalBoard     = levels[levels.Count - 1].PickOne();

            //Generate content for each board
            for (var i = 0; i < levels.Count; i++)
            {
                for (var j = 0; j < levels[i].Count; j++)
                {
                    var board = levels[i][j];

                    if (Random.NextDouble() > 0.7 || board == entranceBoard)
                    {
                        caveGen.Board = board;
                        caveGen.Create(biomeData);
                        caveGen.ToTilemap(ref board.Tilemap);
                    }
                    else
                    {
                        dunGen.Board = board;
                        dunGen.Create(biomeData);
                        dunGen.ToTilemap(ref board.Tilemap);
                    }

                    board.Name = string.Format("Level {0}-{1}", i + 1, (char)('A' + j));
                    if (!name.IsBlank())
                    {
                        board.Name = string.Format("{0}, level {1}-{2}", name, i + 1, (char)('A' + j));
                    }
                    board.ID        = string.Format("Dng_{0}_{1}{2}", DungeonGeneratorEntranceBoardNum, i + 1, (char)('A' + j));
                    board.BoardType = BoardType.Dungeon;
                    board.Music     = "set://Dungeon";
                    var encounters = board.GetToken("encounters");
                    foreach (var e in biomeData.Encounters)
                    {
                        encounters.AddToken(e);
                    }
                    encounters.Value = biomeData.MaxEncounters;
                    encounters.GetToken("stock").Value = encounters.Value * Random.Next(3, 5);
                    board.RespawnEncounters();

                    //If this is the entrance board, add a warp back to the Overworld.
                    if (board == entranceBoard)
                    {
                        var exit = findWarpSpot(board);
                        originalExit = exit;
                        exit.ID      = "Dng_" + DungeonGeneratorEntranceBoardNum + "_Exit";
                        board.Warps.Add(exit);
                        board.SetTile(exit.YPosition, exit.XPosition, "dungeonExit");                         //board.SetTile(exit.YPosition, exit.XPosition, '<', Color.Silver, Color.Black);
                    }
                }
            }

            /* Step 2 - Randomly add up/down links
             * -----------------------------------
             * (imagine for the moment that each board can have more than one exit and that this goes for both directions.)
             * [EXIT] [ 01 ] [ 02 ]
             *    |
             * [ 03 ] [ 04 ]
             *           |
             * [ 05 ] [ 06 ] [ 07 ] [ 08 ]
             *    |             |
             * [ 09 ] [ 10 ] [ 11 ]
             *                  |
             *        [GOAL] [ 13 ]
             */
            var connected = new List <Board>();

            for (var i = 0; i < levels.Count; i++)
            {
                var j = Random.Next(0, levels[i].Count);
                //while (connected.Contains(levels[i][j]))
                //	j = Randomizer.Next(0, levels[i].Count);

                var up        = false;
                var destLevel = i + 1;
                if (destLevel == levels.Count)
                {
                    up        = true;
                    destLevel = i - 1;
                }
                var dest = Random.Next(0, levels[destLevel].Count);

                var boardHere  = levels[i][j];
                var boardThere = levels[destLevel][dest];

                var here  = findWarpSpot(boardHere);
                var there = findWarpSpot(boardThere);
                boardHere.Warps.Add(here);
                boardThere.Warps.Add(there);
                here.ID            = boardHere.ID + boardHere.Warps.Count;
                there.ID           = boardThere.ID + boardThere.Warps.Count;
                here.TargetBoard   = boardThere.BoardNum;
                there.TargetBoard  = boardHere.BoardNum;
                here.TargetWarpID  = there.ID;
                there.TargetWarpID = here.ID;
                boardHere.SetTile(here.YPosition, here.XPosition, up ? "dungeonUpstairs" : "dungeonDownstairs");                 //boardHere.SetTile(here.YPosition, here.XPosition, up ? '<' : '>', Color.Gray, Color.Black);
                boardThere.SetTile(there.YPosition, there.XPosition, up ? "dungeonDownstairs" : "dungeonUpstairs");              //boardThere.SetTile(there.YPosition, there.XPosition, !up ? '<' : '>', Color.Gray, Color.Black);

                Program.WriteLine("Connected {0} || {1}.", boardHere.ID, boardThere.ID);

                connected.Add(boardHere);
                connected.Add(boardThere);
            }

            /* Step 3 - Connect the Unconnected
             * --------------------------------
             * [EXIT]=[ 01 ]=[ 02 ]
             *  |
             * [ 03 ]=[ 04 ]
             *           |
             * [ 05 ]=[ 06 ] [ 07 ]=[ 08 ]
             *    |             |
             * [ 09 ]=[ 10 ]=[ 11 ]
             *                  |
             *        [GOAL]=[ 13 ]
             */

            for (var i = 0; i < levels.Count; i++)
            {
                for (var j = 0; j < levels[i].Count - 1; j++)
                {
                    //Don't connect if this board AND the right-hand neighbor are already connected.
                    //if (connected.Contains(levels[i][j]) && connected.Contains(levels[i][j + 1]))
                    //	continue;

                    var boardHere  = levels[i][j];
                    var boardThere = levels[i][j + 1];

                    var here  = findWarpSpot(boardHere);
                    var there = findWarpSpot(boardThere);
                    boardHere.Warps.Add(here);
                    boardThere.Warps.Add(there);
                    here.ID            = boardHere.ID + boardHere.Warps.Count;
                    there.ID           = boardThere.ID + boardThere.Warps.Count;
                    here.TargetBoard   = boardThere.BoardNum;
                    there.TargetBoard  = boardHere.BoardNum;
                    here.TargetWarpID  = there.ID;
                    there.TargetWarpID = here.ID;
                    boardHere.SetTile(here.YPosition, here.XPosition, "dungeonSideexit");                     //boardHere.SetTile(here.YPosition, here.XPosition, '\x2261', Color.Gray, Color.Black);
                    boardThere.SetTile(there.YPosition, there.XPosition, "dungeonSideexit");                  //boardThere.SetTile(there.YPosition, there.XPosition, '\x2261', Color.Gray, Color.Black);

                    Program.WriteLine("Connected {0} -- {1}.", boardHere.ID, boardThere.ID);

                    connected.Add(boardHere);
                    connected.Add(boardThere);
                }
            }

            // Step 4 - place sick lewt in goalBoard
            var treasureX = 0;
            var treasureY = 0;

            while (true)
            {
                treasureX = Random.Next(1, goalBoard.Width - 1);
                treasureY = Random.Next(1, goalBoard.Height - 1);

                //2013-03-07: prevent treasure from spawning inside a wall
                if (goalBoard.IsSolid(treasureY, treasureX))
                {
                    continue;
                }
                //2013-03-07: prevent placing warps on same tile as clutter
                if (goalBoard.Entities.FirstOrDefault(e => e.XPosition == treasureX && e.YPosition == treasureY) != null)
                {
                    Program.WriteLine("Tried to place cave treasure below an entity -- rerolling...");
                    continue;
                }

                var sides = 0;
                if (goalBoard.IsSolid(treasureY - 1, treasureX))
                {
                    sides++;
                }
                if (goalBoard.IsSolid(treasureY + 1, treasureX))
                {
                    sides++;
                }
                if (goalBoard.IsSolid(treasureY, treasureX - 1))
                {
                    sides++;
                }
                if (goalBoard.IsSolid(treasureY, treasureX + 1))
                {
                    sides++;
                }
                if (sides < 3 && sides > 1 && goalBoard.Warps.FirstOrDefault(w => w.XPosition == treasureX && w.YPosition == treasureY) == null)
                {
                    break;
                }
            }
            var treasure = DungeonGenerator.GetRandomLoot("container", "dungeon_chest", new Dictionary <string, string>()
            {
                { "biome", BiomeData.Biomes[DungeonGenerator.DungeonGeneratorBiome].Name.ToLowerInvariant() },
            });
            var treasureChest = new Container(i18n.GetString("treasurechest"), treasure)
            {
                Glyph           = 0x14A,
                XPosition       = treasureX,
                YPosition       = treasureY,
                ForegroundColor = Color.FromName("SaddleBrown"),
                BackgroundColor = Color.Black,
                ParentBoard     = goalBoard,
                Blocking        = false,
            };

            goalBoard.Entities.Add(treasureChest);

            if (forTravel)
            {
                originalExit.TargetBoard = -2;                 //causes Travel menu to appear on use.
                return(entranceBoard);
            }

            var entrance = nox.CurrentBoard.Warps.Find(w => w.ID == DungeonGeneratorEntranceWarpID);

            entrance.TargetBoard      = entranceBoard.BoardNum;        //should be this one.
            entrance.TargetWarpID     = originalExit.ID;
            originalExit.TargetBoard  = nox.CurrentBoard.BoardNum;
            originalExit.TargetWarpID = entrance.ID;

            nox.CurrentBoard.EntitiesToRemove.Add(nox.Player);
            nox.CurrentBoard       = entranceBoard;
            nox.Player.ParentBoard = entranceBoard;
            entranceBoard.Entities.Add(nox.Player);
            nox.Player.XPosition = originalExit.XPosition;
            nox.Player.YPosition = originalExit.YPosition;
            entranceBoard.UpdateLightmap(nox.Player, true);
            entranceBoard.Redraw();
            entranceBoard.PlayMusic();
            NoxicoGame.Immediate = true;
            NoxicoGame.Mode      = UserMode.Walkabout;
            NoxicoGame.Me.SaveGame();

            return(entranceBoard);
        }