From 402d5a4a1b9c41cf19dd0df9ae2848dafa0de5e7 Mon Sep 17 00:00:00 2001 From: Eric Robinson <68359262+kphoenix137@users.noreply.github.com> Date: Sat, 13 Dec 2025 04:01:51 -0500 Subject: [PATCH] Render dirt in OOB area (#8331) --- Source/engine/render/scrollrt.cpp | 76 +++++++++++++++++++++++++++++-- docs/CHANGELOG.md | 1 + 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index 3b1bbeb9f..6e85670c0 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -928,10 +928,8 @@ void DrawFloor(const Surface &out, const Lightmap &lightmap, Point tilePosition, { for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++, tilePosition += Direction::East, targetBufferPosition.x += TILE_WIDTH) { - if (!InDungeonBounds(tilePosition)) { - world_draw_black_tile(out, targetBufferPosition.x, targetBufferPosition.y); + if (!InDungeonBounds(tilePosition)) continue; - } if (IsFloor(tilePosition)) { DrawFloorTile(out, lightmap, tilePosition, targetBufferPosition); } @@ -1018,6 +1016,77 @@ void DrawTileContent(const Surface &out, const Lightmap &lightmap, Point tilePos } } +void DrawDirtTile(const Surface &out, const Lightmap &lightmap, Point tilePosition, Point targetBufferPosition) +{ + // This should be the *top-left* of the 2×2 dirt pattern in the actual dungeon. + // You might need to tweak these to where your dirt patch actually lives. + constexpr Point base { 0, 0 }; + + // Decide which of the 4 tiles of the 2×2 block to use, + // based on where this OOB tile is in the world grid. + const int ox = (tilePosition.x & 1); // 0 or 1 + const int oy = (tilePosition.y & 1); // 0 or 1 + + Point sample { + base.x + ox, + base.y + oy, + }; + + // Safety: clamp in case tilePosition is wildly outside and base+offset ever escapes + sample.x = std::clamp(sample.x, 0, MAXDUNX - 1); + sample.y = std::clamp(sample.y, 0, MAXDUNY - 1); + + if (!InDungeonBounds(sample) || dPiece[sample.x][sample.y] == 0) { + // Failsafe: if our sample somehow isn't valid, fall back to black + world_draw_black_tile(out, targetBufferPosition.x, targetBufferPosition.y); + return; + } + + const int lightTableIndex = dLight[sample.x][sample.y]; + + // Let the normal dungeon tile renderer compose the full tile + DrawCell(out, lightmap, sample, targetBufferPosition, lightTableIndex); +} + +/** + * @brief Render a row of tiles + * @param out Buffer to render to + * @param lightmap Per-pixel light buffer + * @param tilePosition dPiece coordinates + * @param targetBufferPosition Target buffer coordinates + * @param rows Number of rows + * @param columns Tile in a row + */ +void DrawOOB(const Surface &out, const Lightmap &lightmap, Point tilePosition, Point targetBufferPosition, int rows, int columns) +{ + for (int i = 0; i < rows + 5; i++) { // 5 extra rows needed to make sure everything gets rendered at the bottom half of the screen + for (int j = 0; j < columns; j++, tilePosition += Direction::East, targetBufferPosition.x += TILE_WIDTH) { + if (!InDungeonBounds(tilePosition)) { + if (leveltype == DTYPE_TOWN) { + world_draw_black_tile(out, targetBufferPosition.x, targetBufferPosition.y); + } else { + DrawDirtTile(out, lightmap, tilePosition, targetBufferPosition); + } + } + } + // Return to start of row + tilePosition += Displacement(Direction::West) * columns; + targetBufferPosition.x -= columns * TILE_WIDTH; + + // Jump to next row + targetBufferPosition.y += TILE_HEIGHT / 2; + if ((i & 1) != 0) { + tilePosition.x++; + columns--; + targetBufferPosition.x += TILE_WIDTH / 2; + } else { + tilePosition.y++; + columns++; + targetBufferPosition.x -= TILE_WIDTH / 2; + } + } +} + /** * @brief Scale up the top left part of the buffer 2x. */ @@ -1183,6 +1252,7 @@ void DrawGame(const Surface &fullOut, Point position, Displacement offset) DrawFloor(out, lightmap, position, Point {} + offset, rows, columns); DrawTileContent(out, lightmap, position, Point {} + offset, rows, columns); + DrawOOB(out, lightmap, position, Point {} + offset, rows, columns); if (*GetOptions().Graphics.zoom) { Zoom(fullOut.subregionY(0, gnViewportHeight)); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 392443929..9257d9910 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Graphics / Audio - Music doesn't unmute when focus is lost on level transition with Auto Pause On Focus Lost disabled +- Image ghosting visible on border of map in higher resolutions ## DevilutionX 1.5.2