From 3812d75480205105d7f624dbffef35ad01c8f34f Mon Sep 17 00:00:00 2001 From: ephphatha Date: Thu, 23 Jun 2022 17:35:00 +1000 Subject: [PATCH] Use point/size for WillThemeRoomFit, rename to match use --- Source/levels/gendung.cpp | 68 ++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/Source/levels/gendung.cpp b/Source/levels/gendung.cpp index be36c13a4..c7c2cb653 100644 --- a/Source/levels/gendung.cpp +++ b/Source/levels/gendung.cpp @@ -78,37 +78,52 @@ std::unique_ptr LoadMinData(size_t &tileCount) } } -bool WillThemeRoomFit(int floor, int x, int y, int minSize, int maxSize, int *width, int *height) +/** + * @brief Starting from the origin point determine how much floor space is available with the given bounds + * + * Essentially looks for the widest/tallest rectangular area of at least the minimum size, but due to a weird/buggy + * bounds check can return an area smaller than the available width/height. + * + * @param floor what value defines floor tiles within a dungeon + * @param origin starting point for the search + * @param minSize minimum allowable value for both dimensions + * @param maxSize maximum allowable value for both dimensions + * @return how much width/height is available for a theme room or an empty optional if there's not enough space + */ +std::optional GetSizeForThemeRoom(int floor, Point origin, int minSize, int maxSize) { - if (x + maxSize > DMAXX && y + maxSize > DMAXY) { - return false; // Original broken bounds check, avoids lower right corner + if (origin.x + maxSize > DMAXX && origin.y + maxSize > DMAXY) { + return {}; // Original broken bounds check, avoids lower right corner } - if (x + minSize > DMAXX || y + minSize > DMAXY) { - return false; // Skip definit OOB cases + if (origin.x + minSize > DMAXX || origin.y + minSize > DMAXY) { + return {}; // Skip definit OOB cases } - if (IsNearThemeRoom({ x, y })) { - return false; + if (IsNearThemeRoom(origin)) { + return {}; } - int maxWidth = std::min(DMAXX - x, maxSize); - int maxHeight = std::min(DMAXY - y, maxSize); + int maxWidth = std::min(DMAXX - origin.x, maxSize); + int maxHeight = std::min(DMAXY - origin.y, maxSize); // see if we can find a region at least as large as the minSize on each dimension for (int yOffset = 0; yOffset < maxHeight; yOffset++) { for (int xOffset = 0; xOffset < maxWidth; xOffset++) { // Start out looking for the widest area at least as tall as minSize - if (dungeon[x + xOffset][y + yOffset] == floor) + if (dungeon[origin.x + xOffset][origin.y + yOffset] == floor) continue; // found a floor tile earlier than the previous row, so the width has shrunk somewhat if (xOffset < minSize && yOffset < minSize) { // area is too small to hold a room of the desired size - return false; + return {}; } if (yOffset >= minSize) { - // area is at least as high as necessary, this row now defines our max height (also breaks out of the outer loop) + // area is at least as high as necessary. If we wanted to find the largest rectangular area we should + // keep going and start checking for the largest total area we can find (could be either the current + // width and height, or maybe a narrower but taller region has a larger area). Instead to match + // vanilla Diablo logic we trigger a break from the outer loop and fall through to the other checks maxHeight = yOffset; } @@ -122,10 +137,7 @@ bool WillThemeRoomFit(int floor, int x, int y, int minSize, int maxSize, int *wi } } - *width = maxWidth - 2; - *height = maxHeight - 2; - - return true; + return Size { maxWidth, maxHeight } - 2; } void CreateThemeRoom(int themeIndex) @@ -611,21 +623,25 @@ void DRLG_PlaceThemeRooms(int minSize, int maxSize, int floor, int freq, bool rn memset(themeLoc, 0, sizeof(*themeLoc)); for (int j = 0; j < DMAXY; j++) { for (int i = 0; i < DMAXX; i++) { - int themeW = 0; - int themeH = 0; - if (dungeon[i][j] == floor && FlipCoin(freq) && WillThemeRoomFit(floor, i, j, minSize, maxSize, &themeW, &themeH)) { + if (dungeon[i][j] == floor && FlipCoin(freq)) { + std::optional themeSize = GetSizeForThemeRoom(floor, { i, j }, minSize, maxSize); + + if (!themeSize) + continue; + if (rndSize) { int min = minSize - 2; int max = maxSize - 2; - themeW = min + GenerateRnd(GenerateRnd(themeW - min + 1)); - if (themeW < min || themeW > max) - themeW = min; - themeH = min + GenerateRnd(GenerateRnd(themeH - min + 1)); - if (themeH < min || themeH > max) - themeH = min; + themeSize->width = min + GenerateRnd(GenerateRnd(themeSize->width - min + 1)); + if (themeSize->width < min || themeSize->width > max) + themeSize->width = min; + themeSize->height = min + GenerateRnd(GenerateRnd(themeSize->height - min + 1)); + if (themeSize->height < min || themeSize->height > max) + themeSize->height = min; } + THEME_LOC &theme = themeLoc[themeCount]; - theme.room = { Point { i, j } + Direction::South, Size { themeW, themeH } }; + theme.room = { Point { i, j } + Direction::South, *themeSize }; if (IsAnyOf(leveltype, DTYPE_CAVES, DTYPE_NEST)) { DRLG_RectTrans({ (theme.room.position + Direction::South).megaToWorld(), theme.room.size * 2 - 5 }); } else {