diff --git a/Source/cursor.cpp b/Source/cursor.cpp index ed8f0aafd..9ccbb532b 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -183,7 +183,7 @@ void CheckRportal() void CheckCursMove() { - int i, sx, sy, fx, fy, mx, my, tx, ty, px, py, xx, yy, mi; + int i, sx, sy, fx, fy, mx, my, tx, ty, px, py, xx, yy, mi, columns, rows, xo, yo; char bv; BOOL flipflag, flipx, flipy; @@ -205,19 +205,19 @@ void CheckCursMove() } } } - if (sy > PANEL_TOP - 1 && track_isscrolling()) { + if (sy > PANEL_TOP - 1 && MouseX >= PANEL_LEFT && MouseX < PANEL_LEFT + PANEL_WIDTH && track_isscrolling()) { sy = PANEL_TOP - 1; } - sx -= (SCREEN_WIDTH % 64) / 2; - sy -= (VIEWPORT_HEIGHT % 32) / 2; + if (!zoomflag) { sx >>= 1; sy >>= 1; } - // Adjust by player offset - sx -= ScrollInfo._sxoff; - sy -= ScrollInfo._syoff; + // Adjust by player offset and tile grid alignment + CalcTileOffset(&xo, &yo); + sx -= ScrollInfo._sxoff - xo; + sy -= ScrollInfo._syoff - yo; // Predict the next frame when walking to avoid input jitter fx = plr[myplr]._pVar6 / 256; @@ -229,29 +229,23 @@ void CheckCursMove() sy -= fy; } - if (sx < 0) { - sx = 0; - } - if (sx >= SCREEN_WIDTH) { - sx = SCREEN_WIDTH; - } - if (sy < 0) { - sy = 0; - } - if (sy >= SCREEN_HEIGHT) { - sy = SCREEN_HEIGHT; - } - // Convert to tile grid - - tx = sx >> 6; // sx / TILE_WIDTH - ty = sy >> 5; // sy / TILE_HEIGHT - px = sx & (TILE_WIDTH - 1); - py = sy & (TILE_HEIGHT - 1); + mx = ViewX; + my = ViewY; + tx = sx / TILE_WIDTH; + ty = sy / TILE_HEIGHT; + ShiftGrid(&mx, &my, tx, ty); // Center player tile on screen - mx = ViewX + tx + ty - (zoomflag ? (SCREEN_WIDTH / TILE_WIDTH) : (SCREEN_WIDTH / 2 / TILE_WIDTH)); - my = ViewY + ty - tx; + TilesInView(&columns, &rows); + ShiftGrid(&mx, &my, -columns / 2, -(rows - RowsCoveredByPanel()) / 4); + if ((columns % 2) != 0) { + my++; + } + + // Shift position to match diamond grid aligment + px = sx % TILE_WIDTH; + py = sy % TILE_HEIGHT; // Shift position to match diamond grid aligment flipy = py < (px >> 1); diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index d2027f142..7925078f2 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -767,15 +767,15 @@ static void scrollrt_draw_dungeon(int sx, int sy, int dx, int dy) * @param y dPiece coordinate * @param sx Back buffer coordinate * @param sy Back buffer coordinate - * @param blocks Number of rows - * @param chunks Tile in a row + * @param rows Number of rows + * @param columns Tile in a row */ -static void scrollrt_drawFloor(int x, int y, int sx, int sy, int blocks, int chunks) +static void scrollrt_drawFloor(int x, int y, int sx, int sy, int rows, int columns) { assert(gpBuffer); - for (int i = 0; i < (blocks << 1); i++) { - for (int j = 0; j < chunks ; j++) { + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { if (x >= 0 && x < MAXDUNX && y >= 0 && y < MAXDUNY) { level_piece_id = dPiece[x][y]; if (level_piece_id != 0) { @@ -787,24 +787,22 @@ static void scrollrt_drawFloor(int x, int y, int sx, int sy, int blocks, int chu } else { world_draw_black_tile(sx, sy); } - x++; - y--; + ShiftGrid(&x, &y, 1, 0); sx += TILE_WIDTH; } // Return to start of row - x -= chunks; - y += chunks; - sx -= chunks * TILE_WIDTH; - sy += TILE_HEIGHT / 2; + ShiftGrid(&x, &y, -columns, 0); + sx -= columns * TILE_WIDTH; // Jump to next row + sy += TILE_HEIGHT / 2; if (i & 1) { x++; - chunks--; + columns--; sx += TILE_WIDTH / 2; } else { y++; - chunks++; + columns++; sx -= TILE_WIDTH / 2; } } @@ -819,15 +817,19 @@ static void scrollrt_drawFloor(int x, int y, int sx, int sy, int blocks, int chu * @param y dPiece coordinate * @param sx Back buffer coordinate * @param sy Back buffer coordinate - * @param blocks Number of rows - * @param chunks Tile in a row + * @param rows Number of rows + * @param columns Tile in a row */ -static void scrollrt_draw(int x, int y, int sx, int sy, int blocks, int chunks) +static void scrollrt_draw(int x, int y, int sx, int sy, int rows, int columns) { assert(gpBuffer); - for (int i = 0; i < (blocks << 1); i++) { - for (int j = 0; j < chunks ; j++) { + // Keep evaluating until MicroTiles can't affect screen + rows += MicroTileLen; + memset(dRendered, 0, sizeof(dRendered)); + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns ; j++) { if (x >= 0 && x < MAXDUNX && y >= 0 && y < MAXDUNY) { if (x + 1 < MAXDUNX && y - 1 >= 0 && sx + TILE_WIDTH <= SCREEN_X + SCREEN_WIDTH) { // Render objects behind walls first to prevent sprites, that are moving @@ -844,171 +846,255 @@ static void scrollrt_draw(int x, int y, int sx, int sy, int blocks, int chunks) scrollrt_draw_dungeon(x, y, sx, sy); } } - x++; - y--; + ShiftGrid(&x, &y, 1, 0); sx += TILE_WIDTH; } // Return to start of row - x -= chunks; - y += chunks; - sx -= chunks * TILE_WIDTH; - sy += TILE_HEIGHT / 2; + ShiftGrid(&x, &y, -columns, 0); + sx -= columns * TILE_WIDTH; // Jump to next row + sy += TILE_HEIGHT / 2; if (i & 1) { x++; - chunks--; + columns--; sx += TILE_WIDTH / 2; } else { y++; - chunks++; + columns++; sx -= TILE_WIDTH / 2; } } } /** - * @brief Configure render and process screen rows - * @param x Center of view in dPiece coordinate - * @param y Center of view in dPiece coordinate + * @brief Scale up the rendered part of the back buffer to take up the full view */ -static void DrawGame(int x, int y) +static void Zoom() { - int i, sx, sy, chunks, blocks; - int wdt, nSrcOff, nDstOff; + int wdt = SCREEN_WIDTH / 2; + int nSrcOff = SCREENXY(SCREEN_WIDTH / 2 - 1, VIEWPORT_HEIGHT / 2 - 1); + int nDstOff = SCREENXY(SCREEN_WIDTH - 1, VIEWPORT_HEIGHT - 1); + + if (PANELS_COVER) { + if (chrflag || questlog) { + wdt >>= 1; + nSrcOff -= wdt; + } else if (invflag || sbookflag) { + wdt >>= 1; + nSrcOff -= wdt; + nDstOff -= SPANEL_WIDTH; + } + } - sx = (SCREEN_WIDTH % TILE_WIDTH) / 2; - sy = (VIEWPORT_HEIGHT % TILE_HEIGHT) / 2; + BYTE *src = &gpBuffer[nSrcOff]; + BYTE *dst = &gpBuffer[nDstOff]; - if (zoomflag) { - chunks = ceil(SCREEN_WIDTH / TILE_WIDTH); - blocks = ceil(VIEWPORT_HEIGHT / TILE_HEIGHT); + for (int hgt = 0; hgt < VIEWPORT_HEIGHT / 2; hgt++) { + for (int i = 0; i < wdt; i++) { + *dst-- = *src; + *dst-- = *src; + src--; + } + memcpy(dst - BUFFER_WIDTH, dst, wdt * 2 + 1); + src -= BUFFER_WIDTH - wdt; + dst -= 2 * (BUFFER_WIDTH - wdt); + } +} - gpBufStart = &gpBuffer[BUFFER_WIDTH * SCREEN_Y]; - gpBufEnd = &gpBuffer[BUFFER_WIDTH * (VIEWPORT_HEIGHT + SCREEN_Y)]; +/** + * @brief Shifting the view area along the logical grid + * Note: this won't allow you to shift between even and odd rows + * @param horizontal Shift the screen left or right + * @param vertical Shift the screen up or down + */ +void ShiftGrid(int *x, int *y, int horizontal, int vertical) +{ + *x += vertical + horizontal; + *y += vertical - horizontal; +} + +/** + * @brief Gets the number of rows covered by the main panel + */ +int RowsCoveredByPanel() +{ + if (SCREEN_WIDTH <= PANEL_WIDTH) { + return 0; + } + + int rows = PANEL_HEIGHT / TILE_HEIGHT * 2; + if (!zoomflag) { + rows /= 2; + } + + return rows; +} + +/** + * @brief Calculate the offset needed for centering tiles in view area + * @param offsetX Offset in pixels + * @param offsetY Offset in pixels + */ +void CalcTileOffset(int *offsetX, int *offsetY) +{ + int x, y; + + if (zoomflag) { + x = SCREEN_WIDTH % TILE_WIDTH; + y = VIEWPORT_HEIGHT % TILE_HEIGHT; } else { - sy -= TILE_HEIGHT; + x = (SCREEN_WIDTH / 2) % TILE_WIDTH; + y = (VIEWPORT_HEIGHT / 2 + TILE_HEIGHT / 2) % TILE_HEIGHT; + } + + if (x) + x = (TILE_WIDTH - x) / 2; + if (y) + y = (TILE_HEIGHT - y) / 2; - chunks = ceil(SCREEN_WIDTH / 2 / TILE_WIDTH) + 1; // TODO why +1? - blocks = ceil(VIEWPORT_HEIGHT / 2 / TILE_HEIGHT); + *offsetX = x; + *offsetY = y; +} + +/** + * @brief Calculate the needed diamond tile to cover the view area + * @param columns Tiles needed per row + * @param rows Both even and odd rows + */ +void TilesInView(int *rcolumns, int *rrows) +{ + int columns = SCREEN_WIDTH / TILE_WIDTH; + if (SCREEN_WIDTH % TILE_WIDTH) { + columns++; + } + int rows = VIEWPORT_HEIGHT / (TILE_HEIGHT / 2); + if (VIEWPORT_HEIGHT % (TILE_HEIGHT / 2)) { + rows++; + } - gpBufStart = &gpBuffer[(-(TILE_HEIGHT / 2 + 1) + SCREEN_Y) * BUFFER_WIDTH]; - gpBufEnd = &gpBuffer[((VIEWPORT_HEIGHT - TILE_HEIGHT) / 2 + SCREEN_Y) * BUFFER_WIDTH]; + if (!zoomflag) { + // Half the number of tiles, rounded up + if (columns % 2) { + columns++; + } + columns /= 2; + if (rows % 2) { + rows++; + } + rows /= 2; } + rows++; // Cover lower edge saw tooth, right edge accounted for in scrollrt_draw() + + *rcolumns = columns; + *rrows = rows; +} - sx += ScrollInfo._sxoff + SCREEN_X; - sy += ScrollInfo._syoff + SCREEN_Y + TILE_HEIGHT / 2 - 1; +/** + * @brief Configure render and process screen rows + * @param x Center of view in dPiece coordinate + * @param y Center of view in dPiece coordinate + */ +static void DrawGame(int x, int y) +{ + int i, sx, sy, columns, rows, xo, yo; + + // Limit rendering to the view area + if (zoomflag) + gpBufEnd = &gpBuffer[BUFFER_WIDTH * (VIEWPORT_HEIGHT + SCREEN_Y)]; + else + gpBufEnd = &gpBuffer[BUFFER_WIDTH * (VIEWPORT_HEIGHT / 2 + SCREEN_Y)]; - // Center screen - x -= chunks; - y--; + // Adjust by player offset and tile grid alignment + CalcTileOffset(&xo, &yo); + sx = ScrollInfo._sxoff - xo + SCREEN_X; + sy = ScrollInfo._syoff - yo + SCREEN_Y + (TILE_HEIGHT / 2 - 1); - // Keep evaulating untill MicroTiles can't affect screen - blocks += ceil((double)MicroTileLen / 2); + // Center player tile on screen + TilesInView(&columns, &rows); + ShiftGrid(&x, &y, -columns / 2, -(rows - RowsCoveredByPanel()) / 4); + if ((columns % 2) == 0) { + y--; + } + // Skip rendering parts covered by the panels if (PANELS_COVER) { if (zoomflag) { if (chrflag || questlog) { - x += 2; - y -= 2; + ShiftGrid(&x, &y, 2, 0); + columns -= 4; sx += SPANEL_WIDTH - TILE_WIDTH / 2; - chunks -= 4; } if (invflag || sbookflag) { - x += 2; - y -= 2; - sx -= TILE_WIDTH / 2; - chunks -= 4; + ShiftGrid(&x, &y, 2, 0); + columns -= 4; + sx += -TILE_WIDTH / 2; + } + } else { + if (chrflag || questlog) { + ShiftGrid(&x, &y, 1, 0); + columns -= 2; + sx += -TILE_WIDTH / 2 / 2; // SPANEL_WIDTH accounted for in Zoom() + } + if (invflag || sbookflag) { + ShiftGrid(&x, &y, 1, 0); + columns -= 2; + sx += -TILE_WIDTH / 2 / 2; } } } - switch (ScrollInfo._sdir) { + // Draw areas moving in and out of the screen + switch (ScrollInfo._sdir) { case SDIR_N: sy -= TILE_HEIGHT; - x--; - y--; - blocks++; + ShiftGrid(&x, &y, 0, -1); + rows += 2; break; case SDIR_NE: sy -= TILE_HEIGHT; - x--; - y--; - chunks++; - blocks++; + ShiftGrid(&x, &y, 0, -1); + columns++; + rows += 2; break; case SDIR_E: - chunks++; + columns++; break; case SDIR_SE: - chunks++; - blocks++; + columns++; + rows++; break; case SDIR_S: - blocks++; + rows += 2; break; case SDIR_SW: sx -= TILE_WIDTH; - x--; - y++; - chunks++; - blocks++; + ShiftGrid(&x, &y, -1, 0); + columns++; + rows++; break; case SDIR_W: sx -= TILE_WIDTH; - x--; - y++; - chunks++; + ShiftGrid(&x, &y, -1, 0); + columns++; break; case SDIR_NW: - sx -= TILE_WIDTH; - sy -= TILE_HEIGHT; - x -= 2; - chunks++; - blocks++; + sx -= TILE_WIDTH / 2; + sy -= TILE_HEIGHT / 2; + x--; + columns++; + rows++; break; } - memset(dRendered, 0, sizeof(dRendered)); - scrollrt_drawFloor(x, y, sx, sy, blocks, chunks); - scrollrt_draw(x, y, sx, sy, blocks, chunks); + scrollrt_drawFloor(x, y, sx, sy, rows, columns); + scrollrt_draw(x, y, sx, sy, rows, columns); - gpBufStart = &gpBuffer[BUFFER_WIDTH * SCREEN_Y]; + // Allow rendering to the whole screen gpBufEnd = &gpBuffer[BUFFER_WIDTH * (SCREEN_HEIGHT + SCREEN_Y)]; - if (zoomflag) - return; - - nSrcOff = SCREENXY(TILE_WIDTH / 2, VIEWPORT_HEIGHT / 2 - (TILE_HEIGHT / 2 + 1)); - nDstOff = SCREENXY(0, VIEWPORT_HEIGHT - 2); - wdt = SCREEN_WIDTH / 2; - if (PANELS_COVER) { - if (chrflag || questlog) { - nSrcOff = SCREENXY(TILE_WIDTH / 2 + SPANEL_WIDTH / 4, VIEWPORT_HEIGHT / 2 - (TILE_HEIGHT / 2 + 1)); - nDstOff = SCREENXY(SPANEL_WIDTH, VIEWPORT_HEIGHT - 2); - wdt = (SCREEN_WIDTH - SPANEL_WIDTH) / 2; - } else if (invflag || sbookflag) { - nSrcOff = SCREENXY(TILE_WIDTH / 2 + SPANEL_WIDTH / 4, VIEWPORT_HEIGHT / 2 - (TILE_HEIGHT / 2 + 1)); - nDstOff = SCREENXY(0, VIEWPORT_HEIGHT - 2); - wdt = (SCREEN_WIDTH - SPANEL_WIDTH) / 2; - } - } - - int hgt; - BYTE *src, *dst1, *dst2; - - src = &gpBuffer[nSrcOff]; - dst1 = &gpBuffer[nDstOff]; - dst2 = &gpBuffer[nDstOff + BUFFER_WIDTH]; - - for (hgt = VIEWPORT_HEIGHT / 2; hgt != 0; hgt--, src -= BUFFER_WIDTH + wdt, dst1 -= 2 * (BUFFER_WIDTH + wdt), dst2 -= 2 * (BUFFER_WIDTH + wdt)) { - for (i = wdt; i != 0; i--) { - *dst1++ = *src; - *dst1++ = *src; - *dst2++ = *src; - *dst2++ = *src; - src++; - } + if (!zoomflag) { + Zoom(); } } @@ -1078,6 +1164,8 @@ void DrawView(int StartX, int StartY) DrawManaFlask(); } +extern SDL_Surface *pal_surface; + /** * @brief Render the whole screen black */ @@ -1085,16 +1173,15 @@ void ClearScreenBuffer() { lock_buf(3); - assert(gpBuffer); + assert(pal_surface != NULL); - int i; - BYTE *dst; - - dst = &gpBuffer[SCREENXY(0, 0)]; - - for (i = 0; i < SCREEN_HEIGHT; i++, dst += BUFFER_WIDTH) { - memset(dst, 0, SCREEN_WIDTH); - } + SDL_Rect SrcRect = { + SCREEN_X, + SCREEN_Y, + SCREEN_WIDTH, + SCREEN_HEIGHT, + }; + SDL_FillRect(pal_surface, &SrcRect, 0); unlock_buf(3); } diff --git a/Source/scrollrt.h b/Source/scrollrt.h index eb455e43c..5fab9fccb 100644 --- a/Source/scrollrt.h +++ b/Source/scrollrt.h @@ -22,6 +22,10 @@ extern void (*DrawPlrProc)(int, int, int, int, int, BYTE *, int, int, int, int); void ClearCursor(); void DrawMissile(int x, int y, int sx, int sy, BOOL pre); void DrawDeadPlayer(int x, int y, int sx, int sy); +void ShiftGrid(int *x, int *y, int horizontal, int vertical); +int RowsCoveredByPanel(); +void CalcTileOffset(int *offsetX, int *offsetY); +void TilesInView(int *columns, int *rows); void DrawView(int StartX, int StartY); void ClearScreenBuffer(); #ifdef _DEBUG diff --git a/defs.h b/defs.h index a399f2c41..e90d353b4 100644 --- a/defs.h +++ b/defs.h @@ -111,9 +111,6 @@ #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 -#define ZOOM_WIDTH (SCREEN_WIDTH / 2 + TILE_WIDTH) -#define ZOOM_HEIGHT (VIEWPORT_HEIGHT / 2 + TILE_HEIGHT + TILE_HEIGHT / 2) - // If defined, use 32-bit colors instead of 8-bit [Default -> Undefined] //#define RGBMODE