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); }