Пример #1
0
        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);
        }
Пример #2
0
        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());
                }
            }
        }