public virtual void ToTilemap(ref Tile[,] map) { var woodFloor = Color.FromArgb(86, 63, 44); var caveFloor = Color.FromArgb(65, 66, 87); var wall = Color.FromArgb(20, 15, 12); var water = BiomeData.Biomes[BiomeData.ByName(biome.Realm == Realms.Nox ? "Water" : "KoolAid")]; allowCaveFloor = false; Clutter.ParentBoardHack = Board; var doorCount = 0; var safeZones = new List <Rectangle>(); for (var row = 0; row < plotRows; row++) { for (var col = 0; col < plotCols; col++) { if (plots[col, row].BaseID == null) { //Can clutter this up! if (includeClutter && Random.Flip()) { Board.AddClutter(col * plotWidth, row * plotHeight, (col * plotWidth) + plotWidth, (row * plotHeight) + plotHeight + row); } else { safeZones.Add(new Rectangle() { Left = col * plotWidth, Top = row * plotHeight, Right = (col * plotWidth) + plotWidth, Bottom = (row * plotHeight) + plotHeight + row }); } continue; } if (plots[col, row].BaseID == "<spillover>") { continue; } var building = plots[col, row]; var template = building.Template; var sX = (col * plotWidth) + building.XShift; var sY = (row * plotHeight) + building.YShift; for (var y = 0; y < template.Height; y++) { for (var x = 0; x < template.Width; x++) { var tc = template.MapScans[y][x]; var def = string.Empty; var fluid = Fluids.Dry; var addDoor = true; if (tc == '/') { addDoor = false; tc = '\\'; } switch (tc) { case '\'': continue; case ',': def = "pathWay"; // FIXME: kind of ugly but does the job break; case '.': def = "woodFloor"; break; case '+': //Exit -- can't be seen, coaxes walls into shape. def = "doorwayClosed"; if (addDoor) { doorCount++; var door = new Door() { XPosition = sX + x, YPosition = sY + y, ForegroundColor = woodFloor, BackgroundColor = woodFloor.Darken(), ID = building.BaseID + "_Door" + doorCount, ParentBoard = Board, Closed = true, Glyph = '+' }; Board.Entities.Add(door); } break; case '=': def = "outerWoodWall"; break; case '-': def = "innerWoodWall"; break; case '#': def = allowCaveFloor ? "stoneFloor" : "woodFloor"; break; default: if (template.Markings.ContainsKey(tc)) { #region Custom markings var m = template.Markings[tc]; if (m.Text == "block") { throw new Exception("Got a BLOCK-type marking in a building template."); } if (m.Text != "tile" && m.Text != "floor" && m.Text != "water") { //Keep a floor here. The entity fills in the blank. def = "woodFloor"; var tileDef = TileDefinition.Find(def, false); map[sX + x, sY + y].Index = tileDef.Index; //var owner = m.Owner == 0 ? null : building.Inhabitants[m.Owner - 1]; var owner = (Character)null; if (m.HasToken("owner")) { owner = building.Inhabitants[(int)m.GetToken("owner").Value - 1]; } if (m.Text == "bed") { var newBed = new Clutter() { XPosition = sX + x, YPosition = sY + y, Name = "Bed", ID = "Bed_" + (owner == null ? Board.Entities.Count.ToString() : owner.Name.ToID()), Description = owner == null?i18n.GetString("freebed") : i18n.Format("someonesbed", owner.Name.ToString(true)), ParentBoard = Board, }; Clutter.ResetToKnown(newBed); Board.Entities.Add(newBed); } if (m.Text == "container") { //var type = c == '\x14B' ? "cabinet" : c == '\x14A' ? "chest" : "container"; var type = "chest"; if (m.HasToken("wardrobe")) { type = "wardrobe"; } var contents = DungeonGenerator.GetRandomLoot("container", type, new Dictionary <string, string>() { { "gender", owner.PreferredGender.ToString().ToLowerInvariant() }, { "biome", BiomeData.Biomes[DungeonGenerator.DungeonGeneratorBiome].Name.ToLowerInvariant() }, }); if (owner != null) { foreach (var content in contents) { content.AddToken("owner", 0, owner.ID); } } var newContainer = new Container(type, contents) //owner == null ? type.Titlecase() : owner.Name.ToString(true) + "'s " + type, contents) { XPosition = sX + x, YPosition = sY + y, ID = "Container_" + type + "_" + (owner == null ? Board.Entities.Count.ToString() : owner.Name.ToID()), ParentBoard = Board, }; Clutter.ResetToKnown(newContainer); Board.Entities.Add(newContainer); } else if (m.Text == "clutter") { if (m.HasToken("id")) { var newClutter = new Clutter() { XPosition = sX + x, YPosition = sY + y, ParentBoard = Board, ID = m.GetToken("id").Text, Name = string.Empty, }; Clutter.ResetToKnown(newClutter); Board.Entities.Add(newClutter); } else { var newClutter = new Clutter() { Glyph = (char)m.GetToken("char").Value, //m.Params.Last()[0], XPosition = sX + x, YPosition = sY + y, ForegroundColor = Color.Black, BackgroundColor = tileDef.Background, ParentBoard = Board, Name = m.GetToken("name").Text, //Name, Description = m.HasToken("description") ? m.GetToken("description").Text : string.Empty, Blocking = m.HasToken("blocking"), }; Board.Entities.Add(newClutter); } } } else if (m.Text == "water") { fluid = Fluids.Water; } else { def = TileDefinition.Find((int)m.GetToken("index").Value).Name; } #endregion } break; } map[sX + x, sY + y].Index = TileDefinition.Find(def).Index; map[sX + x, sY + y].Fluid = fluid; } } for (var i = 0; i < building.Inhabitants.Count; i++) { var inhabitant = building.Inhabitants[i]; //Find each inhabitant's bed so we can give them a starting place. //Alternatively, place them anywhere there's a ' ' within their sector. var bc = new BoardChar(inhabitant); //var bedID = building.BaseID + "_Bed_" + inhabitant.Name.FirstName; //var bed = Board.Entities.OfType<Clutter>().FirstOrDefault(b => b.ID == bedID); //if (bed != null) //{ // bc.XPosition = bed.XPosition; // bc.YPosition = bed.YPosition; //} //else { //var okay = false; var x = 0; var y = 0; var lives = 100; while (lives > 0) { lives--; x = (col * plotWidth) + Random.Next(plotWidth); y = (row * plotHeight) + Random.Next(plotHeight); if (!map[x, y].Definition.Wall && (!template.AllowOutside && map[x, y].Definition.Ceiling) && Board.Entities.FirstOrDefault(e => e.XPosition == x && e.YPosition == y) == null) { break; } } bc.XPosition = x; bc.YPosition = y; } bc.Character.AddToken("sectorlock"); bc.ParentBoard = Board; bc.AdjustView(); bc.Sector = string.Format("s{0}x{1}", row, col); Board.Entities.Add(bc); } } } Board.ResolveVariableWalls(); if (safeZones.Count > 0 && includeWater) { Board.AddWater(safeZones); } }
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); }