#include "gameMap.h" #include <algorithm> gameMap::gameMap(std::size_t numRooms, const Resources& resources, std::string& modelList, int seed) : m_numRooms(numRooms), m_resources(resources), m_seed(seed) { mapGen(modelList); } void gameMap::mapGen(std::string& modelList) { readModelFile(modelList); m_corridorModels = m_models; m_corridorModels.walls[1] = nullptr; memset(m_map, false, sizeof(m_map)); generateRooms(); generateDoors(); createRooms(); createCorridors(); } const WallPoints& gameMap::getWallPoints() { if (m_wallPoints.empty()) { std::for_each(m_rooms.begin(), m_rooms.end(), [this](const gameRoom& gR) { m_wallPoints.push_back(gR.getWallPoints()); }); } return m_wallPoints; } const WallPoints& gameMap::getCorridorWallPoints() { if (m_corridorWallPoints.empty()) { std::for_each(m_corridors.begin(), m_corridors.end(), [this](const gameRoom& gR) { m_corridorWallPoints.push_back(gR.getWallPoints()); }); } return m_corridorWallPoints; } const EntityBSphere& gameMap::getBoundingSpheres() { if (m_entityBSpheres.empty()) { std::for_each(m_rooms.begin(), m_rooms.end(), [this](const gameRoom& gR) { m_entityBSpheres.push_back(gR.getBoundingSpheres()); }); } return m_entityBSpheres; } const std::vector<std::pair<glm::vec3, glm::vec3>>& gameMap::getRoomSizes() { if (m_roomSizes.empty()) { std::for_each(m_rooms.begin(), m_rooms.end(), [this](const gameRoom& gR) { m_roomSizes.emplace_back(gR.getMin(), gR.getMax()); }); std::for_each(m_corridors.begin(), m_corridors.end(), [this](const gameRoom& gR) { m_roomSizes.emplace_back(gR.getMin(), gR.getMax()); }); } return m_roomSizes; } void gameMap::readModelFile(std::string& modelList) { std::ifstream inFile(modelList); if (!inFile) { throw std::runtime_error("Failed to open " + modelList + " when creating a map.\n"); } std::string line; inFile >> line; if (line != "wall") { throw std::runtime_error("Wrong file format for map resources.\n"); } inFile >> line; while (!inFile.eof() && line != "floor") { m_models.walls.push_back(m_resources.modelManager.getModel(line)); inFile >> line; } inFile >> line; while (!inFile.eof() && line != "debug") { m_models.floors.push_back(m_resources.modelManager.getModel(line)); inFile >> line; } if (!inFile.eof()) { inFile >> line; m_models.debugSphere = m_resources.modelManager.getModel(line); } if (!inFile.eof()) { inFile >> line; m_models.debugCube = m_resources.modelManager.getModel(line); } } void gameMap::draw() { for (auto& room : m_rooms) { room.draw(); if (m_debug) { room.drawDebug(); } } for (auto& corridor : m_corridors) { corridor.draw(); if (m_debug) { corridor.drawDebug(); } } } void gameMap::draw(glm::vec3 camPos, float distance) { camPos.y = 0; float camOffset{camPos.z + 10}; for (auto& room : m_rooms) { if (glm::distance(camPos, room.getLocation()) < distance && room.getLocation().z < camOffset) { room.draw(); if (m_debug) { room.drawDebug(); } } } for (auto& corridor : m_corridors) { if (glm::distance(camPos, corridor.getLocation()) < distance && corridor.getLocation().z < camOffset) { corridor.draw(); if (m_debug) { corridor.drawDebug(); } } } } bool gameMap::updateMap(int xSize, int zSize, int xOrigin, int zOrigin) { if (xSize + xOrigin < GM_X_MAX - 1 && zSize + zOrigin < GM_Z_MAX - 1 && xOrigin > 0 && zOrigin > 0) { // Check map area is clear for (int x = xOrigin; x <= xOrigin + xSize; ++x) { for (int z = zOrigin; z <= zOrigin + zSize; ++z) { if (m_map[x][z] || m_map[x + 1][z] || m_map[x - 1][z] || m_map[x][z + 1] || m_map[x][z - 1] || m_map[x - 1][z - 1] || m_map[x + 1][z + 1]) { return false; } } } // update map for (int x = xOrigin; x <= xOrigin + xSize; ++x) { for (int z = zOrigin; z <= zOrigin + zSize; ++z) { m_map[x][z] = true; } } return true; } return false; } gameMap::RoomInfo gameMap::generateSpawn() { glm::vec3 directionNormal{1, 0, 1}; glm::vec3 nextRoomDistance{GM_X_MAX / 2, 0, GM_Z_MAX / 2}; glm::vec3 finalLocation{directionNormal * nextRoomDistance}; std::vector<std::pair<int, int>> doors; int x{6}, z{6}, wallFill{15}, floorFill{0}; int xOrigin{(int)finalLocation.x}, zOrigin{(int)finalLocation.z}; RoomInfo roomInfo(x, z, wallFill, floorFill, xOrigin, zOrigin, doors); if (!updateMap(x, z, xOrigin, zOrigin)) { throw std::runtime_error("Invalid spawn room for map\n"); } m_roomInfo.push_back(roomInfo); return roomInfo; } void gameMap::generateRooms() { RoomInfo roomInfo; if (m_rooms.empty()) { roomInfo = generateSpawn(); } glm::vec3 normal{0}; // Set to spawn glm::vec3 finalLocation{GM_X_MAX / 2, 0, GM_Z_MAX / 2}; std::uniform_int_distribution<> distRoomDistance{5, 30}; std::uniform_int_distribution<> distRoomSizes{5, 20}; int wallFill{15}; int floorFill{8}; int nRooms = (int)m_numRooms - 1; int failCount{0}, reset{0}; while (nRooms > 0) { std::mt19937 genX{static_cast<std::random_device::result_type>( m_seed + m_seedOffset++)}; std::mt19937 genZ{static_cast<std::random_device::result_type>( m_seed + m_seedOffset++)}; std::uniform_int_distribution<> distX{roomInfo.xOrigin - roomInfo.xSize + 1, roomInfo.xOrigin + roomInfo.xSize - 1}; std::uniform_int_distribution<> distZ{roomInfo.zOrigin - roomInfo.zSize + 1, roomInfo.zOrigin + roomInfo.zSize - 1}; normal = randomNormal(); glm::vec3 offset{distX(genX), 0, distZ(genZ)}; glm::vec3 tempFinal = offset * normal + glm::vec3{distRoomDistance(genX), 0, distRoomDistance(genZ)}; int xSize = distRoomSizes(genX); int zSize = distRoomSizes(genZ); if (updateMap(xSize, zSize, (int)tempFinal.x, (int)tempFinal.z)) { std::vector<std::pair<int, int>> doors; m_roomInfo.emplace_back(xSize, zSize, wallFill, floorFill, (int)tempFinal.x, (int)tempFinal.z, doors); --nRooms; } else { if (failCount++ > 30000) { memset(m_map, false, sizeof(m_map)); m_roomInfo.clear(); roomInfo = generateSpawn(); failCount = 0; m_seed *= 3; nRooms = 9; if (++reset > 3) { std::cerr << "Failed to generate a valid map\n"; throw std::runtime_error( "Failed to generate a valid map\n"); } } } } } int gameMap::insertDoors(RoomInfo& roomInfo, std::vector<DoorData>& possibleDoorLocations) { std::mt19937 gen{ static_cast<std::random_device::result_type>(m_seed + m_seedOffset++)}; std::uniform_int_distribution<> dist{0, (int)possibleDoorLocations.size() - 1}; int rand = dist(gen); roomInfo.doors.emplace_back(possibleDoorLocations[rand].x, possibleDoorLocations[rand].z); auto inserted = possibleDoorLocations[rand]; // find connecting room for (auto& room : m_roomInfo) { if (inserted.connectingRoomX >= room.xOrigin && inserted.connectingRoomX <= room.xOrigin + room.xSize && inserted.connectingRoomZ >= room.zOrigin && inserted.connectingRoomZ <= room.zOrigin + room.zSize) { int count{0}; if (possibleDoorLocations[0].x == 0) { for (int z = room.zOrigin; z <= room.zOrigin + room.zSize; ++z) { if (z == inserted.connectingRoomZ) { ++count; room.doors.emplace_back(room.xSize + 1, count); } ++count; } } else { for (int x = room.xOrigin; x <= room.xOrigin + room.xSize; ++x) { if (x == inserted.connectingRoomX) { ++count; room.doors.emplace_back(count, room.zSize + 1); } ++count; } } } } return rand; } void gameMap::generateDoors() { for (auto& room : m_roomInfo) { std::vector<DoorData> possibleDoorLocations; DoorData doorData; int xCount{2}; // find another room adjacent to x-axis for (int x = room.xOrigin + 1; x < room.xOrigin + room.xSize; ++x) { for (int z = room.zOrigin - 1; z > 0; --z) { if (m_map[x][z] && m_map[x - 1][z] && m_map[x + 1][z]) { possibleDoorLocations.emplace_back(xCount, 0, x, z, room); break; } else if (m_map[x][z]) { break; } } ++xCount; } if (!possibleDoorLocations.empty()) { int index = insertDoors(room, possibleDoorLocations); // connect rooms auto inserted = possibleDoorLocations[index]; generateCorridor(inserted); possibleDoorLocations.clear(); } // find another room adjacent to z-axis int zCount{2}; for (int z = room.zOrigin + 1; z < room.zOrigin + room.zSize; ++z) { for (int x = room.xOrigin - 1; x > 0; --x) { if (m_map[x][z] && m_map[x][z - 1] && m_map[x][z + 1]) { possibleDoorLocations.emplace_back(0, zCount, x, z, room); break; } else if (m_map[x][z]) { break; } } ++zCount; } if (!possibleDoorLocations.empty()) { int index = insertDoors(room, possibleDoorLocations); // connect rooms auto inserted = possibleDoorLocations[index]; generateCorridor(inserted); } } checkCorridorIntersections(); } void gameMap::generateCorridor(const DoorData& doorData) { DoorSet doors; int originPlusX = doorData.info.xOrigin + doorData.x; int originPlusZ = doorData.info.zOrigin + doorData.z; if (doorData.x == 0) { doors.emplace_back(originPlusX - doorData.connectingRoomX + 1, 1); doors.emplace_back(0, 1); m_corridorInfo.emplace_back(originPlusX - doorData.connectingRoomX, 1, 15, 0, originPlusX - (originPlusX - doorData.connectingRoomX), originPlusZ - 1, doors); } else { doors.emplace_back(1, doorData.info.zOrigin + doorData.z - doorData.connectingRoomZ + 1); doors.emplace_back(1, 0); m_corridorInfo.emplace_back(1, originPlusZ - doorData.connectingRoomZ, 15, 0, originPlusX - 1, originPlusZ - (originPlusZ - doorData.connectingRoomZ), doors); } } void gameMap::checkCorridorIntersections() { for (auto& info : m_corridorInfo) { for (auto& cmpInfo : m_corridorInfo) { if (&info != &cmpInfo) { if (info.xOrigin > cmpInfo.xOrigin && info.xOrigin < cmpInfo.xOrigin + cmpInfo.xSize && info.zOrigin < cmpInfo.zOrigin && info.zOrigin + info.zSize > cmpInfo.zOrigin) { // intersecting info.doors.emplace_back(0, cmpInfo.zOrigin - info.zOrigin + 1); info.doors.emplace_back(2, cmpInfo.zOrigin - info.zOrigin + 1); cmpInfo.doors.emplace_back(info.xOrigin - cmpInfo.xOrigin + 1, 0); cmpInfo.doors.emplace_back(info.xOrigin - cmpInfo.xOrigin + 1, 2); } } } } } void gameMap::createRooms() { std::for_each(m_roomInfo.begin(), m_roomInfo.end(), [this](RoomInfo& info) { m_rooms.emplace_back(info.xSize, info.zSize, info.wallFil, info.floorFill, info.xOrigin, info.zOrigin, m_models, m_seed, info.doors); }); } void gameMap::createCorridors() { std::for_each(m_corridorInfo.begin(), m_corridorInfo.end(), [this](RoomInfo& info) { m_corridors.emplace_back(info.xSize, info.zSize, info.wallFil, info.floorFill, info.xOrigin, info.zOrigin, m_corridorModels, m_seed, info.doors); }); } glm::vec3 gameMap::randomNormal() { std::mt19937 genX{ static_cast<std::random_device::result_type>(m_seed + m_seedOffset++)}; std::mt19937 genZ{ static_cast<std::random_device::result_type>(m_seed + m_seedOffset++)}; std::uniform_int_distribution<> dist{-1, 1}; return glm::vec3{dist(genX), 0, dist(genZ)}; } gameMap::RoomInfo::RoomInfo(int xS, int zS, int wF, int fF, int xO, int zO, const DoorSet& d) : xSize(xS), zSize(zS), wallFil(wF), floorFill(fF), xOrigin(xO), zOrigin(zO), doors(d) {} gameMap::DoorData::DoorData(int xA, int zA, int crx, int crz, const RoomInfo& ri) : x(xA), z(zA), connectingRoomX(crx), connectingRoomZ(crz), info(ri) {}