From 823884f403ba8da974ffab7abc76561882cd019e Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 20 Jul 2015 21:56:16 +0100 Subject: [PATCH] level 3 dungeon generation --- apps/freeablo/falevelgen/levelgen.cpp | 479 ++++++++++++++++++++------ apps/freeablo/falevelgen/tileset.cpp | 140 +++++++- apps/freeablo/falevelgen/tileset.h | 120 ++++++- apps/freeablo/main.cpp | 4 +- components/cel/celfile.cpp | 2 + components/diabloexe/diabloexe.cpp | 5 +- resources/tilesets/l1.ini | 30 ++ resources/tilesets/l2.ini | 30 ++ resources/tilesets/l3.ini | 109 ++++++ 9 files changed, 796 insertions(+), 123 deletions(-) create mode 100644 resources/tilesets/l3.ini diff --git a/apps/freeablo/falevelgen/levelgen.cpp b/apps/freeablo/falevelgen/levelgen.cpp index ff32c4a9c..944880b61 100644 --- a/apps/freeablo/falevelgen/levelgen.cpp +++ b/apps/freeablo/falevelgen/levelgen.cpp @@ -76,14 +76,17 @@ namespace FALevelGen } }; + // the values here are not significant, these were just conventient when debugging level 3 + // they must only be distinct enum Basic { - wall = 135, - upStairs = 64, - downStairs = 59, - door = 47, - floor = 13, - blank = 104 + insideWall = 27, + wall = 56, + upStairs = 35, + downStairs = 34, + door = 67, + floor = 116, + blank = 115 }; void fillRoom(const Room& room, Level::Dun& level) @@ -436,25 +439,60 @@ namespace FALevelGen return false; } - - // Remove double walls, as the tileset does not allow for them - void cleanup(Level::Dun& level, std::vector& rooms) + + bool addWalls(Level::Dun& level) { + bool retval = false; + + // Bound corridoors with walls + for(size_t x = 0; x < level.width(); x++) + { + for(size_t y = 0; y < level.height(); y++) + { + if(getXY(x, y, level) == blank && borders(x, y, floor, level)) + { + level[x][y] = wall; + retval = true; + } + } + } + + return retval; + } + + bool cleanupHelper(Level::Dun& level, std::vector& rooms, bool includeBorders) + { + bool retval = false; + for(int32_t x = 0; x < (int32_t)level.width(); x++) { for(int32_t y = 0; y < (int32_t)level.height(); y++) { - if(level[x][y] != wall || borders(x, y, blank, level)) + if(level[x][y] != wall || (!includeBorders && borders(x, y, blank, level))) continue; - - // This expression is a bit of a monster, take my word for it that it finds double walls :P - if((level[x][y-1] == wall && (((level[x+1][y-1] == wall && level[x+1][y] == wall)) || - (level[x-1][y-1] == wall && level[x-1][y] == wall)) ) - || - (level[x][y+1] == wall && (((level[x+1][y+1] == wall && level[x+1][y] == wall)) || - (level[x-1][y+1] == wall && level[x-1][y] == wall)) )) + + bool fixExternalT = false; + + if(includeBorders && borders(x, y, blank, level)) + { + int wallCount = 0; + + if(level[x+1][y] == wall && borders(x+1, y, blank, level)) + wallCount++; + if(level[x-1][y] == wall && borders(x-1, y, blank, level)) + wallCount++; + if(level[x][y+1] == wall && borders(x, y+1, blank, level)) + wallCount++; + if(level[x][y-1] == wall && borders(x, y-1, blank, level)) + wallCount++; + + fixExternalT = wallCount > 2; + } + + if((level[x][y+1] == wall && level[x+1][y] == wall && level[x+1][y+1] == wall) || fixExternalT) { level[x][y] = floor; + retval = true; for(size_t i = 0; i < rooms.size(); i++) { @@ -463,18 +501,18 @@ namespace FALevelGen // Draw x oriented walls for(size_t x = 0; x < rooms[i].width; x++) { - if(!borders(x + rooms[i].xPos, rooms[i].yPos, blank, level)) + if(includeBorders || !borders(x + rooms[i].xPos, rooms[i].yPos, blank, level)) level[x + rooms[i].xPos][rooms[i].yPos] = floor; - if(!borders(x + rooms[i].xPos, rooms[i].height-1 + rooms[i].yPos, blank, level)) + if(includeBorders || !borders(x + rooms[i].xPos, rooms[i].height-1 + rooms[i].yPos, blank, level)) level[x + rooms[i].xPos][rooms[i].height-1 + rooms[i].yPos] = floor; } // Draw y oriented walls for(size_t y = 0; y < rooms[i].height; y++) { - if(!borders(rooms[i].xPos, y + rooms[i].yPos, blank, level)) + if(includeBorders || !borders(rooms[i].xPos, y + rooms[i].yPos, blank, level)) level[rooms[i].xPos][y + rooms[i].yPos] = floor; - if(!borders(rooms[i].width-1 + rooms[i].xPos, y + rooms[i].yPos, blank, level)) + if(includeBorders || !borders(rooms[i].width-1 + rooms[i].xPos, y + rooms[i].yPos, blank, level)) level[rooms[i].width-1 + rooms[i].xPos][y + rooms[i].yPos] = floor; } @@ -485,12 +523,57 @@ namespace FALevelGen } } - // Remove any isolated wall blocks which may have been created by removing double walls + return retval; + } + + + bool isWall(size_t x, size_t y, const Level::Dun& level, bool inside) + { + if(inside) + return getXY(x, y, level) == insideWall || getXY(x, y, level) == door; + else + return getXY(x, y, level) == wall || getXY(x, y, level) == upStairs; + } + + bool cleanLooseWallsHelper(size_t x, size_t y, const Level::Dun& level, bool wallsSeparated) + { + if(wallsSeparated) + return getXY(x, y, level) == insideWall || getXY(x, y, level) == door; + else + return getXY(x, y, level) == wall || getXY(x, y, level) == door; + } + + void cleanLooseWalls(Level::Dun& level, bool wallsSeparated) + { + // Remove any isolated wall blocks for(int32_t x = 0; x < (int32_t)level.width(); x++) + { for(int32_t y = 0; y < (int32_t)level.height(); y++) - if(level[x][y] == wall && getXY(x+1, y, level) != wall && getXY(x-1, y, level) != wall && - getXY(x, y+1, level) != wall && getXY(x, y-1, level) != wall) + { + if(cleanLooseWallsHelper(x,y, level, wallsSeparated) && (!cleanLooseWallsHelper(x+1, y, level, wallsSeparated)) && (!cleanLooseWallsHelper(x-1, y, level, wallsSeparated)) && + (!cleanLooseWallsHelper(x, y+1, level, wallsSeparated)) && (!cleanLooseWallsHelper(x, y-1, level, wallsSeparated))) + { level[x][y] = floor; + } + } + } + } + + // Remove double walls, as the tileset does not allow for them + void cleanup(Level::Dun& level, std::vector& rooms) + { + bool dirty = cleanupHelper(level, rooms, false); + + // iterate until there are no double walls at all + while(dirty) + { + dirty = cleanupHelper(level, rooms, true); + + if(dirty) + addWalls(level); // cleanupHelper may leave floor blocks directly beside blank blocks + } + + cleanLooseWalls(level, false); } // Helper function for adding doors @@ -571,7 +654,7 @@ namespace FALevelGen bool placeUpStairs(Level::Dun& level, const std::vector& rooms, size_t levelNum) { - if(levelNum == 1) + if(levelNum == 1 || levelNum == 3) { for(size_t i = 0; i < rooms.size(); i++) { @@ -579,7 +662,8 @@ namespace FALevelGen size_t baseY = rooms[i].yPos; // on a wall - if(level[baseX-1][baseY-1] == blank && level[baseX][baseY-1] == blank && level[baseX+1][baseY-1] == blank && + if(level[baseX-1][baseY-2] == blank && level[baseX][baseY-2] == blank && level[baseX+1][baseY-2] == blank && + level[baseX-1][baseY-1] == blank && level[baseX][baseY-1] == blank && level[baseX+1][baseY-1] == blank && level[baseX-1][baseY] == wall && level[baseX][baseY] == wall && level[baseX+1][baseY] == wall && level[baseX-1][baseY+1] == floor && level[baseX][baseY+1] == floor && level[baseX+1][baseY+1] == floor) { @@ -611,8 +695,26 @@ namespace FALevelGen return false; } - bool placeDownStairs(Level::Dun& level, const std::vector& rooms) + bool placeDownStairs(Level::Dun& level, const std::vector& rooms, size_t levelNum) { + if(levelNum == 3) + { + for(size_t i = 0; i < rooms.size(); i++) + { + size_t baseX = rooms[i].xPos; + size_t baseY = rooms[i].yPos + (rooms[i].width/2); + + // on a wall + if(level[baseX-2][baseY+1] == blank && level[baseX-2][baseY] == blank && level[baseX-2][baseY+1] == blank && + level[baseX-1][baseY+1] == blank && level[baseX-1][baseY] == blank && level[baseX-1][baseY+1] == blank && + level[baseX][baseY-1] == wall && level[baseX][baseY] == wall && level[baseX][baseY+1] == wall && + level[baseX+1][baseY-1] == floor && level[baseX+1][baseY] == floor && level[baseX+1][baseY+1] == floor) + { + level[baseX][baseY] = downStairs; + return true; + } + } + } for(size_t i = rooms.size()-1; i != 0; i--) { if(rooms[i].width >= 6 && rooms[i].height >= 6) @@ -708,92 +810,98 @@ namespace FALevelGen drawRoom(rooms[i], level); // Bound corridoors with walls - for(size_t x = 0; x < width; x++) - { - for(size_t y = 0; y < height; y++) - { - if(getXY(x, y, level) == blank) - { - if(borders(x, y, floor, level)) - level[x][y] = wall; - } - } - } - + addWalls(level); + cleanup(level, rooms); addDoors(level, rooms); // Make sure we always place stairs - if(!(placeUpStairs(level, rooms, levelNum) && placeDownStairs(level, rooms))) + if(!(placeUpStairs(level, rooms, levelNum) && placeDownStairs(level, rooms, levelNum))) return generateTmp(width, height, levelNum); + // Separate internal from external walls + for(int32_t x = 0; x < (int32_t)width; x++) + { + for(int32_t y = 0; y < (int32_t)height; y++) + { + if(level[x][y] == wall && !borders(x, y, blank, level)) + level[x][y] = insideWall; + } + } + + cleanLooseWalls(level, true); + return level; } - bool isWall(size_t x, size_t y, const Level::Dun& level) + bool isPassable(int32_t x, int32_t y, const Level::Dun& tmpLevel) { - return getXY(x, y, level) == wall || getXY(x, y, level) == door || getXY(x, y, level) == upStairs; + return getXY(x, y, tmpLevel) == floor || getXY(x, y, tmpLevel) == door; } - void setPoint(int32_t x, int32_t y, size_t val, const Level::Dun& tmpLevel, Level::Dun& level, const TileSet& tileset) + void setPoint(int32_t x, int32_t y, size_t val, const Level::Dun& tmpLevel, Level::Dun& level, const TileSet& tileset, int wallOffset, bool isInsideWall) { size_t newVal = val; - if(val == tileset.xWall) + if(val == TileSetEnum::xWall+wallOffset) { - if(getXY(x, y, tmpLevel) == door) - newVal = tileset.xDoor; + if(getXY(x, y, tmpLevel) == door && isInsideWall) + newVal = TileSetEnum::xDoor; else if(getXY(x, y, tmpLevel) == upStairs) - newVal = upStairs; - else if(getXY(x, y+1, tmpLevel) == blank) - newVal = tileset.outsideXWall; - else if(getXY(x+1, y, tmpLevel) == floor) - newVal = tileset.xWallEnd; - else if(getXY(x-1, y, tmpLevel) == floor) - newVal = tileset.leftCorner; + newVal = TileSetEnum::upStairs; + else if(getXY(x-1, y, tmpLevel) == blank && getXY(x, y+1, tmpLevel) == blank) + newVal = TileSetEnum::outsideLeftCorner; + else if(!isInsideWall && getXY(x, y+1, tmpLevel) == blank) + newVal = TileSetEnum::outsideXWall; + else if(getXY(x+1, y, tmpLevel) == floor)// && isInsideWall) + newVal = TileSetEnum::insideXWallEnd; + else if(isWall(x+1, y, tmpLevel, isInsideWall) && isWall(x, y-1, tmpLevel, isInsideWall)) + newVal = TileSetEnum::leftCorner+wallOffset; + else if(getXY(x-1, y, tmpLevel) == floor)// && isInsideWall) + newVal = TileSetEnum::insideXWallEndBack; } - else if(val == tileset.yWall) + else if(val == TileSetEnum::yWall+wallOffset) { - if(getXY(x, y, tmpLevel) == door) - newVal = tileset.yDoor; - else if(getXY(x+1, y, tmpLevel) == blank) - newVal = tileset.outsideYWall; - else if(getXY(x, y+1, tmpLevel) == floor) - newVal = tileset.yWallEnd; - else if(getXY(x, y-1, tmpLevel) == floor) - newVal = tileset.rightCorner; + if(getXY(x, y, tmpLevel) == door && isInsideWall) + newVal = TileSetEnum::yDoor; + else if(!isInsideWall && getXY(x+1, y, tmpLevel) == blank) + newVal = TileSetEnum::outsideYWall; + else if(getXY(x, y+1, tmpLevel) == floor)// && isInsideWall) + newVal = TileSetEnum::insideYWallEnd; + else if(getXY(x, y-1, tmpLevel) == floor)// && isInsideWall) + newVal = TileSetEnum::insideYWallEndBack; } - else if(val == tileset.bottomCorner) + else if(val == TileSetEnum::bottomCorner+wallOffset) { - if(getXY(x+1, y+1, tmpLevel) == blank || getXY(x+1, y, tmpLevel) == blank || getXY(x, y+1, tmpLevel) == blank) + if(!isInsideWall && (getXY(x+1, y+1, tmpLevel) == blank || getXY(x+1, y, tmpLevel) == blank || getXY(x, y+1, tmpLevel) == blank)) { - if(isWall(x, y+1, tmpLevel)) - newVal = tileset.outsideYWall; + if(isWall(x, y+1, tmpLevel, isInsideWall)) + newVal = TileSetEnum::outsideYWall; else - newVal = tileset.outsideBottomCorner; + newVal = TileSetEnum::outsideBottomCorner; } - else if(isWall(x, y+1, tmpLevel)) - newVal = tileset.yWall; + else if(isWall(x, y+1, tmpLevel, isInsideWall)) + newVal = TileSetEnum::yWall+wallOffset; } - else if(val == tileset.topCorner) + else if(!isInsideWall && val == TileSetEnum::topCorner) { if(getXY(x+1, y+1, tmpLevel) == blank) - newVal = tileset.outsideTopCorner; + newVal = TileSetEnum::outsideTopCorner; } - else if(val == tileset.rightCorner) + else if(!isInsideWall && val == TileSetEnum::rightCorner) { if(getXY(x+1, y-1, tmpLevel) == blank || getXY(x+1, y, tmpLevel) == blank || getXY(x, y-1, tmpLevel) == blank) - newVal = tileset.outsideRightCorner; + newVal = TileSetEnum::outsideRightCorner; } - else if(val == tileset.leftCorner) + else if(!isInsideWall &&val == TileSetEnum::leftCorner) { if(getXY(x-1, y+1, tmpLevel) == blank || getXY(x-1, y, tmpLevel) == blank || getXY(x, y+1, tmpLevel) == blank) - newVal = tileset.outsideLeftCorner; + newVal = TileSetEnum::outsideLeftCorner; } level[x][y] = newVal; @@ -820,69 +928,216 @@ namespace FALevelGen monsters.push_back(m); } } - - Level::Level* generate(size_t width, size_t height, size_t dLvl, const DiabloExe::DiabloExe& exe) - { - size_t levelNum = ((dLvl-1) / 4) + 1; - Level::Dun tmpLevel = generateTmp(width, height, levelNum); - - Level::Dun level(width, height); - - std::stringstream ss; ss << "resources/tilesets/l" << levelNum << ".ini"; - TileSet tileset(ss.str()); - - // Fill in isometric information (wall direction, etc), using flat tmpLevel as a base - for(int32_t x = 0; x < (int32_t)width; x++) + void fillIsometric(Level::Dun& tmpLevel, Level::Dun& level, TileSet& tileset, bool ignoreNotWall, int wallOffset, bool isInsideWall) + { + for(int32_t x = 0; x < tmpLevel.width(); x++) { - for(int32_t y = 0; y < (int32_t)height; y++) + for(int32_t y = 0; y < tmpLevel.height(); y++) { - if(tmpLevel[x][y] == upStairs || tmpLevel[x][y] == downStairs) + if(tmpLevel[x][y] == upStairs) + { + level[x][y] = TileSetEnum::upStairs; + continue; + } + if(tmpLevel[x][y] == downStairs) { - level[x][y] = tmpLevel[x][y]; + level[x][y] = TileSetEnum::downStairs; continue; } - if(isWall(x, y, tmpLevel)) + if(isWall(x, y, tmpLevel, isInsideWall)) { - if(isWall(x+1, y, tmpLevel)) + if(isWall(x+1, y, tmpLevel, isInsideWall)) { - if(isWall(x, y+1, tmpLevel)) - setPoint(x, y, tileset.topCorner, tmpLevel, level, tileset); + if(isWall(x, y+1, tmpLevel, isInsideWall)) + setPoint(x, y, TileSetEnum::topCorner+wallOffset, tmpLevel, level, tileset, wallOffset, isInsideWall); else { - if(isWall(x, y-1, tmpLevel)) - setPoint(x, y, tileset.leftCorner, tmpLevel, level, tileset); - else - setPoint(x, y, tileset.xWall, tmpLevel, level, tileset); + //if(isWall(x, y-1, tmpLevel, insideWall)) + // setPoint(x, y, TileSetEnum::leftCorner+wallOffset, tmpLevel, level, tileset, wallOffset, isInsideWall); + //else + setPoint(x, y, TileSetEnum::xWall+wallOffset, tmpLevel, level, tileset, wallOffset, isInsideWall); } } - else if(isWall(x-1, y, tmpLevel)) + else if(isWall(x-1, y, tmpLevel, isInsideWall)) { - if(isWall(x, y-1, tmpLevel)) - setPoint(x, y, tileset.bottomCorner, tmpLevel, level, tileset); + if(isWall(x, y-1, tmpLevel, isInsideWall)) + setPoint(x, y, TileSetEnum::bottomCorner+wallOffset, tmpLevel, level, tileset, wallOffset, isInsideWall); else { - if(isWall(x, y+1, tmpLevel)) - setPoint(x, y, tileset.rightCorner, tmpLevel, level, tileset); + if(isWall(x, y+1, tmpLevel, isInsideWall)) + setPoint(x, y, TileSetEnum::rightCorner+wallOffset, tmpLevel, level, tileset, wallOffset, isInsideWall); else - setPoint(x, y, tileset.xWall, tmpLevel, level, tileset); + setPoint(x, y, TileSetEnum::xWall+wallOffset, tmpLevel, level, tileset, wallOffset, isInsideWall); } } else { - setPoint(x, y, tileset.yWall, tmpLevel, level, tileset); + setPoint(x, y, TileSetEnum::yWall+wallOffset, tmpLevel, level, tileset, wallOffset, isInsideWall); } } - else + else if(!ignoreNotWall) { if(tmpLevel[x][y] == blank) - level[x][y] = tileset.blank; + level[x][y] = TileSetEnum::blank; + else if(tmpLevel[x][y] == insideWall) + level[x][y] = TileSetEnum::insideXWall; else - level[x][y] = tileset.floor; + level[x][y] = TileSetEnum::floor; + } + } + } + } + + bool connectHelperY(int32_t x, int32_t y, Level::Dun& level) + { + return level[x][y] == TileSetEnum::insideYWall || level[x][y] == TileSetEnum::yDoor || + level[x][y] == TileSetEnum::insideXWallEnd || level[x][y] == TileSetEnum::insideXWallEndBack || + level[x][y] == TileSetEnum::insideLeftCorner || level[x][y] == TileSetEnum::insideRightCorner || + level[x][y] == TileSetEnum::insideTopCorner || level[x][y] == TileSetEnum::insideBottomCorner; + } + bool connectHelperX(int32_t x, int32_t y, Level::Dun& level) + { + return level[x][y] == TileSetEnum::insideXWall || level[x][y] == TileSetEnum::xDoor || + level[x][y] == TileSetEnum::insideYWallEnd || level[x][y] == TileSetEnum::insideYWallEndBack || + level[x][y] == TileSetEnum::insideLeftCorner || level[x][y] == TileSetEnum::insideRightCorner || + level[x][y] == TileSetEnum::insideTopCorner || level[x][y] == TileSetEnum::insideBottomCorner; + } + + void connectWalls(Level::Dun& level) + { + for(int32_t x = 0; x < (int32_t)level.width(); x++) + { + for(int32_t y = 0; y < (int32_t)level.height(); y++) + { + switch(level[x][y]) + { + case TileSetEnum::yWall: + { + if(connectHelperX(x+1, y, level)) + level[x][y] = TileSetEnum::joinY; + break; + } + + case TileSetEnum::rightCorner: + { + if(connectHelperX(x+1, y, level) && connectHelperY(x, y-1, level)) + { + level[x][y] = TileSetEnum::joinRightCorner; + } + else + { + if(connectHelperX(x+1, y, level)) + level[x][y] = TileSetEnum::joinYRightCorner; + else if(connectHelperY(x, y-1, level)) + level[x][y] = TileSetEnum::joinOutXRightCorner; + } + break; + } + + case TileSetEnum::outsideXWall: + { + if(connectHelperY(x, y-1, level)) + level[x][y] = TileSetEnum::joinOutX; + break; + } + + case TileSetEnum::outsideTopCorner: + { + if(connectHelperX(x-1, y, level) && connectHelperY(x, y-1, level)) + { + level[x][y] = TileSetEnum::joinTopCorner; + } + else + { + if(connectHelperX(x-1, y, level)) + level[x][y] = TileSetEnum::joinOutYTopCorner; + else if(connectHelperY(x, y-1, level)) + level[x][y] = TileSetEnum::joinOutXTopCorner; + } + break; + } + + case TileSetEnum::outsideYWall: + { + if(connectHelperX(x-1, y, level)) + level[x][y] = TileSetEnum::joinOutY; + break; + } + + case TileSetEnum::leftCorner: + { + if(connectHelperX(x-1, y, level) && connectHelperY(x, y+1, level)) + { + level[x][y] = TileSetEnum::joinLeftCorner; + } + else + { + if(connectHelperX(x-1, y, level)) + level[x][y] = TileSetEnum::joinOutYLeftCorner; + else if(connectHelperY(x, y+1, level)) + level[x][y] = TileSetEnum::joinXLeftCorner; + } + break; + } + + case TileSetEnum::xWall: + { + if(connectHelperY(x, y+1, level)) + level[x][y] = TileSetEnum::joinX; + break; + } + + case TileSetEnum::bottomCorner: + { + if(connectHelperX(x+1, y, level) && connectHelperY(x, y+1, level)) + { + level[x][y] = TileSetEnum::joinBottomCorner; + } + else + { + if(connectHelperX(x+1, y, level)) + level[x][y] = TileSetEnum::joinYBottomCorner; + else if(connectHelperY(x, y+1, level)) + level[x][y] = TileSetEnum::joinXBottomCorner; + } + break; + } + + default: + { + break; + } } } } + } + + + Level::Level* generate(size_t width, size_t height, size_t dLvl, const DiabloExe::DiabloExe& exe) + { + size_t levelNum = ((dLvl-1) / 4) + 1; + + Level::Dun tmpLevel = generateTmp(width, height, levelNum); + + Level::Dun level(width, height); + + std::stringstream ss; ss << "resources/tilesets/l" << levelNum << ".ini"; + TileSet tileset(ss.str()); + + fillIsometric(tmpLevel, level, tileset, false, 0, false); + fillIsometric(tmpLevel, level, tileset, true, TileSetEnum::insideXWall, true); + + connectWalls(level); + + for(int32_t x = 0; x < (int32_t)width; x++) + { + for(int32_t y = 0; y < (int32_t)height; y++) + { + level[x][y] = tileset.convert(((TileSetEnum::TileSetEnum)level[x][y])); + } + } std::pair downStairsPoint; std::pair upStairsPoint; @@ -892,7 +1147,7 @@ namespace FALevelGen { for(int32_t y = 0; y < (int32_t)height; y++) { - if(level[x][y] == upStairs) + if(level[x][y] == TileSetEnum::upStairs) { level[x-1][y-1] = tileset.upStairs1; level[x][y-1] = tileset.upStairs2; @@ -908,7 +1163,7 @@ namespace FALevelGen upStairsPoint = std::make_pair(x*2,y*2); } - else if(level[x][y] == downStairs) + else if(level[x][y] == TileSetEnum::downStairs) { level[x-1][y-1] = tileset.downStairs1; level[x][y-1] = tileset.downStairs2; @@ -944,7 +1199,7 @@ namespace FALevelGen std::string solPath = ss.str(); Level::Level* retval = new Level::Level(level, tilPath, minPath, solPath, celPath, downStairsPoint, upStairsPoint, tileset.getDoorMap()); - placeMonsters(*retval, exe, dLvl); + placeMonsters(*retval, exe, dLvl); return retval; } diff --git a/apps/freeablo/falevelgen/tileset.cpp b/apps/freeablo/falevelgen/tileset.cpp index 79ac8392a..ecb81c62f 100644 --- a/apps/freeablo/falevelgen/tileset.cpp +++ b/apps/freeablo/falevelgen/tileset.cpp @@ -59,18 +59,67 @@ namespace FALevelGen blank = pt.get("Basic.blank"); fillTile(blank, pt, "Blank"); - xWallEnd = pt.get("Basic.xWallEnd"); - fillTile(xWallEnd, pt, "XWallEnd"); - - yWallEnd = pt.get("Basic.yWallEnd"); - fillTile(yWallEnd, pt, "YWallEnd"); - xDoor = pt.get("Basic.xDoor"); fillTile(xDoor, pt, "XDoor"); yDoor = pt.get("Basic.yDoor"); fillTile(yDoor, pt, "YDoor"); + insideXWall = pt.get("Basic.insideXWall"); + fillTile(insideXWall, pt, "InsideXWall"); + insideXWallEnd = pt.get("Basic.insideXWallEnd"); + fillTile(insideXWallEnd, pt, "InsideXWallEnd"); + insideXWallEndBack = pt.get("Basic.insideXWallEndBack"); + fillTile(insideXWallEndBack, pt, "InsideXWallEndBack"); + insideYWall = pt.get("Basic.insideYWall"); + fillTile(insideYWall, pt, "InsideYWall"); + insideYWallEnd = pt.get("Basic.insideYWallEnd"); + fillTile(insideYWallEnd, pt, "InsideYWallEnd"); + insideYWallEndBack = pt.get("Basic.insideYWallEndBack"); + fillTile(insideYWallEndBack, pt, "InsideYWallEndBack"); + insideLeftCorner = pt.get("Basic.insideLeftCorner"); + fillTile(insideLeftCorner, pt, "InsideLeftCorner"); + insideRightCorner = pt.get("Basic.insideRightCorner"); + fillTile(insideRightCorner, pt, "InsideRightCorner"); + insideBottomCorner = pt.get("Basic.insideBottomCorner"); + fillTile(insideBottomCorner, pt, "InsideBottomCorner"); + insideTopCorner = pt.get("Basic.insideTopCorner"); + fillTile(insideTopCorner, pt, "InsideTopCorner"); + + joinY = pt.get("Basic.joinY"); + fillTile(joinY, pt, "JoinY"); + joinYRightCorner = pt.get("Basic.joinYRightCorner"); + fillTile(joinYRightCorner, pt, "JoinYRightCorner"); + joinRightCorner = pt.get("Basic.joinRightCorner"); + fillTile(joinRightCorner, pt, "JoinRightCorner"); + joinOutXRightCorner = pt.get("Basic.joinOutXRightCorner"); + fillTile(joinOutXRightCorner, pt, "JoinOutXRightCorner"); + joinOutX = pt.get("Basic.joinOutX"); + fillTile(joinOutX, pt, "JoinOutX"); + joinOutXTopCorner = pt.get("Basic.joinOutXTopCorner"); + fillTile(joinOutXTopCorner, pt, "JoinOutXTopCorner"); + joinTopCorner = pt.get("Basic.joinTopCorner"); + fillTile(joinTopCorner, pt, "JoinTopCorner"); + joinOutYTopCorner = pt.get("Basic.joinOutYTopCorner"); + fillTile(joinOutYTopCorner, pt, "JoinOutYTopCorner"); + joinOutY = pt.get("Basic.joinOutY"); + fillTile(joinOutY, pt, "JoinOutY"); + joinOutYLeftCorner = pt.get("Basic.joinOutYLeftCorner"); + fillTile(joinOutYLeftCorner, pt, "JoinOutYLeftCorner"); + joinLeftCorner = pt.get("Basic.joinLeftCorner"); + fillTile(joinLeftCorner, pt, "JoinLeftCorner"); + joinXLeftCorner = pt.get("Basic.joinXLeftCorner"); + fillTile(joinXLeftCorner, pt, "JoinXLeftCorner"); + joinX = pt.get("Basic.joinX"); + fillTile(joinX, pt, "JoinX"); + joinXBottomCorner = pt.get("Basic.joinXBottomCorner"); + fillTile(joinXBottomCorner, pt, "JoinXBottomCorner"); + joinBottomCorner = pt.get("Basic.joinBottomCorner"); + fillTile(joinBottomCorner, pt, "JoinBottomCorner"); + joinYBottomCorner = pt.get("Basic.joinYBottomCorner"); + fillTile(joinYBottomCorner, pt, "JoinYBottomCorner"); + + upStairs1 = pt.get("Basic.upStairs1"); upStairs2 = pt.get("Basic.upStairs2"); upStairs3 = pt.get("Basic.upStairs3"); @@ -146,7 +195,7 @@ namespace FALevelGen size_t i = 0; for(; i < tileVec.size() && random > tileVec[i].second; i++) random -= tileVec[i].second; - + return tileVec[i].first; } @@ -171,4 +220,81 @@ namespace FALevelGen mDoorMap[second] = first; } } + + size_t TileSet::convert(TileSetEnum::TileSetEnum val) + { + switch(val) + { + case TileSetEnum::xWall: return xWall; + case TileSetEnum::outsideXWall: return outsideXWall; + case TileSetEnum::yWall: return yWall; + case TileSetEnum::outsideYWall: return outsideYWall; + case TileSetEnum::bottomCorner: return bottomCorner; + case TileSetEnum::outsideBottomCorner: return outsideBottomCorner; + case TileSetEnum::rightCorner: return rightCorner; + case TileSetEnum::outsideRightCorner: return outsideRightCorner; + case TileSetEnum::leftCorner: return leftCorner; + case TileSetEnum::outsideLeftCorner: return outsideLeftCorner; + case TileSetEnum::topCorner: return topCorner; + case TileSetEnum::outsideTopCorner: return outsideTopCorner; + case TileSetEnum::floor: return floor; + case TileSetEnum::blank: return blank; + case TileSetEnum::xDoor: return xDoor; + case TileSetEnum::yDoor: return yDoor; + + case TileSetEnum::upStairs1: return upStairs1; + case TileSetEnum::upStairs2: return upStairs2; + case TileSetEnum::upStairs3: return upStairs3; + + case TileSetEnum::upStairs4: return upStairs4; + case TileSetEnum::upStairs5: return upStairs5; + case TileSetEnum::upStairs6: return upStairs6; + + case TileSetEnum::upStairs7: return upStairs7; + case TileSetEnum::upStairs8: return upStairs8; + case TileSetEnum::upStairs9: return upStairs9; + + case TileSetEnum::downStairs1: return downStairs1; + case TileSetEnum::downStairs2: return downStairs2; + case TileSetEnum::downStairs3: return downStairs3; + + case TileSetEnum::downStairs4: return downStairs4; + case TileSetEnum::downStairs5: return downStairs5; + case TileSetEnum::downStairs6: return downStairs6; + + case TileSetEnum::downStairs7: return downStairs7; + case TileSetEnum::downStairs8: return downStairs8; + case TileSetEnum::downStairs9: return downStairs9; + + case TileSetEnum::insideXWall: return insideXWall; + case TileSetEnum::insideXWallEnd: return insideXWallEnd; + case TileSetEnum::insideXWallEndBack: return insideXWallEndBack; + case TileSetEnum::insideYWall: return insideYWall; + case TileSetEnum::insideYWallEnd: return insideYWallEnd; + case TileSetEnum::insideYWallEndBack: return insideYWallEndBack; + case TileSetEnum::insideLeftCorner: return insideLeftCorner; + case TileSetEnum::insideRightCorner: return insideRightCorner; + case TileSetEnum::insideBottomCorner: return insideBottomCorner; + case TileSetEnum::insideTopCorner: return insideTopCorner; + + case TileSetEnum::joinY: return joinY; + case TileSetEnum::joinYRightCorner: return joinYRightCorner; + case TileSetEnum::joinRightCorner: return joinRightCorner; + case TileSetEnum::joinOutXRightCorner: return joinOutXRightCorner; + case TileSetEnum::joinOutX: return joinOutX; + case TileSetEnum::joinOutXTopCorner: return joinOutXTopCorner; + case TileSetEnum::joinTopCorner: return joinTopCorner; + case TileSetEnum::joinOutYTopCorner: return joinOutYTopCorner; + case TileSetEnum::joinOutY: return joinOutY; + case TileSetEnum::joinOutYLeftCorner: return joinOutYLeftCorner; + case TileSetEnum::joinLeftCorner: return joinLeftCorner; + case TileSetEnum::joinXLeftCorner: return joinXLeftCorner; + case TileSetEnum::joinX: return joinX; + case TileSetEnum::joinXBottomCorner: return joinXBottomCorner; + case TileSetEnum::joinBottomCorner: return joinBottomCorner; + case TileSetEnum::joinYBottomCorner: return joinYBottomCorner; + } + + return val; + } } diff --git a/apps/freeablo/falevelgen/tileset.h b/apps/freeablo/falevelgen/tileset.h index 6bfda6eec..6f655f555 100644 --- a/apps/freeablo/falevelgen/tileset.h +++ b/apps/freeablo/falevelgen/tileset.h @@ -11,6 +11,93 @@ namespace FALevelGen { + + namespace TileSetEnum + { + enum TileSetEnum + { + // this block and the block after it are related + // eg insideYWall MUST be equal to yWall + insideXWall + xWall, + yWall, + leftCorner, + rightCorner, + bottomCorner, + topCorner, + + insideXWall, + insideYWall, + insideLeftCorner, + insideRightCorner, + insideBottomCorner, + insideTopCorner, + + insideXWallEnd, + insideXWallEndBack, + insideYWallEnd, + insideYWallEndBack, + + + + outsideXWall, + outsideYWall, + outsideBottomCorner, + outsideRightCorner, + outsideLeftCorner, + outsideTopCorner, + floor, + blank, + xDoor, + yDoor, + + + joinY, + joinYRightCorner, + joinRightCorner, + joinOutXRightCorner, + joinOutX, + joinOutXTopCorner, + joinTopCorner, + joinOutYTopCorner, + joinOutY, + joinOutYLeftCorner, + joinLeftCorner, + joinXLeftCorner, + joinX, + joinXBottomCorner, + joinBottomCorner, + joinYBottomCorner, + + upStairs1, + upStairs2, + upStairs3, + + upStairs4, + upStairs5, + upStairs6, + + upStairs7, + upStairs8, + upStairs9, + + downStairs1, + downStairs2, + downStairs3, + + downStairs4, + downStairs5, + downStairs6, + + downStairs7, + downStairs8, + downStairs9, + + // these two just used internally in levelgen.cpp, not loaded from a file like the rest + upStairs, + downStairs + }; + } + class TileSet { public: @@ -31,7 +118,9 @@ namespace FALevelGen size_t floor; size_t blank; size_t xWallEnd; + size_t xWallEndBack; size_t yWallEnd; + size_t yWallEndBack; size_t xDoor; size_t yDoor; @@ -59,8 +148,38 @@ namespace FALevelGen size_t downStairs8; size_t downStairs9; + size_t insideXWall; + size_t insideXWallEnd; + size_t insideXWallEndBack; + size_t insideYWall; + size_t insideYWallEnd; + size_t insideYWallEndBack; + size_t insideLeftCorner; + size_t insideRightCorner; + size_t insideBottomCorner; + size_t insideTopCorner; + + size_t joinY; + size_t joinYRightCorner; + size_t joinRightCorner; + size_t joinOutXRightCorner; + size_t joinOutX; + size_t joinOutXTopCorner; + size_t joinTopCorner; + size_t joinOutYTopCorner; + size_t joinOutY; + size_t joinOutYLeftCorner; + size_t joinLeftCorner; + size_t joinXLeftCorner; + size_t joinX; + size_t joinXBottomCorner; + size_t joinBottomCorner; + size_t joinYBottomCorner; + size_t getRandomTile(size_t tile); std::map getDoorMap(); + + size_t convert(TileSetEnum::TileSetEnum val); private: std::map >, size_t> > mAlternatives; @@ -70,7 +189,6 @@ namespace FALevelGen void fillTile(size_t tile, boost::property_tree::ptree& pt, const std::string& str); void loadDoorMap(boost::property_tree::ptree& pt); - }; } diff --git a/apps/freeablo/main.cpp b/apps/freeablo/main.cpp index c501fff5e..8fe46e3a2 100644 --- a/apps/freeablo/main.cpp +++ b/apps/freeablo/main.cpp @@ -106,7 +106,7 @@ Level::Level* getLevel(size_t dLvl, const DiabloExe::DiabloExe& exe) return new Level::Level(Level::Dun::getTown(sector1, sector2, sector3, sector4), "levels/towndata/town.til", "levels/towndata/town.min", "levels/towndata/town.sol", "levels/towndata/town.cel", std::make_pair(25,29), std::make_pair(75,68), std::map()); } - else if(dLvl < 9) + else if(dLvl < 13) { return FALevelGen::generate(100, 100, dLvl, exe); } @@ -330,7 +330,7 @@ void runGameLoop(const bpo::variables_map& variables) FALevelGen::FAsrand(time(NULL)); - std::vector levels(9); + std::vector levels(13); int32_t currentLevel = variables["level"].as(); diff --git a/components/cel/celfile.cpp b/components/cel/celfile.cpp index 779dd0e91..5bd1a1669 100644 --- a/components/cel/celfile.cpp +++ b/components/cel/celfile.cpp @@ -172,6 +172,8 @@ namespace Cel palFilename = Misc::StringUtils::replaceEnd("l1.cel", "l1.pal", filename); else if(Misc::StringUtils::endsWith(filename, "l2.cel")) palFilename = Misc::StringUtils::replaceEnd("l2.cel", "l2.pal", filename); + else if(Misc::StringUtils::endsWith(filename, "l3.cel")) + palFilename = Misc::StringUtils::replaceEnd("l3.cel", "l3.pal", filename); else palFilename = "levels/towndata/town.pal"; diff --git a/components/diabloexe/diabloexe.cpp b/components/diabloexe/diabloexe.cpp index 1f960fa36..32f29d150 100644 --- a/components/diabloexe/diabloexe.cpp +++ b/components/diabloexe/diabloexe.cpp @@ -140,8 +140,11 @@ namespace DiabloExe for(std::map::const_iterator it = mMonsters.begin(); it != mMonsters.end(); ++it) { - if(levelNum >= it->second.minDunLevel && levelNum <= it->second.maxDunLevel) + if(levelNum >= it->second.minDunLevel && levelNum <= it->second.maxDunLevel && + it->second.monsterName != "Wyrm" && it->second.monsterName != "Cave Slug") // Exception, these monster's CEL files don't exist + { retval.push_back(&(it->second)); + } } return retval; diff --git a/resources/tilesets/l1.ini b/resources/tilesets/l1.ini index d87909ffb..564747323 100644 --- a/resources/tilesets/l1.ini +++ b/resources/tilesets/l1.ini @@ -21,6 +21,36 @@ yWallEnd = 16 xDoor = 26 yDoor = 25 + +insideXWall = 2 +insideXWallEnd = 17 +insideXWallEndBack = 7 +insideYWall = 1 +insideYWallEnd = 16 +insideYWallEndBack = 6 +insideLeftCorner = 7 +insideRightCorner = 6 +insideBottomCorner = 3 +insideTopCorner = 4 + +joinY = 4 +joinYRightCorner = 4 +joinRightCorner = 4 +joinOutXRightCorner = 6 +joinOutX = 19 +joinOutXTopCorner = 21 +joinTopCorner = 21 +joinOutYTopCorner = 21 +joinOutY = 18 +joinOutYLeftCorner = 7 +joinLeftCorner = 4 +joinXLeftCorner = 4 +joinX = 4 +joinXBottomCorner = 6 +joinBottomCorner = 4 +joinYBottomCorner = 2 + + # upStairs values define a 3x3 block of tiles representing stairs up upStairs1 = 22 upStairs2 = 66 diff --git a/resources/tilesets/l2.ini b/resources/tilesets/l2.ini index dcaea7977..224a11275 100644 --- a/resources/tilesets/l2.ini +++ b/resources/tilesets/l2.ini @@ -21,6 +21,36 @@ yWallEnd = 6 xDoor = 151 yDoor = 150 + +insideXWall = 21 +insideXWallEnd = 6 +insideXWallEndBack = 9 +insideYWall = 20 +insideYWallEnd = 6 +insideYWallEndBack = 7 +insideLeftCorner = 9 +insideRightCorner = 7 +insideBottomCorner = 6 +insideTopCorner = 8 + +joinY = 8 +joinYRightCorner = 8 +joinRightCorner = 8 +joinOutXRightCorner = 7 +joinOutX = 15 +joinOutXTopCorner = 13 +joinTopCorner = 13 +joinOutYTopCorner = 13 +joinOutY = 14 +joinOutYLeftCorner = 9 +joinLeftCorner = 8 +joinXLeftCorner = 8 +joinX = 8 +joinXBottomCorner = 7 +joinBottomCorner = 8 +joinYBottomCorner = 9 + + # upStairs values define a 3x3 block of tiles representing stairs up upStairs1= 3 upStairs2= 3 diff --git a/resources/tilesets/l3.ini b/resources/tilesets/l3.ini new file mode 100644 index 000000000..1434a6d91 --- /dev/null +++ b/resources/tilesets/l3.ini @@ -0,0 +1,109 @@ +# Data used to aid level generation +# Basic section defines the base tiles used to +# represent chunks of dungeons +[Basic] +xWall = 10 +outsideXWall = 2 +yWall = 9 +outsideYWall = 4 +bottomCorner = 12 +outsideBottomCorner = 6 +rightCorner = 14 +outsideRightCorner = 3 +leftCorner = 13 +outsideLeftCorner = 1 +topCorner = 11 +outsideTopCorner = 5 +floor = 107 +blank = 8 +xWallEnd = 15 +xWallEndBack = 10 +yWallEnd = 18 +yWallEndBack = 9 +xDoor = 146 +yDoor = 147 + +# upStairs values define a 3x3 block of tiles representing stairs up +upStairs1 = 156 +upStairs2 = 155 +upStairs3 = 8 + +upStairs4 = 153 +upStairs5 = 154 +upStairs6 = 10 + +upStairs7 = 107 +upStairs8 = 107 +upStairs9 = 107 + +# downStairs values define a 3x3 block for stairs down +downStairs1 = 8 +downStairs2 = 47 +downStairs3 = 107 + +downStairs4 = 8 +downStairs5 = 46 +downStairs6 = 107 + +downStairs7 = 8 +downStairs8 = 9 +downStairs9 = 107 + + +insideXWall = 134 +insideXWallEnd = 134 +insideXWallEndBack = 134 +insideYWall = 137 +insideYWallEnd = 137 +insideYWallEndBack = 137 +insideLeftCorner = 152 +insideRightCorner = 151 +insideBottomCorner = 143 +insideTopCorner = 150 + + +joinY = 130 +joinYRightCorner = 14 +joinRightCorner = 14 +joinOutXRightCorner = 14 +joinOutX = 139 +joinOutXTopCorner = 5 +joinTopCorner = 5 +joinOutYTopCorner = 5 +joinOutY = 140 +joinOutYLeftCorner = 13 +joinLeftCorner = 13 +joinXLeftCorner = 13 +joinX = 131 +joinXBottomCorner = 12 +joinBottomCorner = 12 +joinYBottomCorner = 12 + + +# Map of closed door til entries to open door til entries +[DoorMap] +146 = 148 +147 = 149 +#150 = 4 +# +# +## All sections other than Basic and DoorMap list aesthetic replacements +## for their corresponding value +## normal shows the percentage of tiles that will be the +## normal value from the Basic section, all others are of the format +## "index = weight", where weight adjusts it's frequency relative to the others +## No section for a block just means it will always be the "basic" version +[XWall] +normal = 50 +110 = 50 + + +[YWall] +normal = 50 +109 = 50 + + +[Floor] +normal = 33 +108 = 50 +7 = 50