| @@ -0,0 +1,41 @@ | ||
| cmake_minimum_required(VERSION 2.6) | ||
| project(PCGBSPDungeonGen) | ||
|
|
||
| set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR/DIST}") | ||
| set(CMAKE_CXX_FLAGS "-std=c++11 -static-libstdc++ ${CMAKE_CXX_FLAGS}") | ||
|
|
||
| # Project Options/Settings | ||
|
|
||
| set(PCG_DUNGEONGEN_VERSION_MAJOR 0) | ||
| set(PCG_DUNGEONGEN_VERSION_MINOR 1) | ||
|
|
||
| option(BUILD_DEMO | ||
| "Build the demos/examples" | ||
| ON | ||
| ) | ||
|
|
||
| if (BUILD_DEMO) | ||
| add_subdirectory(source_demo) | ||
| endif (BUILD_DEMO) | ||
|
|
||
|
|
||
| # Dependencies | ||
| # - | ||
|
|
||
|
|
||
| # Sources and Building | ||
|
|
||
| include_directories( | ||
| "${PROJECT_SOURCE_DIR}/include" | ||
| ) | ||
|
|
||
| file(GLOB SourceFiles | ||
| "source/*.cpp" | ||
| ) | ||
|
|
||
| add_library(PCG_BSPDungeonGen STATIC ${SourceFiles}) | ||
|
|
||
|
|
||
| # Installation | ||
| install(TARGETS PCG_BSPDungeonGen DESTINATION lib) | ||
| install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/PCG-BSPDungeonGen DESTINATION include) |
| @@ -0,0 +1,21 @@ | ||
| The MIT License (MIT) | ||
|
|
||
| Copyright (c) 2014 George Koutsikos | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| THE SOFTWARE. |
| @@ -0,0 +1,15 @@ | ||
| #ifndef PCG_CORRIDOR_H | ||
| #define PCG_CORRIDOR_H | ||
|
|
||
|
|
||
| #include "Vec2.h" | ||
| #include <vector> | ||
|
|
||
|
|
||
| typedef std::vector<Vec2> Path; | ||
|
|
||
| // Simplistic grid-based pathfinding | ||
| Path FindPath(Vec2 begin, Vec2 end); | ||
|
|
||
|
|
||
| #endif |
| @@ -0,0 +1,74 @@ | ||
| #ifndef PCG_DUNGEON_H | ||
| #define PCG_DUNGEON_H | ||
|
|
||
|
|
||
| #include <vector> | ||
| #include <queue> | ||
| #include <map> | ||
| #include <algorithm> | ||
| #include <random> | ||
| #include <string> | ||
|
|
||
| #include "AABB.h" | ||
| #include "Node.h" | ||
|
|
||
|
|
||
| typedef AABB Room; | ||
| typedef std::vector<Vec2> Path; | ||
| typedef std::vector<unsigned int> GridLine; | ||
| typedef std::vector<std::vector<unsigned int> > Grid; | ||
|
|
||
|
|
||
| /*! Class representing a randomly generated dungeon | ||
| */ | ||
| class Dungeon { | ||
| public: | ||
| Dungeon(std::string seed, int width, int height); | ||
|
|
||
| ~Dungeon(); | ||
|
|
||
| enum TILE_TYPE { | ||
| Empty = 0, Floor = 1, Corridor = 2, Entrance = 3, Exit = 4, Door = 5, Treasure = 6, Monster = 7, Trap = 8 | ||
| }; | ||
| private: | ||
| int mWidth; | ||
| int mHeight; | ||
| int mUnitSquare; | ||
| std::mt19937 mRandGen; | ||
| std::uniform_real_distribution<float> mUniDistr; | ||
| std::seed_seq mSeedSeq; | ||
| std::string mSeedString; | ||
| std::vector<Room> mRooms; | ||
| std::vector<Path> mCorridors; | ||
| std::vector<Vec2> mTreasures; | ||
| std::vector<Vec2> mMonsters; | ||
| std::vector<Vec2> mTraps; | ||
| Vec2 mEntrance; | ||
| Vec2 mExit; | ||
| Grid mGrid; | ||
| Node<AABB> mRootNode; | ||
| public: | ||
| void Generate(void); | ||
|
|
||
| Grid GetGrid(void); | ||
|
|
||
| private: | ||
| void ClearGrid(); | ||
|
|
||
| void SplitSpace(Node<AABB> *node); | ||
|
|
||
| void FindRoomsDigCorridors(); | ||
|
|
||
| void PlaceEntranceAndExit(); | ||
|
|
||
| void BakeFloor(); | ||
|
|
||
| void PlaceDoors(); | ||
|
|
||
| void PlaceTreasureAndMonsters(); | ||
|
|
||
| void BakeDetails(); | ||
| }; | ||
|
|
||
|
|
||
| #endif |
| @@ -0,0 +1,98 @@ | ||
| -- ************************************* -- | ||
| -- BSP Based Dungeon Generation -- | ||
| -- ************************************* -- | ||
|
|
||
| -- Instructions: | ||
| -- | ||
| -- Open a command line app and run | ||
| -- premake4 one_of_these__gmake__vs2010__xcode__codeblocks | ||
|
|
||
| _ACTION = _ACTION or 'gmake' | ||
|
|
||
| -- _ _ | ||
| -- | | (_) | ||
| -- ___ _ __ | |_ _ ___ _ __ ___ | ||
| -- / _ \| '_ \| __| |/ _ \| '_ \/ __| | ||
| -- | (_) | |_) | |_| | (_) | | | \__ \ | ||
| -- \___/| .__/ \__|_|\___/|_| |_|___/ | ||
| -- | | | ||
| -- |_| | ||
|
|
||
| -- No options for now... | ||
|
|
||
|
|
||
| -- _ _ _ | ||
| -- | | | | (_) | ||
| -- ___ ___ | |_ _| |_ _ ___ _ __ ___ | ||
| -- / __|/ _ \| | | | | __| |/ _ \| '_ \/ __| | ||
| -- \__ \ (_) | | |_| | |_| | (_) | | | \__ \ | ||
| -- |___/\___/|_|\__,_|\__|_|\___/|_| |_|___/ | ||
|
|
||
|
|
||
| solution "PCG-BSPDungeonGen" | ||
| configurations { "Debug", "Release" } | ||
| location "./build" | ||
| targetdir "./build" | ||
|
|
||
|
|
||
| project "PCG-BSPDungeonGenLib" | ||
| kind "SharedLib" | ||
| language "C++" | ||
| location "./build/library" | ||
|
|
||
| includedirs { | ||
| path.getabsolute("./include/") | ||
| } | ||
|
|
||
| files { | ||
| "source/AABB.cpp", | ||
| "source/Node.cpp", | ||
| "source/Corridor.cpp", | ||
| "source/Dungeon.cpp" | ||
| } -- .cpp files | ||
|
|
||
| configuration "Debug" | ||
| defines { "DEBUG" } | ||
| flags { "Symbols" } | ||
|
|
||
| configuration "Release" | ||
| defines { "NDEBUG" } | ||
| flags { "Optimize" } | ||
|
|
||
| configuration { "windows", "codelite" } | ||
| buildoptions { "-std=c++11" } | ||
|
|
||
| configuration { "linux", "gmake" } | ||
| buildoptions { "-std=c++11" } | ||
|
|
||
|
|
||
| project "PCG-BSPDungeonGenDemo" | ||
| kind "ConsoleApp" | ||
| language "C++" | ||
| location "./build/demo" | ||
|
|
||
| includedirs { | ||
| path.getabsolute("./include/") | ||
| } | ||
|
|
||
| files { | ||
| "source_demo/Main.cpp" | ||
| } -- .cpp files | ||
|
|
||
| links { | ||
| "PCG-BSPDungeonGenLib" | ||
| } | ||
|
|
||
| configuration "Debug" | ||
| defines { "DEBUG" } | ||
| flags { "Symbols" } | ||
|
|
||
| configuration "Release" | ||
| defines { "NDEBUG" } | ||
| flags { "Optimize" } | ||
|
|
||
| configuration { "windows", "codelite" } | ||
| buildoptions { "-std=c++11" } | ||
|
|
||
| configuration { "linux", "gmake" } | ||
| buildoptions { "-std=c++11" } |
| @@ -0,0 +1,50 @@ | ||
| #include "PCG-BSPDungeonGen\Corridor.h" | ||
|
|
||
|
|
||
| #include <cmath> | ||
|
|
||
|
|
||
| /*! Function that returns a list of points | ||
| * for each neighbouring grid cell of the | ||
| * function's center parameter | ||
| */ | ||
| std::vector<Vec2> GetNeighbours(Vec2 ¢er) { | ||
| std::vector<Vec2> neighbours; | ||
| neighbours.push_back(Vec2(center.x + 1, center.y)); | ||
| neighbours.push_back(Vec2(center.x - 1, center.y)); | ||
| neighbours.push_back(Vec2(center.x, center.y + 1)); | ||
| neighbours.push_back(Vec2(center.x, center.y - 1)); | ||
| return neighbours; | ||
| } | ||
|
|
||
|
|
||
| // Simplistic grid-based pathfinding | ||
| Path FindPath(Vec2 begin, Vec2 end) { | ||
|
|
||
| // Create needed variables and init | ||
| // with the starting point. | ||
| Path result; | ||
| result.push_back(begin); | ||
| Vec2 current = begin; | ||
|
|
||
| do { | ||
| // Get neighbours | ||
| Path neighbours = GetNeighbours(current); | ||
| // Find nearest | ||
| int nearestIndex; | ||
| int nearestRange = 1000; | ||
| for (int i = 0; i < 4; ++i) { | ||
| if ((std::abs(neighbours[i].x - end.x) + std::abs(neighbours[i].y - end.y)) < nearestRange) { | ||
| nearestRange = (std::abs(neighbours[i].x - end.x) + std::abs(neighbours[i].y - end.y)); | ||
| nearestIndex = i; | ||
| } | ||
| } | ||
| // save up and continue... | ||
| current = neighbours[nearestIndex]; | ||
| result.push_back(current); | ||
| } while (current != end); //... until we reach the end point | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
|
|
| @@ -0,0 +1,289 @@ | ||
| #include "PCG-BSPDungeonGen\Dungeon.h" | ||
|
|
||
|
|
||
| #include <iostream> | ||
| #include <cmath> | ||
|
|
||
| #include "PCG-BSPDungeonGen\Corridor.h" | ||
|
|
||
|
|
||
| #define RAND_GEN_PERCENTAGE (float)mUniDistr(mRandGen) | ||
|
|
||
|
|
||
| // Constructors | ||
| Dungeon::Dungeon(std::string seed, int width, int height) : mWidth(width), mHeight(height), | ||
| mRootNode(nullptr, AABB(0, 0, width, height)) { | ||
| // init grid | ||
| mGrid = std::vector<std::vector<unsigned int> >(mHeight, std::vector<unsigned int>(mWidth, TILE_TYPE::Empty)); | ||
|
|
||
| mSeedString = seed; | ||
| mSeedSeq = std::seed_seq(mSeedString.begin(), mSeedString.end()); | ||
| mRandGen = std::mt19937(mSeedSeq); | ||
| mUniDistr = std::uniform_real_distribution<float>(0.0f, 1.0f); | ||
| } | ||
|
|
||
|
|
||
| Dungeon::~Dungeon() { } | ||
|
|
||
|
|
||
| #define DEBUG 1 | ||
|
|
||
| // Public Methods | ||
| void Dungeon::Generate() { | ||
| // Clean-up if already generated | ||
| mRootNode = Node<AABB>(nullptr, AABB(0, 0, mWidth, mHeight)); | ||
| mRooms.clear(); | ||
| mCorridors.clear(); | ||
| mTreasures.clear(); | ||
| mMonsters.clear(); | ||
| mTraps.clear(); | ||
| ClearGrid(); | ||
|
|
||
| // Generate dungeon parts | ||
| SplitSpace(&mRootNode); | ||
| FindRoomsDigCorridors(); | ||
| BakeFloor(); | ||
| PlaceEntranceAndExit(); | ||
| PlaceDoors(); | ||
| PlaceTreasureAndMonsters(); | ||
| BakeDetails(); | ||
|
|
||
| std::cout << "Dungeon Generation complete!" << std::endl; | ||
|
|
||
| #ifdef DEBUG | ||
| for (int i = 0; i < mGrid.size(); i++) { | ||
| for (int j = 0; j < mGrid[i].size(); j++) { | ||
| std::cout << mGrid[i][j]; | ||
| } | ||
| std::cout << std::endl; | ||
| } | ||
| #endif | ||
| } | ||
|
|
||
|
|
||
| Grid Dungeon::GetGrid(void) { | ||
| return mGrid; | ||
| } | ||
|
|
||
|
|
||
| // Private Methods | ||
| void Dungeon::ClearGrid() { | ||
| mGrid.clear(); | ||
| mGrid = std::vector<std::vector<unsigned int> >(mHeight, std::vector<unsigned int>(mWidth, TILE_TYPE::Empty)); | ||
| } | ||
|
|
||
|
|
||
| void Dungeon::SplitSpace(Node <AABB> *node) { | ||
|
|
||
| // Choose how and where to split | ||
| float ratio = (float) node->GetData().getWidth() / node->GetData().getHeight(); | ||
| bool splitVertical = true; | ||
| if (ratio < 1.0f) | ||
| splitVertical = false; | ||
|
|
||
|
|
||
| float split = RAND_GEN_PERCENTAGE; | ||
| do { | ||
| split = RAND_GEN_PERCENTAGE; | ||
| } while (split < 0.4f || split > 0.6f); | ||
|
|
||
|
|
||
| // Create and calculate the 2 subspaces | ||
| // of the splitted one. | ||
| AABB subspaceA, subspaceB; | ||
|
|
||
| if (splitVertical) { | ||
| int splitX = node->GetData().X() + (int) (split * node->GetData().getWidth()); | ||
| subspaceA = AABB(node->GetData().X(), node->GetData().Y(), | ||
| (int) (split * node->GetData().getWidth()), node->GetData().getHeight()); | ||
| subspaceB = AABB(splitX, node->GetData().Y(), | ||
| (int) ((1 - split) * node->GetData().getWidth()), node->GetData().getHeight()); | ||
| } else { | ||
| int splitY = node->GetData().Y() + (int) (split * node->GetData().getHeight()); | ||
| subspaceA = AABB(node->GetData().X(), node->GetData().Y(), | ||
| node->GetData().getWidth(), (int) (split * node->GetData().getHeight())); | ||
| subspaceB = AABB(node->GetData().X(), splitY, | ||
| node->GetData().getWidth(), (int) ((1 - split) * node->GetData().getHeight())); | ||
| } | ||
|
|
||
| #ifdef DEBUG | ||
| std::cout << "Splitting [" << node->GetData().X() << ", " << node->GetData().Y() << ", " << | ||
| node->GetData().getWidth() << ", " << node->GetData().getHeight() << "] into:" << std::endl; | ||
| std::cout << "Space A: [" << subspaceA.X() << ", " << subspaceA.Y() << ", " << subspaceA.getWidth() << ", " << | ||
| subspaceA.getHeight() << "]" << std::endl; | ||
| std::cout << "Space B: [" << subspaceB.X() << ", " << subspaceB.Y() << ", " << subspaceB.getWidth() << ", " << | ||
| subspaceB.getHeight() << "]" << std::endl; | ||
| std::cout << std::endl; | ||
| #endif | ||
|
|
||
| // Add subspaces to the current node | ||
| node->MakeLeftChild(subspaceA); | ||
| node->MakeRightChild(subspaceB); | ||
|
|
||
| // Decide if we need to split more | ||
| // and continue recursion. | ||
| if (subspaceA.getWidth() > 7 && subspaceA.getHeight() > 6) | ||
| SplitSpace(node->GetLeftChild()); | ||
| if (subspaceB.getWidth() > 7 && subspaceB.getHeight() > 6) | ||
| SplitSpace(node->GetRightChild()); | ||
|
|
||
| } // method ends here | ||
|
|
||
|
|
||
| void Dungeon::FindRoomsDigCorridors() { | ||
| Node<AABB>::NodeIterator it(&mRootNode); | ||
|
|
||
| // Iterate over bsp-tree and add Rooms that | ||
| // adhere to the minimum size required | ||
| while (it.Next() != false) { | ||
| if (it.IsLeaf() == true && it.GetData().getWidth() > 3 && it.GetData().getHeight() > 3) { | ||
| mRooms.push_back(AABB(it.GetData().X() + 1, it.GetData().Y() + 1, it.GetData().getWidth() - 2, | ||
| it.GetData().getHeight() - 2)); | ||
| #ifdef DEBUG | ||
| std::cout << "Added [" << it.GetData().X() << ", " << it.GetData().Y() << ", " << it.GetData().getWidth() << | ||
| ", " << it.GetData().getHeight() << "]" << std::endl; | ||
| #endif | ||
| } | ||
| } | ||
|
|
||
| it.Reset(); | ||
|
|
||
| // Re-iterate over bsp-tree and create | ||
| // corridors using pathfind function (grid-based) | ||
| while (it.Next() != false) { | ||
| if (!it.IsLeaf()) { | ||
| Path corridor = FindPath(it.GetNode()->GetLeftChild()->GetData().getCenter(), | ||
| it.GetNode()->GetRightChild()->GetData().getCenter()); | ||
| mCorridors.push_back(corridor); | ||
|
|
||
| #ifdef DEBUG | ||
| std::cout << "Corridor points from [" << it.GetNode()->GetLeftChild()->GetData().getCenter().x << ", " << | ||
| it.GetNode()->GetLeftChild()->GetData().getCenter().y << "]" << std::endl; | ||
| std::cout << " to [" << it.GetNode()->GetRightChild()->GetData().getCenter().x << ", " << | ||
| it.GetNode()->GetRightChild()->GetData().getCenter().y << "]" << std::endl; | ||
| for (Path::iterator itP = corridor.begin(); itP != corridor.end(); itP++) { | ||
| std::cout << itP->x << ", " << itP->y << std::endl; | ||
| } | ||
| std::cout << std::endl; | ||
| #endif | ||
| } | ||
| } | ||
|
|
||
| } // method ends here | ||
|
|
||
|
|
||
| void Dungeon::PlaceEntranceAndExit() { | ||
|
|
||
| int i, j; // i is the index of the room with the entrance | ||
| // j is the index of the room with the exit | ||
| i = j = 0; | ||
|
|
||
| // There are N rooms, choose if entrance will be in one | ||
| // of the rooms of the first half (0 to N/2) or on the | ||
| // second (N/2 to N). Exit will be on the other. | ||
| if (RAND_GEN_PERCENTAGE > 0.5f) { | ||
| i = floorf(RAND_GEN_PERCENTAGE * (mRooms.size() / 2.0f)); | ||
| j = floorf((mRooms.size() / 2.0f) + RAND_GEN_PERCENTAGE * (mRooms.size() / 2.0f)); | ||
| } else { | ||
| j = floorf(RAND_GEN_PERCENTAGE * (mRooms.size() / 2.0f)); | ||
| i = floorf((mRooms.size() / 2.0f) + RAND_GEN_PERCENTAGE * (mRooms.size() / 2.0f)); | ||
| } | ||
|
|
||
| // Set the center of the rooms as entrance and exit | ||
| mEntrance = mRooms[i].getCenter(); | ||
| mExit = mRooms[j].getCenter(); | ||
|
|
||
| #ifdef DEBUG | ||
| std::cout << "Entrance: [" << mEntrance.x << ", " << mEntrance.y << "]" << std::endl; | ||
| std::cout << "Exit: [" << mExit.x << ", " << mExit.y << "]" << std::endl; | ||
| #endif | ||
|
|
||
| } // method ends here | ||
|
|
||
|
|
||
|
|
||
| void Dungeon::BakeFloor() { | ||
| std::cout << std::endl << "Baking data on mGrid..." << std::endl; | ||
|
|
||
| // Fill rooms on the grid with the proper id (floor=1) | ||
| for (std::vector<Room>::iterator it = mRooms.begin(); it != mRooms.end(); ++it) { | ||
| for (int i = it->Y(); i < it->Y() + it->getHeight(); i++) { | ||
| for (int j = it->X(); j < it->X() + it->getWidth(); j++) { | ||
| mGrid[i][j] = TILE_TYPE::Floor; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Fill corridors on the grid with the proper id (corridor=2) | ||
| for (std::vector<Path>::iterator it = mCorridors.begin(); it != mCorridors.end(); ++it) { | ||
| for (Path::iterator pathIt = it->begin(); pathIt != it->end(); ++pathIt) { | ||
| if (mGrid[pathIt->y][pathIt->x] != 1) | ||
| mGrid[pathIt->y][pathIt->x] = TILE_TYPE::Corridor; | ||
| } | ||
| } | ||
|
|
||
| } // method ends here | ||
|
|
||
|
|
||
|
|
||
| void Dungeon::PlaceDoors() { | ||
| // Detects corridor-room crossings and | ||
| // places doors (door=5) | ||
| // *fix* weird door placement sometimes | ||
| for (int i = 1; i < mGrid.size() - 1; i++) { | ||
| for (int j = 1; j < mGrid[i].size() - 1; j++) { | ||
| if (mGrid[i][j] == 2 && | ||
| (mGrid[i + 1][j] == 1 || mGrid[i - 1][j] == 1 || mGrid[i][j + 1] == 1 || mGrid[i][j - 1] == 1) && | ||
| (mGrid[i + 1][j] != 5 && mGrid[i - 1][j] != 5 && mGrid[i][j + 1] != 5 && mGrid[i][j - 1] != 5) && | ||
| ((mGrid[i + 1][j] == 0 && mGrid[i - 1][j] == 0) || (mGrid[i][j + 1] == 0 && mGrid[i][j - 1] == 0))) { | ||
| mGrid[i][j] = TILE_TYPE::Door; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| void Dungeon::PlaceTreasureAndMonsters() { | ||
| // Iterate rooms and place treasure, monsters and traps | ||
| for (std::vector<Room>::iterator it = mRooms.begin(); it != mRooms.end(); ++it) { | ||
| int scale = (int) ((float) it->getVolume() / 8.0f); | ||
| // Monsters | ||
| for (int i = 0; i < scale; i++) | ||
| mMonsters.push_back(Vec2(it->X() + (int) (it->getWidth() * RAND_GEN_PERCENTAGE), | ||
| it->Y() + (int) (it->getHeight() * RAND_GEN_PERCENTAGE))); | ||
| // Treasures | ||
| if (scale > 3) { | ||
| mTreasures.push_back(Vec2(it->X() + (int) (it->getWidth() * RAND_GEN_PERCENTAGE), | ||
| it->Y() + (int) (it->getHeight() * RAND_GEN_PERCENTAGE))); | ||
| mTreasures.push_back(Vec2(it->X() + (int) (it->getWidth() * RAND_GEN_PERCENTAGE), | ||
| it->Y() + (int) (it->getHeight() * RAND_GEN_PERCENTAGE))); | ||
| } else if (scale >= 1) | ||
| mTreasures.push_back(Vec2(it->X() + (int) (it->getWidth() * RAND_GEN_PERCENTAGE), | ||
| it->Y() + (int) (it->getHeight() * RAND_GEN_PERCENTAGE))); | ||
| // Traps | ||
| if (RAND_GEN_PERCENTAGE > 0.35f) | ||
| mTraps.push_back(Vec2(it->X() + (int) (it->getWidth() * RAND_GEN_PERCENTAGE), | ||
| it->Y() + (int) (it->getHeight() * RAND_GEN_PERCENTAGE))); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| void Dungeon::BakeDetails() { | ||
| // Write the details of the dungeon to its grid. | ||
| // Details = Entrance/Exit, Monsters, Treasure and Traps | ||
|
|
||
| // Entrance and exit... | ||
| mGrid[mEntrance.y][mEntrance.x] = TILE_TYPE::Entrance; | ||
| mGrid[mExit.y][mExit.x] = TILE_TYPE::Exit; | ||
| // Monsters... | ||
| for (std::vector<Vec2>::iterator it = mMonsters.begin(); it != mMonsters.end(); ++it) | ||
| mGrid[it->y][it->x] = TILE_TYPE::Monster; | ||
| // Treasure... | ||
| for (std::vector<Vec2>::iterator it = mTreasures.begin(); it != mTreasures.end(); ++it) | ||
| mGrid[it->y][it->x] = TILE_TYPE::Treasure; | ||
| // Traps... | ||
| for (std::vector<Vec2>::iterator it = mTraps.begin(); it != mTraps.end(); ++it) | ||
| mGrid[it->y][it->x] = TILE_TYPE::Trap; | ||
| } | ||
|
|
||
|
|
| @@ -0,0 +1,6 @@ | ||
| roguelike notes | ||
|
|
||
| * crawl thru megacity skyscraper ala dredd? pvp, fighting styles, slots for skills and augments | ||
| * roguelikes depend on augmentation and strategy | ||
| * DX and system shock both feature biomechanical augmentation as part of their character progression | ||
| * human vs augmented progression paths? |
| @@ -0,0 +1,70 @@ | ||
| #include "PCG-BSPDungeonGen\AABB.h" | ||
|
|
||
|
|
||
| // Constructors | ||
| AABB::AABB() { | ||
| mPosition.x = 0; | ||
| mPosition.y = 0; | ||
| mSize.x = 0; | ||
| mSize.y = 0; | ||
| } | ||
|
|
||
| AABB::AABB(int x, int y, int w, int h) { | ||
| mPosition.x = x; | ||
| mPosition.y = y; | ||
| mSize.x = w; | ||
| mSize.y = h; | ||
| } | ||
|
|
||
| AABB::~AABB() { } | ||
|
|
||
|
|
||
| // Methods | ||
| int AABB::X() const { | ||
| return mPosition.x; | ||
| } | ||
|
|
||
|
|
||
| int AABB::Y() const { | ||
| return mPosition.y; | ||
| } | ||
|
|
||
|
|
||
| Vec2 AABB::getCenter() const { | ||
| return Vec2(mPosition.x + mSize.x / 2, mPosition.y + mSize.y / 2); | ||
| } | ||
|
|
||
|
|
||
| int AABB::getHeight() const { | ||
| return mSize.y; | ||
| } | ||
|
|
||
|
|
||
| int AABB::getVolume() const { | ||
| return mSize.x * mSize.y; | ||
| } | ||
|
|
||
|
|
||
| int AABB::getWidth() const { | ||
| return mSize.x; | ||
| } | ||
|
|
||
|
|
||
| void AABB::setPosition(int x, int y) { | ||
| mPosition.x = x; | ||
| mPosition.y = y; | ||
| } | ||
|
|
||
|
|
||
| void AABB::setSize(int w, int h) { | ||
| mSize.x = w; | ||
| mSize.y = h; | ||
| } | ||
|
|
||
|
|
||
| bool AABB::isInside(Vec2 &p) const { | ||
| return (p.x >= mPosition.x && p.x <= (mPosition.x + mSize.x) && | ||
| p.y >= mPosition.y && p.y <= (mPosition.y + mSize.y)); | ||
| } | ||
|
|
||
|
|
| @@ -0,0 +1,4 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
|
|
| @@ -0,0 +1,122 @@ | ||
| #include "PCG-BSPDungeonGen\Node.h" | ||
| #include "PCG-BSPDungeonGen\AABB.h" | ||
|
|
||
| // Constructors | ||
| template<typename T> | ||
| Node<T>::Node(Node <T> *parent, T data) { | ||
| mpParent = parent; | ||
| mpLeft = nullptr; | ||
| mpRight = nullptr; | ||
| mData = data; | ||
| } | ||
|
|
||
| template<typename T> | ||
| Node<T>::~Node() { | ||
| if (mpLeft != nullptr) | ||
| delete mpLeft; | ||
| if (mpRight != nullptr) | ||
| delete mpRight; | ||
| } | ||
|
|
||
|
|
||
| // Methods | ||
| template<typename T> | ||
| T Node<T>::GetData() const { | ||
| return mData; | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| Node <T> *Node<T>::GetParent(void) const { | ||
| return mpParent; | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| Node <T> *Node<T>::GetLeftChild(void) const { | ||
| return mpLeft; | ||
| } | ||
|
|
||
| template<typename T> | ||
| void Node<T>::MakeLeftChild(T data) { | ||
| mpLeft = new Node(this, data); | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| Node <T> *Node<T>::GetRightChild(void) const { | ||
| return mpRight; | ||
| } | ||
|
|
||
| template<typename T> | ||
| void Node<T>::MakeRightChild(T data) { | ||
| mpRight = new Node(this, data); | ||
| } | ||
|
|
||
|
|
||
| // NodeIterator -- Public inner class | ||
| template<typename T> | ||
| Node<T>::NodeIterator::NodeIterator(Node *parent) { | ||
| mpRoot = parent; | ||
| mpCurrent = parent; | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| bool Node<T>::NodeIterator::Next() { | ||
| if (mpCurrent->GetLeftChild() != 0 && mVisited.find(mpCurrent->GetLeftChild()) == mVisited.end()) { | ||
| //mpCurrent->SetLeftVisited(); | ||
| mVisited.insert(mpCurrent->GetLeftChild()); | ||
| mpCurrent = mpCurrent->GetLeftChild(); | ||
| return true; | ||
| } | ||
|
|
||
| if (mpCurrent->GetRightChild() != 0 && mVisited.find(mpCurrent->GetRightChild()) == mVisited.end()) { | ||
| //mpCurrent->SetRightVisited(); | ||
| mVisited.insert(mpCurrent->GetRightChild()); | ||
| mpCurrent = mpCurrent->GetRightChild(); | ||
| return true; | ||
| } | ||
|
|
||
| if (mpCurrent->GetParent() != 0) { | ||
| mpCurrent = mpCurrent->GetParent(); | ||
| return true; | ||
| } else { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| T Node<T>::NodeIterator::GetData() const { | ||
| return mpCurrent->mData; | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| Node <T> *Node<T>::NodeIterator::GetNode() const { | ||
| return mpCurrent; | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| bool Node<T>::NodeIterator::IsLeaf() const { | ||
| if (mpCurrent->GetLeftChild() == 0 && mpCurrent->GetRightChild() == 0) | ||
| return true; | ||
| else | ||
| return false; | ||
| } | ||
|
|
||
|
|
||
| template<typename T> | ||
| void Node<T>::NodeIterator::Reset() { | ||
| mVisited.clear(); | ||
| mpCurrent = mpRoot; | ||
| } | ||
|
|
||
|
|
||
| // explicit template instantiation | ||
| template | ||
| class Node<AABB>; | ||
|
|
||
|
|
| @@ -0,0 +1,44 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
| #include "BearLibTerminal.h" | ||
| #include "engine.h" | ||
| #include "input.h" | ||
|
|
||
| int Engine::run() { | ||
| mainCharacter = Player(10, 10); | ||
| mainCharacter.render(); | ||
| terminal_refresh(); | ||
| while (terminal_read() != TK_CLOSE) { | ||
| input::checkForInputAndBlock(); | ||
| terminal_clear(); | ||
| printf("position is %d X %d Y\n", mainCharacter.playerPosition.posX(), mainCharacter.playerPosition.posY()); | ||
| } | ||
| terminal_close(); | ||
| return 0; | ||
| } | ||
|
|
||
| bool Engine::canMovePlayerTo(int x, int y) { | ||
| return true; | ||
| } | ||
|
|
||
| bool Engine::movePlayerTo(int newX, int newY) { | ||
| if (!canMovePlayerTo(newX, newY)) { | ||
| return false; | ||
| } else { | ||
| mainCharacter.playerPosition.setX(newX); | ||
| mainCharacter.playerPosition.setY(newY); | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| void Engine::movePlayerBy(int x, int y) { | ||
| mainCharacter.playerPosition.setX(mainCharacter.playerPosition.posX() + x); | ||
| mainCharacter.playerPosition.setY(mainCharacter.playerPosition.posY() + y); | ||
| mainCharacter.render(); | ||
| terminal_refresh(); | ||
| } | ||
|
|
||
| const Player Engine::getCurrentPlayer() { | ||
| return this->mainCharacter; | ||
| } |
| @@ -0,0 +1,35 @@ | ||
| #ifndef PCG_AABB_H | ||
| #define PCG_AABB_H | ||
|
|
||
|
|
||
|
|
||
| #include "Vec2.h" | ||
|
|
||
|
|
||
| /*! Class for Axis-Aligned Bounding Box | ||
| * Instances of this class are used to represent | ||
| * a region in 2d space. | ||
| */ | ||
| class AABB { | ||
| public: | ||
| AABB(); | ||
| AABB(int x, int y, int w, int h); | ||
| ~AABB(); | ||
| private: | ||
| Vec2 mPosition; | ||
| Vec2 mSize; | ||
| public: | ||
| int getWidth() const; | ||
| int getHeight() const; | ||
| int getVolume() const; | ||
| Vec2 getCenter() const; | ||
| int X() const; | ||
| int Y() const; | ||
| void setPosition(int x, int y); | ||
| void setSize(int w, int h); | ||
| bool isInside(Vec2 &p) const; | ||
| }; | ||
|
|
||
|
|
||
|
|
||
| #endif |
| @@ -0,0 +1,14 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
|
|
||
| #ifndef SNOWDEVICE_INVENTORY_H | ||
| #define SNOWDEVICE_INVENTORY_H | ||
|
|
||
|
|
||
| class Inventory { | ||
|
|
||
| }; | ||
|
|
||
|
|
||
| #endif //SNOWDEVICE_INVENTORY_H |
| @@ -0,0 +1,46 @@ | ||
| #ifndef PCG_NODE_H | ||
| #define PCG_NODE_H | ||
|
|
||
|
|
||
|
|
||
| #include <unordered_set> | ||
|
|
||
| template<typename T> | ||
| class Node { | ||
| public: | ||
| Node(Node<T>* parent, T data); | ||
| ~Node(); | ||
| private: | ||
| T mData; | ||
| Node* mpParent; | ||
| Node* mpLeft; | ||
| Node* mpRight; | ||
| public: // Public Methods | ||
| T GetData(void) const; | ||
| Node* GetParent(void) const; | ||
|
|
||
| void MakeLeftChild(T space); | ||
| Node* GetLeftChild(void) const; | ||
|
|
||
| void MakeRightChild(T space); | ||
| Node* GetRightChild(void) const; | ||
| public: // Public inner types | ||
| class NodeIterator { | ||
| public: | ||
| NodeIterator(Node* parent); | ||
| private: | ||
| std::unordered_set<Node*> mVisited; | ||
| Node* mpRoot; | ||
| Node* mpCurrent; | ||
| public: | ||
| bool Next(); | ||
| T GetData() const; | ||
| Node* GetNode() const; | ||
| bool IsLeaf() const; | ||
| void Reset(); | ||
| }; | ||
| }; | ||
|
|
||
|
|
||
|
|
||
| #endif |
| @@ -0,0 +1,41 @@ | ||
| #ifndef PCG_VEC2_H | ||
| #define PCG_VEC2_H | ||
|
|
||
|
|
||
|
|
||
| /*! Struct representing a point or vector in 2d space. | ||
| */ | ||
| typedef struct _Vec2 { | ||
| /* Data */ | ||
| int x; | ||
| int y; | ||
|
|
||
| /* Constructors and Operators */ | ||
| _Vec2() { | ||
| x=0; | ||
| y=0; | ||
| } | ||
|
|
||
| _Vec2(int _x, int _y) { | ||
| x=_x; | ||
| y=_y; | ||
| } | ||
|
|
||
| _Vec2(const _Vec2& v) { | ||
| x=v.x; | ||
| y=v.y; | ||
| } | ||
|
|
||
| bool operator==(const _Vec2& rhs) const { | ||
| return (x == rhs.x && y == rhs.y); | ||
| } | ||
|
|
||
| bool operator!=(const _Vec2& rhs) const { | ||
| return (x != rhs.x || y != rhs.y); | ||
| } | ||
|
|
||
| } Vec2; | ||
|
|
||
|
|
||
|
|
||
| #endif |
| @@ -0,0 +1,32 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
|
|
||
| #ifndef SNOWDEVICE_SD_ENGINE_H | ||
| #define SNOWDEVICE_SD_ENGINE_H | ||
|
|
||
|
|
||
| #include "player.h" | ||
|
|
||
| class Engine { | ||
| public: | ||
| static Engine &getInstance() { | ||
| static Engine instance_; | ||
| return instance_; | ||
| } | ||
|
|
||
| int run(); | ||
|
|
||
| bool movePlayerTo(int, int); | ||
|
|
||
| bool canMovePlayerTo(int, int); | ||
|
|
||
| void movePlayerBy(int, int); | ||
|
|
||
| const Player getCurrentPlayer(); | ||
|
|
||
| private: | ||
| Player mainCharacter; | ||
| }; | ||
|
|
||
| #endif //SNOWDEVICE_SD_ENGINE_H |
| @@ -0,0 +1,17 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
|
|
||
| #ifndef SNOWDEVICE_INPUT_H | ||
| #define SNOWDEVICE_INPUT_H | ||
|
|
||
|
|
||
| class input { | ||
| public: | ||
| static void checkForInputAndBlock(); | ||
|
|
||
| static void parseInput(int suppliedKey); | ||
| }; | ||
|
|
||
|
|
||
| #endif //SNOWDEVICE_INPUT_H |
| @@ -0,0 +1,25 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
| #include "Inventory.h" | ||
| #include "position.h" | ||
|
|
||
| #ifndef SNOWDEVICE_PLAYER_H | ||
| #define SNOWDEVICE_PLAYER_H | ||
|
|
||
|
|
||
| struct Player { | ||
| char *playerGlyph; | ||
| public: | ||
| Player(); | ||
|
|
||
| Player(int, int); | ||
|
|
||
| void render(); | ||
|
|
||
| Position playerPosition; | ||
| Inventory playerInventory; | ||
| }; | ||
|
|
||
|
|
||
| #endif //SNOWDEVICE_PLAYER_H |
| @@ -0,0 +1,33 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
|
|
||
| #ifndef SNOWDEVICE_POSITION_H | ||
| #define SNOWDEVICE_POSITION_H | ||
|
|
||
|
|
||
| struct Position { | ||
| private: | ||
| int x; | ||
| int y; | ||
| int z; | ||
| public: | ||
| Position(); | ||
|
|
||
| Position(int, int, int); | ||
|
|
||
| const int posX(); | ||
|
|
||
| const int posY(); | ||
|
|
||
| const int posZ(); | ||
|
|
||
| void setX(int); | ||
|
|
||
| void setY(int); | ||
|
|
||
| void setZ(int); | ||
| }; | ||
|
|
||
|
|
||
| #endif //SNOWDEVICE_POSITION_H |
| @@ -0,0 +1,34 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
| #include <engine.h> | ||
| #include "BearLibTerminal.h" | ||
| #include "input.h" | ||
|
|
||
| void input::checkForInputAndBlock() { | ||
| int key = terminal_read(); | ||
| parseInput(key); | ||
| return; | ||
| } | ||
|
|
||
| void input::parseInput(int suppliedKey) { | ||
| switch (suppliedKey) { | ||
| case TK_ESCAPE: | ||
| terminal_close(); | ||
| break; | ||
| case TK_LEFT: | ||
| Engine::getInstance().movePlayerBy(-1, 0); | ||
| break; | ||
| case TK_RIGHT: | ||
| Engine::getInstance().movePlayerBy(1, 0); | ||
| break; | ||
| case TK_DOWN: | ||
| Engine::getInstance().movePlayerBy(0, 1); | ||
| break; | ||
| case TK_UP: | ||
| Engine::getInstance().movePlayerBy(0, -1); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } |
| @@ -0,0 +1,19 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
|
|
||
| #include "player.h" | ||
| #include "BearLibTerminal.h" | ||
|
|
||
| Player::Player(void) { | ||
| playerGlyph = (char *) "@"; | ||
| playerPosition = Position(); | ||
| } | ||
|
|
||
| Player::Player(int x, int y) { | ||
| playerPosition = Position(x, y, 0); | ||
| } | ||
|
|
||
| void Player::render() { | ||
| terminal_print(playerPosition.posX(), playerPosition.posY(), "@"); | ||
| } |
| @@ -0,0 +1,39 @@ | ||
| // | ||
| // Created by Karl on 12/12/2015. | ||
| // | ||
|
|
||
| #include "position.h" | ||
|
|
||
| const int Position::posX() { | ||
| return x; | ||
| } | ||
|
|
||
| const int Position::posY() { | ||
| return y; | ||
| } | ||
|
|
||
| const int Position::posZ() { | ||
| return z; | ||
| } | ||
|
|
||
| Position::Position() { | ||
| x = y = z = 0; | ||
| } | ||
|
|
||
| Position::Position(int suppliedX, int suppliedY, int suppliedZ) { | ||
| x = suppliedX; | ||
| y = suppliedY; | ||
| z = suppliedZ; | ||
| } | ||
|
|
||
| void Position::setX(int newX) { | ||
| x = newX; | ||
| } | ||
|
|
||
| void Position::setY(int newY) { | ||
| y = newY; | ||
| } | ||
|
|
||
| void Position::setZ(int newZ) { | ||
| z = newZ; | ||
| } |
| @@ -1,14 +1,17 @@ | ||
| #include <BearLibTerminal.h> | ||
| #include "engine.h" | ||
|
|
||
| int main() { | ||
| terminal_open(); | ||
| terminal_set | ||
| ( | ||
| "input:" | ||
| "cursor-symbol = 0x1F," | ||
| "cursor-blink-rate = 500," | ||
| "precise-mouse = false," | ||
| "mouse-cursor = true," | ||
| "filter=[];" | ||
| ); | ||
| Engine::getInstance().run(); | ||
| return 0; | ||
| } |