diff --git a/source/generator/Generator.cpp b/source/generator/Generator.cpp index 46f36f2..d39e27f 100644 --- a/source/generator/Generator.cpp +++ b/source/generator/Generator.cpp @@ -8,7 +8,6 @@ #include "Generator.h" #include -#include #include #include @@ -20,9 +19,15 @@ #include "../Pathfinder.h" #include "../World.h" -/// For usage with simplexnoise.h +/// Seed for usage with simplexnoise.h uint8_t perm[512]; +/** + * Amount of tiles extra to generate, to get consistent walls + * across multiple generateTiles calls for bordering areas. + */ +const int Generator::MARGIN = 10; + /** * Generates new random seed. */ @@ -32,17 +37,15 @@ Generator::Generator(World& world, Pathfinder& pathfinder) : std::mt19937 mersenne(time(nullptr)); std::uniform_int_distribution distribution(0, 255); - for (int i = 0; i < 512; i++) { + for (int i = 0; i < 512; i++) perm[i] = distribution(mersenne); - } } /** - * Fill Tile with procedurally generated tiles. + * Fill world with procedurally generated tiles. * - * @param tm Tile instance to set tiles in. - * @param area Size and position of area to generate tiles for. Must be - * power of two. + * @param area Size and position of area to generate tiles for. Width and + * height must each be a power of two. */ void Generator::generateTiles(const sf::IntRect& area) { @@ -50,37 +53,35 @@ Generator::generateTiles(const sf::IntRect& area) { assert(area.width && !(area.width & (area.width - 1))); assert(area.height && !(area.height & (area.height - 1))); - std::vector > - noise(area.width, std::vector(area.height)); - std::vector > - filtered(area.width, std::vector( - area.height, Tile::Type::FLOOR)); - - for (int x = area.left; x < area.left + area.width; x++) { - for (int y = area.top; y < area.top + area.height; y++) { - noise[x-area.left][y-area.top] = + array noise; + array filtered; + for (int x = area.left - MARGIN; x < area.left + area.width + MARGIN; x++) { + for (int y = area.top - MARGIN; y < area.top + area.height + MARGIN; y++) { + noise[x][y] = (scaled_octave_noise_2d(2, 2, 0.05f, 0.5f, -0.5f, x, y) + scaled_octave_noise_2d(2, 2, 0.5f, 0.15f, -0.15f, x, y) < -0.1f) - ? Tile::Type::WALL - : Tile::Type::FLOOR; + ? type::WALL + : type::FLOOR; } } - for (int x = 0; x < (int) noise.size(); x++) { - for (int y = 0; y < (int) noise[x].size(); y++) { + fill(filtered, area, type::FLOOR); + + for (int x = area.left; x < area.left + area.width; x++) { + for (int y = area.top; y < area.top + area.height; y++) { filterWalls(noise, filtered, x, y, 2, 1, 0); filterWalls(noise, filtered, x, y, 6, 1, 2); filterWalls(noise, filtered, x, y, 10, 1, 4); } } + for (int x = area.left; x < area.left + area.width; x++) { for (int y = area.top; y < area.top + area.height; y++) { mWorld.insert(std::shared_ptr( - new Tile(filtered[x-area.left][y-area.top], x, y))); + new Tile(filtered.at(x).at(y), x, y))); } } - generateAreas(filtered, area, - sf::Vector2f(area.left, area.top)); + generateAreas(filtered, area, sf::Vector2f(area.left, area.top)); mPathfinder.generatePortals(); mGenerated = filtered; } @@ -88,34 +89,30 @@ Generator::generateTiles(const sf::IntRect& area) { /** * Fills a rectangular area with the specified value. * - * @param[in] Rectangular map. + * @param[in,out] Array to set values to. * @param area The area to fill. * @param value The value to set. */ void -Generator::fill(std::vector >& image, - const sf::IntRect& area, Tile::Type value) { - for (int x = area.left; - x < area.left + area.width && x < (int) image.size(); x++) { - for (int y = area.top; - y < area.top + area.height && y < (int) image[x].size(); y++) { +Generator::fill(array& image, const sf::IntRect& area, Tile::Type value) { + for (int x = area.left; x < area.left + area.width; x++) { + for (int y = area.top; y < area.top + area.height; y++) image[x][y] = value; - } } } /** - * Returns the number of walls in the area in tiles. + * Counts and returns the number of walls within the area. * + * @param[in] tiles Array of tile values. * @param area The area to count in. - * @param tiles Array of tile values. */ -int Generator::countWalls(const sf::IntRect& area, - std::vector >& tiles) { +int +Generator::countWalls(const array& tiles, const sf::IntRect& area) { int count = 0; for (int x = area.left; x < area.left + area.width; x++) { for (int y = area.top; y < area.top + area.height; y++) - count += (int) (tiles[x][y] == Tile::Type::WALL); + count += (int) (tiles.at(x).at(y) == type::WALL); } return count; } @@ -124,8 +121,9 @@ int Generator::countWalls(const sf::IntRect& area, * Finds rectangles of specific size inside vector in and * puts them into vector out. * - * @param[in] in Rectangular map of tiles. - * @param[out] out Rectangular map of tiles. + * @param[in] in Perlin noise values. + * @param[in,out] out Tiles to be placed. Does not explicitly set floor values + * (keeps previous values). * @param x Position to check from (top left corner for rectangle). * @param y Position to check from (top left corner for rectangle). * @param longside Length of the longer side of the rectangle. @@ -134,39 +132,31 @@ int Generator::countWalls(const sf::IntRect& area, * tiles is not walls (tilecount >= longside * shortside - subtract). */ void -Generator::filterWalls(std::vector >& in, - std::vector >& out, - int x, int y, int longside, int shortside, int subtract) { - // Skip if we would go out of range. - if ((x + longside >= (int) in.size()) || - (y + longside >= (int) in[0].size())) - return; - +Generator::filterWalls(const array& in, array& out, int x, int y, int longside, + int shortside, int subtract) { // Filter in horizontal direction. - if (countWalls(sf::IntRect(x, y, longside, shortside), in) >= + if (countWalls(in, sf::IntRect(x, y, longside, shortside)) >= shortside * longside - subtract) - fill(out, sf::IntRect(x, y, longside, shortside), Tile::Type::WALL); + fill(out, sf::IntRect(x, y, longside, shortside), type::WALL); // Filter in vertical direction. - if (countWalls(sf::IntRect(x, y, shortside, longside), in) >= + if (countWalls(in, sf::IntRect(x, y, shortside, longside)) >= shortside * longside - subtract) - fill(out, sf::IntRect(x, y, shortside, longside), Tile::Type::WALL); + fill(out, sf::IntRect(x, y, shortside, longside), type::WALL); } /** * Inserts tile if all values within area are the same, otherwise divides area * into four and continues recursively. * - * @param tiles Array of tile values. + * @param in Array of tile values. * @param area The area to generate areas for. * @param offset Offset of tiles[0][0] from World coordinate (0, 0). */ void -Generator::generateAreas( - std::vector >& tiles, - const sf::IntRect& area, const sf::Vector2f& offset) { +Generator::generateAreas(const array& in, const sf::IntRect& area, + const sf::Vector2f& offset) const { assert(area.width > 0 && area.height > 0); - int count = countWalls(sf::IntRect(area.left - offset.y, area.top - offset.x, - area.width, area.height), tiles); + int count = countWalls(in, area); if (count == 0) { mPathfinder.insertArea(sf::IntRect(area)); } @@ -176,13 +166,13 @@ Generator::generateAreas( else { int halfWidth = area.width / 2.0f; int halfHeight = area.height / 2.0f; - generateAreas(tiles, sf::IntRect(area.left, + generateAreas(in, sf::IntRect(area.left, area.top, halfWidth, halfHeight), offset); - generateAreas(tiles, sf::IntRect(area.left + halfWidth, + generateAreas(in, sf::IntRect(area.left + halfWidth, area.top, halfWidth, halfHeight), offset); - generateAreas(tiles, sf::IntRect(area.left, + generateAreas(in, sf::IntRect(area.left, area.top + halfHeight, halfWidth, halfHeight), offset); - generateAreas(tiles, sf::IntRect(area.left + halfWidth, + generateAreas(in, sf::IntRect(area.left + halfWidth, area.top + halfHeight, halfWidth, halfHeight), offset); } } @@ -192,15 +182,15 @@ Generator::generateAreas( */ sf::Vector2f Generator::getPlayerSpawn() const { - sf::Vector2i spawn = findClosestFloor(sf::Vector2i(mGenerated.size() / 2, - mGenerated[0].size() / 2)); - return sf::Vector2f( - (spawn.x - mGenerated.size() / 2.0f) * Tile::TILE_SIZE.x, - (spawn.y - mGenerated[0].size() / 2.0f) * Tile::TILE_SIZE.y); + sf::Vector2i spawn = findClosestFloor(sf::Vector2i(0, 0)); + return sf::Vector2f(spawn.x * Tile::TILE_SIZE.x, + spawn.y * Tile::TILE_SIZE.y); } /** * Finds the point array index closest to position which has a floor tile. + * + * @position Point to start search for a floor tile from. */ sf::Vector2i Generator::findClosestFloor(const sf::Vector2i& position) const { @@ -219,7 +209,7 @@ Generator::findClosestFloor(const sf::Vector2i& position) const { const sf::Vector2i& current = open.begin()->first; open.erase(current); closed.insert(current); - if (mGenerated[current.x][current.y] == Tile::Type::FLOOR) + if (mGenerated.at(current.x).at(current.y) == Tile::Type::FLOOR) return current; else { if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end()) diff --git a/source/generator/Generator.h b/source/generator/Generator.h index 7e9e841..bacd7f4 100644 --- a/source/generator/Generator.h +++ b/source/generator/Generator.h @@ -22,21 +22,23 @@ public: sf::Vector2f getPlayerSpawn() const; private: - sf::Vector2i findClosestFloor(const sf::Vector2i& position) const; - static void fill(std::vector >& image, - const sf::IntRect& area, Tile::Type value); - static void filterWalls(std::vector >& in, - std::vector >& out, - int x, int y, int longside, int shortside, int subtract); - static int countWalls(const sf::IntRect& area, - std::vector >& tiles); - void generateAreas(std::vector >& tiles, - const sf::IntRect& area, const sf::Vector2f& offset); + typedef Tile::Type type; + typedef std::map > array; private: + sf::Vector2i findClosestFloor(const sf::Vector2i& position) const; + static void fill(array& in, const sf::IntRect& area, type value); + static void filterWalls(const array& in, array& out, int x, int y, + int longside, int shortside, int subtract); + static int countWalls(const array& in, const sf::IntRect& area); + void generateAreas(const array& in, const sf::IntRect& area, + const sf::Vector2f& offset) const; + +private: + static const int MARGIN; World& mWorld; Pathfinder& mPathfinder; - std::vector > mGenerated; + array mGenerated; }; #endif /* DG_GENERATOR_H_ */