public CompleteMap Generate(MT19337 rng, MapRequirements reqs) { var map = new Map((byte)reqs.Barrier); GenerateTree(rng); foreach (var roomspec in reqs.Rooms) { // randomly descend rooms until you find one that fits // var leaf_that_fits = RandomLeafWhere(rng, leaf => leaf.Width >= roomspec.Width && leaf.Height >= roomspec.Height); // if (leaf_that_fits == null) // return null; // leaf_that_fits.WalkableSpace = Rectangle.Empty; // don't generate one // TODO something } root.GenerateRooms(rng); IEnumerable <Rectangle> all_the_rectangles = all_nodes. Where(leaf => leaf.WalkableSpace != null). Select(leaf => (Rectangle)leaf.WalkableSpace). Concat( all_nodes. Where(leaf => leaf.Hallways != null). SelectMany(leaf => leaf.Hallways) ); foreach (var rect in all_the_rectangles) { // TODO something smarter map.Fill((rect.Left, rect.Top), (rect.Width, rect.Height), reqs.Floor); } var smoothIterations = 0; while (MapHelper.SmoothFilter(map, reqs.Barrier, reqs.Floor)) { smoothIterations++; } Point entranceLocation = PointInAnyRandomRoom(rng); map[entranceLocation.Y, entranceLocation.X] = (byte)Tile.WarpUp; return(new CompleteMap { Map = map, Entrance = new Coordinate((byte)entranceLocation.X, (byte)entranceLocation.Y, CoordinateLocale.Standard), Requirements = reqs }); }
public CompleteMap Generate(MT19337 rng, MapRequirements reqs) { //Constants! Get yer constants here! int iteration_count = 15; //((List<RoomSpec>)reqs.Rooms)[0].Tiledata = ((List<RoomSpec>)reqs.Rooms)[0].Tiledata; CompleteMap complete = new CompleteMap { Map = new Map((byte)Tile.WaterfallInside) }; //(57,56) var startLoc = (x : 0x39, y : 0x38); var startingX = rng.Between(-3, 0) + startLoc.x; var startingY = rng.Between(-4, -1) + startLoc.y; var startRegion = new Region(startingX, startingY, 4, 5, Tile.WaterfallRandomEncounters); List <Region> regionList = new List <Region>(); regionList.Add(startRegion); List <Region> endingRegions = new List <Region>(); for (var i = 0; i < iteration_count; i++) { var startPoint = regionList[rng.Between(0, regionList.Count - 1)]; var newRegions = RegionChain(rng, startPoint, regionList, 30 - i); regionList.AddRange(newRegions); if (newRegions.Count > 0) { endingRegions.Add(newRegions[newRegions.Count - 1]); } } var foundRoomPlace = false; var room = ((List <RoomSpec>)reqs.Rooms)[0]; var room_x = -1; var room_y = -1; while (!foundRoomPlace && endingRegions.Count > 0) { var borderRegion = endingRegions.PickRandom(rng); var base_x = (borderRegion.x - (room.Width - 1) + 64) % 64; room_y = (borderRegion.y - room.Height + 64) % 64; var x_offset = 1; List <int> valid_offsets = new List <int>(); while (x_offset < room.Width) { room_x = (base_x + x_offset) % 64; var validRoomPlace = true; foreach (Region r in regionList) { var testVal = validRoomPlace && !r.IntersectsRoom(room, room_x, room_y); if (!testVal && validRoomPlace) { //Console.WriteLine(room_x); } validRoomPlace = testVal; } if (validRoomPlace) { valid_offsets.Add(x_offset); } x_offset++; } if (valid_offsets.Count != 0) { foundRoomPlace = true; room_x = (base_x + valid_offsets[rng.Between(0, valid_offsets.Count - 1)]); } endingRegions.Remove(borderRegion); } if (!foundRoomPlace) { List <int> idxs = Enumerable.Range(0, regionList.Count).ToList(); while (!foundRoomPlace && idxs.Count > 0) { int regionIdx = idxs.PickRandom(rng); var borderRegion = regionList[regionIdx]; var base_x = (borderRegion.x - (room.Width - 1) + 64) % 64; room_y = (borderRegion.y - (room.Height - 1) + 64) % 64; var x_offset = 1; List <int> valid_offsets = new List <int>(); while (x_offset < room.Width) { room_x = (base_x + x_offset) % 64; var validRoomPlace = true; foreach (Region r in regionList) { var testVal = validRoomPlace && !r.IntersectsRoom(room, room_x, room_y); if (!testVal && validRoomPlace) { //Console.WriteLine(room_x); } validRoomPlace = testVal; } if (validRoomPlace) { valid_offsets.Add(x_offset); } x_offset++; } if (valid_offsets.Count != 0) { foundRoomPlace = true; room_x = (base_x + valid_offsets[rng.Between(0, valid_offsets.Count - 1)]); } idxs.Remove(regionIdx); } } if (!foundRoomPlace) { Console.WriteLine("No room found :o"); return(null); } //Draw every room in regionList to complete foreach (Region r in regionList) { r.DrawRegion(complete); } Region waterfallRoom = new Region(room_x, room_y, room); waterfallRoom.DrawRegion(complete); int doorYPos = (room_y + room.Height) % 64; List <int> possibleDoors = new List <int>(); for (var i = 0; i < room.Width; i++) { if (complete.Map[((room_x + i) % 64, doorYPos)].Tile == Tile.WaterfallRandomEncounters)
public CompleteMap Generate(MT19337 rng, MapGeneratorStrategy strategy, MapRequirements reqs) { CompleteMap map = null; if (reqs.MapId == MapId.Waterfall) { reqs.Floor = Tile.WaterfallRandomEncounters; reqs.InRoomFloor = Tile.WaterfallInside; reqs.FreeNPCs = Enumerable.Range(1, 10); reqs.Rooms = new List <RoomSpec> { new RoomSpec { Tiledata = new byte[, ] { { 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 }, { 0x03, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x05 }, { 0x03, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x05 }, { 0x06, 0x07, 0x48, 0x07, 0x07, 0x07, 0x07, 0x08 }, { 0x30, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30 }, { 0x49, 0x49, 0x3A, 0x49, 0x49, 0x49, 0x49, 0x49 } }, NPCs = new List <NPC> { new NPC { Index = 0, Coord = (5, 2), InRoom = true, Stationary = false } }, } }; reqs.Portals = new byte[] { (byte)Tile.WarpUp }; IMapGeneratorEngine engine = GetEngine(strategy); int iterations = 0; while (iterations < MAX_MAP_ITERATIONS && map == null) { Console.WriteLine($"Generating {reqs.MapId} - iteration #{iterations}"); map = engine.Generate(rng, reqs); } if (map == null) { throw new InsaneException($"Couldn't generate map using {strategy} after maximum {iterations} iterations."); } // add the reqs we used map.Requirements = reqs; } else if (reqs.MapId == MapId.EarthCaveB1) { reqs.Floor = Tile.EarthCaveRandomEncounters; reqs.InRoomFloor = Tile.EarthCaveInside; reqs.OutOfBounds = Tile.EarthCaveOOB; reqs.Barrier = Tile.EarthCaveRockA; reqs.FreeNPCs = new int[] { }; reqs.Rooms = new List <RoomSpec> { }; reqs.Portals = new byte[] { (byte)Tile.WarpUp, 0x24 }; reqs.Objects = Enumerable.Range(0x42, 5).Select(x => (byte)x); reqs.Traps = Enumerable.Repeat(0x1D, 3).Select(x => (byte)x); IMapGeneratorEngine engine = GetEngine(strategy); map = engine.Generate(rng, reqs); // add the reqs we used map.Requirements = reqs; } else if (reqs.MapId == MapId.EarthCaveB2) { reqs.Floor = Tile.EarthCaveRandomEncounters; reqs.InRoomFloor = Tile.EarthCaveInside; reqs.OutOfBounds = Tile.EarthCaveOOB; reqs.Barrier = Tile.EarthCaveRockA; reqs.FreeNPCs = Enumerable.Range(0, 13); reqs.Rooms = new List <RoomSpec> { }; reqs.Portals = new byte[] { (byte)Tile.WarpUp, 0x25 }; reqs.Objects = Enumerable.Range(0x47, 6).Select(x => (byte)x); reqs.Traps = Enumerable.Range(0x1D, 1).Select(x => (byte)x); IMapGeneratorEngine engine = GetEngine(strategy); map = engine.Generate(rng, reqs); // add the reqs we used map.Requirements = reqs; } else { throw new ArgumentOutOfRangeException(); } // Free NPC placement doesn't require the engine var locations = map.Map.Where(element => element.Tile == reqs.Floor).ToList(); reqs.FreeNPCs.ToList().ForEach(npc => { var location = locations.SpliceRandom(rng); reqs.Rom.MoveNpc(reqs.MapId, npc, location.X, location.Y, false, false); }); if (Debugger.IsAttached) { Console.Write(map.AsText()); } return(map); }
public CompleteMap Generate(MT19337 rng, MapRequirements reqs) { CompleteMap complete = new CompleteMap { Map = new Map((byte)Tile.WaterfallInside) }; //Map map = complete.Map; var startLoc = (x : 0x39, y : 0x38); var roomLoc = (x : 0x35, y : 0x30); var startingX = rng.Between(-3, 0) + startLoc.x; var startingY = rng.Between(-4, 0) + startLoc.y; var endPoint = (x : startingX, y : startingY); complete.Map.Fill(endPoint, (4, 5), 0x49); List <(int x, int y)> visited = new List <(int x, int y)>(); List <List <Directions> > visitedDir = new List <List <Directions> >(); var index = 0; var outerLoops = 0; Directions curDirection = (Directions)rng.Between(0, 3); visited.Add(endPoint); visitedDir.Add(FullDirs()); visitedDir[0].Remove(curDirection); do { var iterCount = rng.Between(8, 20); var offset = endPoint; for (int j = 0; j < iterCount; j++) { var newOffset = (x : 0, y : 0); switch (curDirection) { case Directions.up: newOffset = (x : offset.x + rng.Between(-2, 2), y : offset.y - rng.Between(2, 4)); break; case Directions.down: newOffset = (x : offset.x + rng.Between(-2, 2), y : offset.y + rng.Between(2, 4)); break; case Directions.right: newOffset = (x : offset.x + rng.Between(2, 4), y : offset.y + rng.Between(-2, 3)); break; case Directions.left: newOffset = (x : offset.x - rng.Between(2, 4), y : offset.y + rng.Between(-2, 3)); break; } if (newOffset.x < 2 || newOffset.x > 57 || newOffset.y < 2 || newOffset.y > 56) { break; } offset = newOffset; complete.Map.Fill(offset, (4, 5), 0x49); } if (offset.x != endPoint.x && offset.y != endPoint.y) { visited.Add(offset); List <Directions> newDirs = FullDirs(); newDirs.Remove(3 - curDirection); visitedDir.Add(newDirs); } index = rng.Between(0, visited.Count - 1); endPoint = visited[index]; var randDirection = rng.Between(0, visitedDir[index].Count - 1); curDirection = visitedDir[index][randDirection]; outerLoops++; } while (outerLoops < 35); //Now, we need to smooth out any 1x1 chunks, because those don't render well var smoothIterations = 0; while (MapHelper.SmoothFilter(complete.Map, Tile.WaterfallInside, Tile.WaterfallRandomEncounters)) { smoothIterations++; } //SmoothFilter(complete.Map, Tile.WaterfallInside, Tile.WaterfallRandomEncounters); List <Tile> liveTiles = new List <Tile>() { Tile.WaterfallRandomEncounters }; var distances = FloodFillDist(complete.Map, startLoc, liveTiles); int maxDist = distances.Cast <int>().Max(); List <(int x, int y)> roomPlacements = new List <(int x, int y)>(); for (var i = 1; i < 54; i++) { for (var j = 1; j < 54; j++) { if (Math.Abs(i - startLoc.x) + Math.Abs(j - startLoc.y) > 64) { roomPlacements.Add((x: i, y: j)); } } } //Clean up the 1-by-1 chunks var doneWithRoomPlacement = false; List <Tile> targetTiles = new List <Tile>() { Tile.Doorway }; //Now, pick a place for the room. do { var targetPlace = rng.Between(0, roomPlacements.Count - 1); roomLoc = roomPlacements[targetPlace]; roomPlacements.RemoveAt(targetPlace); var tempRoom = (byte[, ])reqs.Rooms.First().Tiledata.Clone(); // Place the room for (var i = 0; i < 6; i++) { for (var j = 0; j < 8; j++) { tempRoom[i, j] = complete.Map[roomLoc.y + i, roomLoc.x + j]; complete.Map[roomLoc.y + i, roomLoc.x + j] = reqs.Rooms.First().Tiledata[i, j]; } } // Test the room doneWithRoomPlacement = FloodFillReachable(complete.Map, startLoc, liveTiles, targetTiles)[Tile.Doorway]; //If not done, reverse if (!doneWithRoomPlacement) { for (var i = 0; i < 6; i++) { for (var j = 0; j < 8; j++) { complete.Map[roomLoc.y + i, roomLoc.x + j] = tempRoom[i, j]; } } } } while (!doneWithRoomPlacement); byte[] tempOutside = { (byte)Tile.WaterfallRandomEncounters, (byte)Tile.Doorway, (byte)Tile.InsideWall }; List <byte> outsideTiles = new List <byte>(tempOutside); //Okay, now, we need to to do the touch up! for (var i = 0; i < 63; i++) { for (var j = 0; j < 63; j++) { var curTile = (Tile)complete.Map[j, i]; if (curTile == Tile.WaterfallInside && i != 0 && outsideTiles.Contains(complete.Map[j, i - 1])) { curTile = Tile.RoomLeft; } if (curTile == Tile.WaterfallInside && i != 63 && outsideTiles.Contains(complete.Map[j, i + 1])) { curTile = Tile.RoomRight; } if (curTile == Tile.WaterfallInside && j != 63 && outsideTiles.Contains(complete.Map[j + 1, i])) { curTile = Tile.RoomFrontCenter; } if (curTile == Tile.RoomLeft && j != 63 && outsideTiles.Contains(complete.Map[j + 1, i])) { curTile = Tile.RoomFrontLeft; } if (curTile == Tile.RoomRight && j != 63 && outsideTiles.Contains(complete.Map[j + 1, i])) { curTile = Tile.RoomFrontRight; } if (curTile == Tile.WaterfallInside && j != 0 && outsideTiles.Contains(complete.Map[j - 1, i])) { curTile = Tile.RoomBackCenter; } if (curTile == Tile.RoomLeft && j != 0 && outsideTiles.Contains(complete.Map[j - 1, i])) { curTile = Tile.RoomBackLeft; } if (curTile == Tile.RoomRight && j != 0 && outsideTiles.Contains(complete.Map[j - 1, i])) { curTile = Tile.RoomBackRight; } if (curTile == Tile.WaterfallRandomEncounters && j != 0 && (complete.Map[j - 1, i] <= 0x08 || complete.Map[j - 1, i] == 0x46)) { curTile = Tile.InsideWall; } complete.Map[j, i] = (byte)curTile; } } //Finally, add the start complete.Map[startLoc.y, startLoc.x] = (byte)Tile.WarpUp; //and the robot. reqs.Rooms.First().NPCs.ToList().ForEach(npc => { npc.Coord.x += roomLoc.x; npc.Coord.y += roomLoc.y; reqs.Rom.MoveNpc(reqs.MapId, npc); }); complete.Entrance = new Coordinate((byte)startLoc.x, (byte)startLoc.y, CoordinateLocale.Standard); return(complete); }
public CompleteMap Generate(MT19337 rng, MapRequirements reqs) { int sanity = 0; while (true) { if (++sanity == 500) { throw new InsaneException("Failed to generate map!"); } try { CompleteMap complete = new CompleteMap { Map = new Map(SentinelDead), Requirements = reqs, }; { (int x, int y)coord = (0, 0); reqs.Rooms.ToList().ForEach(room => { coord.x = rng.Between(0, MapRequirements.Width - 1 - room.Width); complete.Map.Put(coord, room.Tiledata); coord.y += room.Height + rng.Between(0, 5); room.NPCs.ToList().ForEach(npc => { npc.Coord.x += coord.x; npc.Coord.y += coord.y; reqs.Rom.MoveNpc(reqs.MapId, npc); }); }); } // Generate rooms first. We'll aim to have enough for all the required chests. Map rooms = complete.Map.Clone(); InitializeMap(rng, rooms, 0.33); rooms = DoSimulationStep(rooms, 4, 2, 3); Console.WriteLine($"Room map has {rooms.Count(element => element.Value == SentinelAlive)} walkable tiles."); complete.Map = rooms; Console.Write(complete.AsText()); var clone = rooms.Clone(); foreach (var el in clone.Where(el => el.Value == SentinelDead)) { var roomTile = rooms[el.Coord]; foreach (var newTile in roomTile.Surrounding().Concat(roomTile.Left().Surrounding())) { newTile.Value = SentinelDead; } } Console.WriteLine($"Room map has {rooms.Count(element => element.Value == SentinelAlive)} walkable tiles."); complete.Map = rooms; Console.Write(complete.AsText()); clone = rooms.Clone(); foreach (var el in clone.Where(el => el.Value == SentinelDead)) { var roomTile = rooms[el.Coord]; if (el.Left().Value == SentinelAlive) { if (el.Up().Value == SentinelAlive) { roomTile.Tile = Tile.RoomBackLeft; } else if (el.Down().Value == SentinelAlive) { roomTile.Tile = Tile.RoomFrontLeft; roomTile.Down().Tile = Tile.InsideWall; } else { roomTile.Tile = Tile.RoomLeft; } } else if (el.Right().Value == SentinelAlive) { if (el.Up().Value == SentinelAlive) { roomTile.Tile = Tile.RoomBackRight; } else if (el.Down().Value == SentinelAlive) { roomTile.Tile = Tile.RoomFrontRight; roomTile.Down().Tile = Tile.InsideWall; } else { roomTile.Tile = Tile.RoomRight; } } else if (el.Up().Value == SentinelAlive) { roomTile.Tile = Tile.RoomBackCenter; } else if (el.Down().Value == SentinelAlive) { roomTile.Tile = Tile.RoomFrontCenter; roomTile.Down().Tile = Tile.InsideWall; } else { roomTile.Tile = Tile.RoomCenter; } } // Carve out a door to all the accessible room tiles. var roomTiles = rooms.Where(el => el.Tile == Tile.RoomCenter); var doorways = new List <MapElement> { }; foreach (var innerTile in roomTiles) { var results = FloodFill(rooms, innerTile.Coord, new List <Tile> { Tile.RoomCenter, Tile.RoomFrontCenter, Tile.Door }); if (results[Tile.Door].Any()) { continue; } var potentialDoorways = results[Tile.RoomFrontCenter]; if (potentialDoorways.Any()) { var entryway = potentialDoorways.SpliceRandom(rng); var door = entryway.Down(); var doorway = door.Down(); System.Diagnostics.Debug.Assert(door.Tile == Tile.InsideWall); if (doorway.Value != SentinelAlive) { throw new InsaneException("Doorway not available."); } door.Tile = Tile.Door; doorway.Tile = Tile.Doorway; doorways.Add(doorway); } } // Place chests now var chestLocations = roomTiles.Where(el => el.Up().Tile == Tile.RoomBackCenter).ToList(); if (reqs.Objects.Count() > chestLocations.Count()) { throw new InsaneException("Not enough chest locations."); } foreach (byte chest in reqs.Objects) { chestLocations.SpliceRandom(rng).Value = chest; } // Deaden to prepare for second run //foreach (var el in rooms.Where(el => el.Value == SentinelAlive)) { el.Value = SentinelDead; } // Place non-room walls now. /* * for (int sanity2 = 0; sanity2 <501; ++sanity2) * { * if (sanity2 == 500) * throw new InsaneException("Couldn't make floors walls."); * * var walled = rooms.Clone(); * var entrances = doorways.ToList(); * //InitializeMap(rng, walled, 0.4); * walled = DoSimulationStep(walled, 5, 4, 4); * * entrances.ForEach(entrance => * { * foreach (var tile in entrance.Surrounding().Where(el => el.Value == SentinelDead)) * { * walled[tile.Coord].Value = SentinelAlive; * } * }); * * complete.Map = walled; * Console.Write(complete.AsText()); * bool success = false; // eww * * // Find a big enough segment to be the main area. * while (walled.Any(el => el.Value == SentinelAlive)) * { * var tile = walled.First(el => el.Value == SentinelAlive); * var results = FloodFill(walled, tile.Coord, new List<Tile> { reqs.Floor, Tile.Doorway }, new List<Tile> { (Tile)SentinelAlive }, reqs.Floor); * * if (results[reqs.Floor].Count() < 500) * { * // Small section just mark as walls. * FloodFill(walled, tile.Coord, new List<Tile> { reqs.Floor }, new List<Tile> { reqs.Floor }, (Tile)SentinelDead); * } * else * { * // This is the big section. Make sure all the doors are accessible * success = results[Tile.Doorway].Count() == doorways.Count(); * break; * } * } * * if (success) * { * foreach (var el in walled.Where(el => el.Value == SentinelDead)) * { * if (el.Up().Tile == reqs.Floor || el.Down().Tile == reqs.Floor || el.Left().Tile == reqs.Floor || el.Right().Tile == reqs.Floor) * { * el.Tile = reqs.Barrier; * } else * { * el.Tile = reqs.OutOfBounds; * } * } * complete.Map = walled; * break; * } * } */ // All the tiles we're editing are now either SentinelAlive or SentinelDead. // Time to map those to real values now. foreach (var el in complete.Map.Where(el => el.Value == SentinelAlive)) { el.Tile = reqs.Floor; } Console.WriteLine($"Room map has {complete.Map.Count(element => element.Tile == reqs.Floor)} walkable tiles."); Console.Write(complete.AsText()); // Pad all the Alive tiles var locations = complete.Map.Where(element => element.Tile == reqs.Floor).ToList(); reqs.Portals.ToList().ForEach(portal => { var location = locations.SpliceRandom(rng); complete.Map[location.Y, location.X] = portal; if (portal == (byte)Tile.WarpUp) { complete.Entrance = new Coordinate((byte)location.X, (byte)location.Y, CoordinateLocale.Standard); var finalResult = FloodFill(complete.Map, location.Coord, new List <Tile> { reqs.Floor, Tile.WarpUp, Tile.Doorway, Tile.Door, Tile.RoomCenter, Tile.RoomFrontCenter }); if (finalResult[Tile.Door].Count() < doorways.Count()) { throw new InsaneException("Can't reach all rooms."); } } }); Console.Write(complete.AsText()); return(complete); } catch (InsaneException e) { Console.WriteLine(e.ToString()); } } }