diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 739a783c2..9fcd48e4d 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -96,6 +96,7 @@ set(libdevilutionx_SRCS engine/actor_position.cpp engine/animationinfo.cpp engine/assets.cpp + engine/backbuffer_state.cpp engine/direction.cpp engine/dx.cpp engine/load_cel.cpp diff --git a/Source/DiabloUI/progress.cpp b/Source/DiabloUI/progress.cpp index 55fc19808..56d4d8cf4 100644 --- a/Source/DiabloUI/progress.cpp +++ b/Source/DiabloUI/progress.cpp @@ -96,14 +96,19 @@ bool UiProgressDialog(int (*fnfunc)()) // Blit the background once and then free it. ProgressLoadBackground(); + ProgressRenderBackground(); - if (RenderDirectlyToOutputSurface && IsDoubleBuffered()) { - // Blit twice for triple buffering. - for (unsigned i = 0; i < 2; ++i) { - UiFadeIn(); + + if (RenderDirectlyToOutputSurface && PalSurface != nullptr) { + // Render into all the backbuffers if there are multiple. + const void *initialPixels = PalSurface->pixels; + UiFadeIn(); + while (PalSurface->pixels != initialPixels) { ProgressRenderBackground(); + UiFadeIn(); } } + ProgressFreeBackground(); ProgressLoadForeground(); diff --git a/Source/capture.cpp b/Source/capture.cpp index 8e5021ab4..c5e855f02 100644 --- a/Source/capture.cpp +++ b/Source/capture.cpp @@ -8,6 +8,7 @@ #include #include "DiabloUI/diabloui.h" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "engine/palette.h" #include "utils/file_util.h" @@ -195,7 +196,7 @@ void CaptureScreen() system_palette[i] = palette[i]; } palette_update(); - force_redraw = 255; + RedrawEverything(); } } // namespace devilution diff --git a/Source/control.cpp b/Source/control.cpp index a246f6449..041855df3 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -16,6 +16,7 @@ #include "controls/modifier_hints.h" #include "controls/plrctrls.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/render/clx_render.hpp" @@ -57,18 +58,10 @@ namespace devilution { -/** - * @brief Set if the life flask needs to be redrawn during next frame - */ -bool drawhpflag; bool dropGoldFlag; bool chrbtn[4]; bool lvlbtndown; int dropGoldValue; -/** - * @brief Set if the mana flask needs to be redrawn during the next frame - */ -bool drawmanaflag; bool chrbtnactive; UiFlags InfoColor; int sbooktab; @@ -76,7 +69,6 @@ int8_t initialDropGoldIndex; bool talkflag; bool sbookflag; bool chrflag; -bool drawbtnflag; StringOrView InfoString; bool panelflag; int initialDropGoldValue; @@ -251,7 +243,7 @@ void DrawFlaskLower(const Surface &out, const Surface &sourceBuffer, int offset, void SetButtonStateDown(int btnId) { PanelButtons[btnId] = true; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = true; } @@ -703,8 +695,8 @@ void InitControlPan() buttonEnabled = false; chrbtnactive = false; InfoString = {}; - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); chrflag = false; spselflag = false; sbooktab = 0; @@ -758,7 +750,7 @@ void ClearPanBtn() { for (bool &panelButton : PanelButtons) panelButton = false; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = false; } @@ -772,7 +764,7 @@ void DoPanBtn() if (MousePosition.x >= PanBtnPos[i].x + mainPanelPosition.x && MousePosition.x <= x) { if (MousePosition.y >= PanBtnPos[i].y + mainPanelPosition.y && MousePosition.y <= y) { PanelButtons[i] = true; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = true; } } @@ -782,7 +774,7 @@ void DoPanBtn() Player &myPlayer = *MyPlayer; myPlayer._pRSpell = SPL_INVALID; myPlayer._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; + RedrawEverything(); return; } DoSpeedBook(); @@ -889,7 +881,7 @@ void CheckBtnUp() bool gamemenuOff = true; const Point mainPanelPosition = GetMainPanel().position; - drawbtnflag = true; + RedrawComponent(PanelDrawComponent::ControlButtons); panbtndown = false; for (int i = 0; i < 8; i++) { @@ -1223,8 +1215,6 @@ void DrawTalkPan(const Surface &out) if (!talkflag) return; - force_redraw = 255; - const Point mainPanelPosition = GetMainPanel().position; DrawPanelBox(out, MakeSdlRect(175, sgbPlrTalkTbl + 20, 294, 5), mainPanelPosition + Displacement { 175, 4 }); @@ -1329,7 +1319,7 @@ void control_type_message() if (!IsChatAvailable()) return; - talkflag = true; + RedrawComponent(PanelDrawComponent::ChatInput); SDL_Rect rect = MakeSdlRect(GetMainPanel().position.x + 200, GetMainPanel().position.y + 22, 250, 39); SDL_SetTextInputRect(&rect); TalkMessage[0] = '\0'; @@ -1337,7 +1327,7 @@ void control_type_message() talkButtonDown = false; } sgbPlrTalkTbl = GetMainPanel().size.height + 16; - force_redraw = 255; + RedrawEverything(); TalkSaveIndex = NextTalkSave; SDL_StartTextInput(); } @@ -1347,7 +1337,7 @@ void control_reset_talk() talkflag = false; SDL_StopTextInput(); sgbPlrTalkTbl = 0; - force_redraw = 255; + RedrawEverything(); } bool IsTalkActive() diff --git a/Source/control.h b/Source/control.h index f260336e7..d54973988 100644 --- a/Source/control.h +++ b/Source/control.h @@ -32,12 +32,10 @@ namespace devilution { constexpr Size SidePanelSize { 320, 352 }; -extern bool drawhpflag; extern bool dropGoldFlag; extern bool chrbtn[4]; extern bool lvlbtndown; extern int dropGoldValue; -extern bool drawmanaflag; extern bool chrbtnactive; extern UiFlags InfoColor; extern int sbooktab; @@ -45,7 +43,6 @@ extern int8_t initialDropGoldIndex; extern bool talkflag; extern bool sbookflag; extern bool chrflag; -extern bool drawbtnflag; extern StringOrView InfoString; extern bool panelflag; extern int initialDropGoldValue; diff --git a/Source/cursor.cpp b/Source/cursor.cpp index 9d85f74ce..23f23f4e0 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -12,6 +12,7 @@ #include "controls/plrctrls.h" #include "doom.h" #include "engine.h" +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/point.hpp" #include "engine/render/clx_render.hpp" @@ -381,7 +382,7 @@ void CheckCursMove() ObjectUnderCursor = nullptr; pcursitem = -1; if (pcursinvitem != -1) { - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); } pcursinvitem = -1; pcursstashitem = uint16_t(-1); diff --git a/Source/debug.cpp b/Source/debug.cpp index c2c1a315b..43d3323ca 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -14,6 +14,7 @@ #include "automap.h" #include "control.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/point.hpp" #include "error.h" @@ -550,8 +551,8 @@ std::string DebugCmdRefillHealthMana(const string_view parameter) Player &myPlayer = *MyPlayer; myPlayer.RestoreFullLife(); myPlayer.RestoreFullMana(); - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); return "Ready for more."; } @@ -589,7 +590,7 @@ std::string DebugCmdChangeMana(const string_view parameter) int newMana = myPlayer._pMana + (change * 64); myPlayer._pMana = newMana; myPlayer._pManaBase = myPlayer._pMana + myPlayer._pMaxManaBase - myPlayer._pMaxMana; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); return "Mana has changed."; } diff --git a/Source/diablo.cpp b/Source/diablo.cpp index e9f138081..15e616dda 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -25,6 +25,7 @@ #include "discord/discord.h" #include "doom.h" #include "encrypt.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/demomode.h" #include "engine/dx.h" @@ -105,7 +106,6 @@ bool ReturnToMainMenu; bool gbProcessPlayers; bool gbLoadGame; bool cineflag; -int force_redraw; int PauseMode; bool gbBard; bool gbBarbarian; @@ -202,7 +202,7 @@ bool ProcessInput() plrctrls_every_frame(); if (!gbIsMultiplayer && gmenu_is_active()) { - force_redraw |= 1; + RedrawViewport(); return false; } @@ -704,7 +704,7 @@ void GameEventHandler(const SDL_Event &event, uint16_t modState) LastMouseButtonAction = MouseActionType::None; sgbMouseDown = CLICK_NONE; ShowProgress(GetCustomEvent(event.type)); - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); LoadPWaterPalette(); if (gbRunGame) @@ -730,11 +730,20 @@ void RunGameLoop(interface_mode uMsg) gbRunGame = true; gbProcessPlayers = true; gbRunGameResult = true; - force_redraw = 255; - DrawAndBlit(); + + RedrawEverything(); + if (!HeadlessMode) { + while (IsRedrawEverything()) { + // In direct rendering mode with double/triple buffering, we need + // to prepare all buffers before fading in. + DrawAndBlit(); + } + } + LoadPWaterPalette(); PaletteFadeIn(8); - force_redraw = 255; + InitBackbufferState(); + RedrawEverything(); gbGameLoopStartup = true; nthread_ignore_mutex(false); @@ -780,7 +789,7 @@ void RunGameLoop(interface_mode uMsg) ProcessInput(); if (!drawGame) continue; - force_redraw |= 1; + RedrawViewport(); DrawAndBlit(); continue; } @@ -807,7 +816,7 @@ void RunGameLoop(interface_mode uMsg) PaletteFadeOut(8); NewCursor(CURSOR_NONE); ClearScreenBuffer(); - force_redraw = 255; + RedrawEverything(); scrollrt_draw_game_screen(); previousHandler = SetEventHandler(previousHandler); assert(HeadlessMode || previousHandler == GameEventHandler); @@ -1324,7 +1333,7 @@ void GameLogic() sound_update(); CheckTriggers(); CheckQuests(); - force_redraw |= 1; + RedrawViewport(); pfile_update(false); plrctrls_after_game_logic(); @@ -1340,7 +1349,7 @@ void TimeoutCursor(bool bTimeout) AddPanelString(_("-- Network timeout --")); AddPanelString(_("-- Waiting for players --")); NewCursor(CURSOR_HOURGLASS); - force_redraw = 255; + RedrawEverything(); } scrollrt_draw_game_screen(); } else if (sgnTimeoutCurs != CURSOR_NONE) { @@ -1351,7 +1360,7 @@ void TimeoutCursor(bool bTimeout) NewCursor(sgnTimeoutCurs); sgnTimeoutCurs = CURSOR_NONE; InfoString = {}; - force_redraw = 255; + RedrawEverything(); } } @@ -2427,7 +2436,7 @@ void diablo_pause_game() LastMouseButtonAction = MouseActionType::None; } - force_redraw = 255; + RedrawEverything(); } } @@ -2584,7 +2593,6 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) if (firstflag) { CloseInventory(); - drawsbarflag = false; qtextflag = false; if (!HeadlessMode) { InitInv(); diff --git a/Source/diablo.h b/Source/diablo.h index d882008db..c1d1d130d 100644 --- a/Source/diablo.h +++ b/Source/diablo.h @@ -66,7 +66,6 @@ extern bool ReturnToMainMenu; extern bool gbProcessPlayers; extern DVL_API_FOR_TEST bool gbLoadGame; extern bool cineflag; -extern int force_redraw; /* These are defined in fonts.h */ extern void FontsCleanup(); extern DVL_API_FOR_TEST int PauseMode; diff --git a/Source/engine/backbuffer_state.cpp b/Source/engine/backbuffer_state.cpp new file mode 100644 index 000000000..631c3564a --- /dev/null +++ b/Source/engine/backbuffer_state.cpp @@ -0,0 +1,98 @@ +#include "engine/backbuffer_state.hpp" + +#include + +#include "engine/dx.h" +#include "utils/enum_traits.h" + +namespace devilution { +namespace { + +struct RedrawState { + enum { + RedrawNone, + RedrawViewportOnly, + RedrawAll + } Redraw; + std::array::value> redrawComponents; +}; + +struct BackbufferState { + RedrawState redrawState; + DrawnCursor cursor; +}; + +std::unordered_map States; + +BackbufferState &GetBackbufferState() +{ + // `PalSurface` is null in headless mode. + void *ptr = PalSurface != nullptr ? PalSurface->pixels : nullptr; + auto result = States.emplace(std::piecewise_construct, std::forward_as_tuple(ptr), std::forward_as_tuple()); + BackbufferState &state = result.first->second; + if (result.second) + state.redrawState.Redraw = RedrawState::RedrawAll; + return state; +} + +} // namespace + +bool IsRedrawEverything() +{ + return GetBackbufferState().redrawState.Redraw == RedrawState::RedrawAll; +} + +void RedrawViewport() +{ + for (auto &&it : States) { + if (it.second.redrawState.Redraw != RedrawState::RedrawAll) { + it.second.redrawState.Redraw = RedrawState::RedrawViewportOnly; + } + } +} + +bool IsRedrawViewport() +{ + return GetBackbufferState().redrawState.Redraw == RedrawState::RedrawViewportOnly; +} + +void RedrawComplete() +{ + GetBackbufferState().redrawState.Redraw = RedrawState::RedrawNone; +} + +void RedrawEverything() +{ + for (auto &&it : States) { + it.second.redrawState.Redraw = RedrawState::RedrawAll; + } +} + +void InitBackbufferState() +{ + States.clear(); +} + +void RedrawComponent(PanelDrawComponent component) +{ + for (auto &&it : States) { + it.second.redrawState.redrawComponents[static_cast(component)] = true; + } +} + +bool IsRedrawComponent(PanelDrawComponent component) +{ + return GetBackbufferState().redrawState.redrawComponents[static_cast(component)]; +} + +void RedrawComponentComplete(PanelDrawComponent component) +{ + GetBackbufferState().redrawState.redrawComponents[static_cast(component)] = false; +} + +DrawnCursor &GetDrawnCursor() +{ + return GetBackbufferState().cursor; +} + +} // namespace devilution diff --git a/Source/engine/backbuffer_state.hpp b/Source/engine/backbuffer_state.hpp new file mode 100644 index 000000000..2cb59bf19 --- /dev/null +++ b/Source/engine/backbuffer_state.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "engine/rectangle.hpp" +#include "engine/surface.hpp" + +namespace devilution { + +enum class PanelDrawComponent { + Health, + Mana, + ControlButtons, + Belt, + ChatInput, + + FIRST = Health, + LAST = ChatInput +}; + +struct DrawnCursor { + Rectangle rect; + uint8_t behindBuffer[8192]; +}; + +void InitBackbufferState(); + +void RedrawEverything(); +bool IsRedrawEverything(); +void RedrawViewport(); +bool IsRedrawViewport(); +void RedrawComplete(); + +void RedrawComponent(PanelDrawComponent component); +bool IsRedrawComponent(PanelDrawComponent component); +void RedrawComponentComplete(PanelDrawComponent component); + +DrawnCursor &GetDrawnCursor(); + +} // namespace devilution diff --git a/Source/engine/dx.h b/Source/engine/dx.h index edca17ad1..05a9dc352 100644 --- a/Source/engine/dx.h +++ b/Source/engine/dx.h @@ -12,6 +12,8 @@ namespace devilution { /** Whether we render directly to the screen surface, i.e. `PalSurface == GetOutputSurface()` */ extern bool RenderDirectlyToOutputSurface; +extern SDL_Surface *PalSurface; + Surface GlobalBackBuffer(); void dx_init(); diff --git a/Source/engine/palette.cpp b/Source/engine/palette.cpp index 8506827f1..64b2cc228 100644 --- a/Source/engine/palette.cpp +++ b/Source/engine/palette.cpp @@ -7,6 +7,7 @@ #include +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "engine/load_file.hpp" #include "engine/random.hpp" @@ -195,7 +196,7 @@ void ApplyGamma(SDL_Color *dst, const SDL_Color *src, int n) dst[i].g = static_cast(pow(src[i].g / 256.0, g) * 256.0); dst[i].b = static_cast(pow(src[i].b / 256.0, g) * 256.0); } - force_redraw = 255; + RedrawEverything(); } void palette_init() diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index 75baa4034..bc46c6f5e 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -11,6 +11,7 @@ #include "cursor.h" #include "dead.h" #include "doom.h" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "engine/render/clx_render.hpp" #include "engine/render/dun_render.hpp" @@ -91,6 +92,7 @@ extern void DrawControllerModifierHints(const Surface &out); bool frameflag; namespace { + /** * @brief Hash algorithm for point */ @@ -186,21 +188,6 @@ void UpdateMissilesRendererData() } } -uint32_t sgdwCursWdtOld; -int sgdwCursX; -int sgdwCursY; -/** - * Lower bound of back buffer. - */ -uint32_t sgdwCursHgt; - -int sgdwCursXOld; -int sgdwCursYOld; - -uint32_t sgdwCursWdt; -uint8_t sgSaveBack[8192]; -uint32_t sgdwCursHgtOld; - /** * @brief Keeps track of which tiles have been rendered already. */ @@ -223,10 +210,12 @@ const char *const PlayerModeNames[] = { "quitting" }; -void BlitCursor(uint8_t *dst, std::uint32_t dstPitch, uint8_t *src, std::uint32_t srcPitch) +Rectangle PrevCursorRect; + +void BlitCursor(uint8_t *dst, uint32_t dstPitch, uint8_t *src, uint32_t srcPitch, uint32_t srcWidth, uint32_t srcHeight) { - for (std::uint32_t i = 0; i < sgdwCursHgt; ++i, src += srcPitch, dst += dstPitch) { - memcpy(dst, src, sgdwCursWdt); + for (std::uint32_t i = 0; i < srcHeight; ++i, src += srcPitch, dst += dstPitch) { + memcpy(dst, src, srcWidth); } } @@ -235,17 +224,9 @@ void BlitCursor(uint8_t *dst, std::uint32_t dstPitch, uint8_t *src, std::uint32_ */ void UndrawCursor(const Surface &out) { - if (sgdwCursWdt == 0) { - return; - } - - BlitCursor(out.at(sgdwCursX, sgdwCursY), out.pitch(), sgSaveBack, sgdwCursWdt); - - sgdwCursXOld = sgdwCursX; - sgdwCursYOld = sgdwCursY; - sgdwCursWdtOld = sgdwCursWdt; - sgdwCursHgtOld = sgdwCursHgt; - sgdwCursWdt = 0; + DrawnCursor &cursor = GetDrawnCursor(); + BlitCursor(&out[cursor.rect.position], out.pitch(), cursor.behindBuffer, cursor.rect.size.width, cursor.rect.size.width, cursor.rect.size.height); + PrevCursorRect = cursor.rect; } bool ShouldShowCursor() @@ -267,23 +248,23 @@ bool ShouldShowCursor() */ void DrawCursor(const Surface &out) { + DrawnCursor &cursor = GetDrawnCursor(); if (pcurs <= CURSOR_NONE || !ShouldShowCursor()) { + cursor.rect.size.width = 0; return; } Size cursSize = GetInvItemSize(pcurs); if (cursSize.width == 0 || cursSize.height == 0) { + cursor.rect.size.width = 0; return; } - // Copy the buffer before the item cursor and its 1px outline are drawn to a temporary buffer. - const int outlineWidth = !MyPlayer->HoldItem.isEmpty() ? 1 : 0; - - if (MousePosition.x < -cursSize.width - outlineWidth || MousePosition.x - outlineWidth >= out.w() || MousePosition.y < -cursSize.height - outlineWidth || MousePosition.y - outlineWidth >= out.h()) - return; - - constexpr auto Clip = [](int &pos, std::uint32_t &length, std::uint32_t posEnd) { - if (pos < 0) { + constexpr auto Clip = [](int &pos, int &length, int posEnd) { + if (pos + length <= 0 || pos >= posEnd) { + pos = 0; + length = 0; + } else if (pos < 0) { length += pos; pos = 0; } else if (pos + length > posEnd) { @@ -291,15 +272,22 @@ void DrawCursor(const Surface &out) } }; - sgdwCursX = MousePosition.x - outlineWidth; - sgdwCursWdt = cursSize.width + 2 * outlineWidth; - Clip(sgdwCursX, sgdwCursWdt, out.w()); + // Copy the buffer before the item cursor and its 1px outline are drawn to a temporary buffer. + const int outlineWidth = !MyPlayer->HoldItem.isEmpty() ? 1 : 0; + + Rectangle &rect = cursor.rect; + rect.position.x = MousePosition.x - outlineWidth; + rect.size.width = cursSize.width + 2 * outlineWidth; + Clip(rect.position.x, rect.size.width, out.w()); - sgdwCursY = MousePosition.y - outlineWidth; - sgdwCursHgt = cursSize.height + 2 * outlineWidth; - Clip(sgdwCursY, sgdwCursHgt, out.h()); + rect.position.y = MousePosition.y - outlineWidth; + rect.size.height = cursSize.height + 2 * outlineWidth; + Clip(rect.position.y, rect.size.height, out.h()); - BlitCursor(sgSaveBack, sgdwCursWdt, out.at(sgdwCursX, sgdwCursY), out.pitch()); + if (rect.size.width == 0 || rect.size.height == 0) + return; + + BlitCursor(cursor.behindBuffer, rect.size.width, &out[rect.position], out.pitch(), rect.size.width, rect.size.height); DrawSoftwareCursor(out, MousePosition + Displacement { 0, cursSize.height - 1 }, pcurs); } @@ -1058,7 +1046,7 @@ void DrawView(const Surface &out, Point startPosition) bool debugGridTextNeeded = IsDebugGridTextNeeded(); if (debugGridTextNeeded || DebugGrid) { // force redrawing or debug stuff stays on panel on 640x480 resolution - force_redraw = 255; + RedrawEverything(); char debugGridTextBuffer[10]; bool megaTiles = IsDebugGridInMegatiles(); @@ -1225,7 +1213,8 @@ void DoBlitScreen(Sint16 dwX, Sint16 dwY, Uint16 dwWdt, Uint16 dwHgt) } /** - * @brief Check render pipeline and blit individual screen parts + * @brief Check render pipeline and update individual screen parts + * @param out Output surface. * @param dwHgt Section of screen to update from top to bottom * @param drawDesc Render info box * @param drawHp Render health bar @@ -1233,7 +1222,7 @@ void DoBlitScreen(Sint16 dwX, Sint16 dwY, Uint16 dwWdt, Uint16 dwHgt) * @param drawSbar Render belt * @param drawBtn Render panel buttons */ -void DrawMain(int dwHgt, bool drawDesc, bool drawHp, bool drawMana, bool drawSbar, bool drawBtn) +void DrawMain(const Surface &out, int dwHgt, bool drawDesc, bool drawHp, bool drawMana, bool drawSbar, bool drawBtn) { if (!gbActive || RenderDirectlyToOutputSurface) { return; @@ -1267,11 +1256,12 @@ void DrawMain(int dwHgt, bool drawDesc, bool drawHp, bool drawMana, bool drawSba DoBlitScreen(mainPanelPosition.x + 524, mainPanelPosition.y + 91, 36, 32); } } - if (sgdwCursWdtOld != 0) { - DoBlitScreen(sgdwCursXOld, sgdwCursYOld, sgdwCursWdtOld, sgdwCursHgtOld); + if (PrevCursorRect.size.width != 0 && PrevCursorRect.size.height != 0) { + DoBlitScreen(PrevCursorRect.position.x, PrevCursorRect.position.y, PrevCursorRect.size.width, PrevCursorRect.size.height); } - if (sgdwCursWdt != 0) { - DoBlitScreen(sgdwCursX, sgdwCursY, sgdwCursWdt, sgdwCursHgt); + Rectangle &cursorRect = GetDrawnCursor().rect; + if (cursorRect.size.width != 0 && cursorRect.size.height != 0) { + DoBlitScreen(cursorRect.position.x, cursorRect.position.y, cursorRect.size.width, cursorRect.size.height); } } } @@ -1301,8 +1291,7 @@ Displacement GetOffsetForWalking(const AnimationInfo &animationInfo, const Direc void ClearCursor() // CODE_FIX: this was supposed to be in cursor.cpp { - sgdwCursWdt = 0; - sgdwCursWdtOld = 0; + PrevCursorRect = {}; } void ShiftGrid(int *x, int *y, int horizontal, int vertical) @@ -1510,24 +1499,23 @@ void scrollrt_draw_game_screen() int hgt = 0; - if (force_redraw == 255) { - force_redraw = 0; + if (IsRedrawEverything()) { + RedrawComplete(); hgt = gnScreenHeight; } + const Surface &out = GlobalBackBuffer(); + UndrawCursor(out); + + DrawMain(out, hgt, false, false, false, false, false); + if (IsHardwareCursor()) { SetHardwareCursorVisible(ShouldShowCursor()); } else { - DrawCursor(GlobalBackBuffer()); + DrawCursor(out); } - DrawMain(hgt, false, false, false, false, false); - RenderPresent(); - - if (!IsHardwareCursor()) { - UndrawCursor(GlobalBackBuffer()); - } } void DrawAndBlit() @@ -1537,53 +1525,55 @@ void DrawAndBlit() } int hgt = 0; - bool ddsdesc = false; - bool ctrlPan = false; + bool drawHealth = IsRedrawComponent(PanelDrawComponent::Health); + bool drawMana = IsRedrawComponent(PanelDrawComponent::Mana); + bool drawControlButtons = IsRedrawComponent(PanelDrawComponent::ControlButtons); + bool drawBelt = IsRedrawComponent(PanelDrawComponent::Belt); + bool drawChatInput = IsRedrawComponent(PanelDrawComponent::ChatInput); + bool drawInfoBox = false; + bool drawCtrlPan = false; const Rectangle &mainPanel = GetMainPanel(); - if (gnScreenWidth > mainPanel.size.width || force_redraw == 255 || IsHighlightingLabelsEnabled()) { - drawhpflag = true; - drawmanaflag = true; - drawbtnflag = true; - drawsbarflag = true; - ddsdesc = false; - ctrlPan = true; + if (gnScreenWidth > mainPanel.size.width || IsRedrawEverything() || IsHighlightingLabelsEnabled()) { + drawHealth = true; + drawMana = true; + drawControlButtons = true; + drawBelt = true; + drawInfoBox = false; + drawCtrlPan = true; hgt = gnScreenHeight; - } else if (force_redraw == 1) { - ddsdesc = true; - ctrlPan = false; + } else if (IsRedrawViewport()) { + drawInfoBox = true; + drawCtrlPan = false; hgt = gnViewportHeight; } - force_redraw = 0; - const Surface &out = GlobalBackBuffer(); UndrawCursor(out); nthread_UpdateProgressToNextGameTick(); DrawView(out, ViewPosition); - if (ctrlPan) { + if (drawCtrlPan) { DrawCtrlPan(out); } - if (drawhpflag) { + if (drawHealth) { DrawLifeFlaskLower(out); } - if (drawmanaflag) { + if (drawMana) { DrawManaFlaskLower(out); DrawSpell(out); } - if (drawbtnflag) { + if (drawControlButtons) { DrawCtrlBtns(out); } - if (drawsbarflag) { + if (drawBelt) { DrawInvBelt(out); } - if (talkflag) { + if (drawChatInput) { DrawTalkPan(out); - hgt = gnScreenHeight; } DrawXPBar(out); if (*sgOptions.Graphics.showHealthValues) @@ -1599,14 +1589,16 @@ void DrawAndBlit() DrawFPS(out); - DrawMain(hgt, ddsdesc, drawhpflag, drawmanaflag, drawsbarflag, drawbtnflag); + DrawMain(out, hgt, drawInfoBox, drawHealth, drawMana, drawBelt, drawControlButtons); - RenderPresent(); + RedrawComplete(); + for (PanelDrawComponent component : enum_values()) { + if (IsRedrawComponent(component)) { + RedrawComponentComplete(component); + } + } - drawhpflag = false; - drawmanaflag = false; - drawbtnflag = false; - drawsbarflag = false; + RenderPresent(); } } // namespace devilution diff --git a/Source/gamemenu.cpp b/Source/gamemenu.cpp index f051e0ebd..acd6db70c 100644 --- a/Source/gamemenu.cpp +++ b/Source/gamemenu.cpp @@ -6,6 +6,7 @@ #include "gamemenu.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/sound.h" #include "engine/sound_defs.hpp" #include "error.h" @@ -102,7 +103,7 @@ void GamemenuNewGame(bool /*bActivate*/) MyPlayerIsDead = false; if (!HeadlessMode) { - force_redraw = 255; + RedrawEverything(); scrollrt_draw_game_screen(); } CornerStone.activated = false; @@ -293,14 +294,14 @@ void gamemenu_load_game(bool /*bActivate*/) gamemenu_off(); NewCursor(CURSOR_NONE); InitDiabloMsg(EMSG_LOADING); - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); LoadGame(false); ClrDiabloMsg(); CornerStone.activated = false; PaletteFadeOut(8); MyPlayerIsDead = false; - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); LoadPWaterPalette(); PaletteFadeIn(8); @@ -324,11 +325,11 @@ void gamemenu_save_game(bool /*bActivate*/) NewCursor(CURSOR_NONE); gamemenu_off(); InitDiabloMsg(EMSG_SAVING); - force_redraw = 255; + RedrawEverything(); DrawAndBlit(); SaveGame(); ClrDiabloMsg(); - force_redraw = 255; + RedrawEverything(); NewCursor(CURSOR_HAND); if (CornerStone.activated) { CornerstoneSave(); diff --git a/Source/init.cpp b/Source/init.cpp index 3e85bbd80..eed425fb2 100644 --- a/Source/init.cpp +++ b/Source/init.cpp @@ -14,6 +14,7 @@ #include "DiabloUI/diabloui.h" #include "engine/assets.hpp" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "hwcursor.hpp" #include "miniwin/misc_msg.h" @@ -332,10 +333,10 @@ void MainWndProc(const SDL_Event &event) break; case SDL_WINDOWEVENT_SHOWN: gbActive = false; - force_redraw = 255; + RedrawEverything(); break; case SDL_WINDOWEVENT_EXPOSED: - force_redraw = 255; + RedrawEverything(); break; case SDL_WINDOWEVENT_SIZE_CHANGED: ReinitializeHardwareCursor(); @@ -343,7 +344,7 @@ void MainWndProc(const SDL_Event &event) case SDL_WINDOWEVENT_LEAVE: sgbMouseDown = CLICK_NONE; LastMouseButtonAction = MouseActionType::None; - force_redraw = 255; + RedrawEverything(); break; case SDL_WINDOWEVENT_CLOSE: diablo_quit(0); diff --git a/Source/interfac.cpp b/Source/interfac.cpp index ff003720e..d5203e396 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -293,13 +293,17 @@ void ShowProgress(interface_mode uMsg) // Blit the background once and then free it. LoadCutsceneBackground(uMsg); DrawCutsceneBackground(); - if (RenderDirectlyToOutputSurface && IsDoubleBuffered()) { - // Blit twice for triple buffering. - for (unsigned i = 0; i < 2; ++i) { + if (RenderDirectlyToOutputSurface && PalSurface != nullptr) { + // Render into all the backbuffers if there are multiple. + const void *initialPixels = PalSurface->pixels; + if (DiabloUiSurface() == PalSurface) + BltFast(nullptr, nullptr); + RenderPresent(); + while (PalSurface->pixels != initialPixels) { + DrawCutsceneBackground(); if (DiabloUiSurface() == PalSurface) BltFast(nullptr, nullptr); RenderPresent(); - DrawCutsceneBackground(); } } FreeCutsceneBackground(); diff --git a/Source/inv.cpp b/Source/inv.cpp index a97cea96e..f6f0f92ce 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -11,6 +11,7 @@ #include "DiabloUI/ui_flags.hpp" #include "controls/plrctrls.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/render/clx_render.hpp" @@ -37,7 +38,6 @@ namespace devilution { bool invflag; -bool drawsbarflag; /** * Maps from inventory slot to screen position. The inventory slots are @@ -566,7 +566,7 @@ void CheckInvPaste(Player &player, Point cursorPosition) if (&player == MyPlayer) { NetSendCmdChBeltItem(false, ii); } - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); } break; case ILOC_NONE: case ILOC_INVALID: @@ -1235,7 +1235,7 @@ bool AutoPlaceItemInBelt(Player &player, const Item &item, bool persistItem) if (persistItem) { beltItem = item; player.CalcScrolls(); - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); if (&player == MyPlayer) { size_t beltIndex = std::distance(&player.SpdList[0], &beltItem); NetSendCmdChBeltItem(false, beltIndex); @@ -1907,7 +1907,7 @@ int8_t CheckInvHLight() pi = &myPlayer.InvList[ii]; } else if (r >= SLOTXY_BELT_FIRST) { r -= SLOTXY_BELT_FIRST; - drawsbarflag = true; + RedrawComponent(PanelDrawComponent::Belt); pi = &myPlayer.SpdList[r]; if (pi->isEmpty()) return -1; diff --git a/Source/inv.h b/Source/inv.h index e18501bae..ded2b67dd 100644 --- a/Source/inv.h +++ b/Source/inv.h @@ -83,7 +83,6 @@ enum item_color : uint8_t { }; extern bool invflag; -extern bool drawsbarflag; extern const Point InvRect[73]; void InvDrawSlotBack(const Surface &out, Point targetPosition, Size size, item_quality itemQuality); diff --git a/Source/items.cpp b/Source/items.cpp index 2e6b7e918..7ca1fcb0a 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -20,6 +20,7 @@ #include "controls/plrctrls.h" #include "cursor.h" #include "doom.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/dx.h" #include "engine/load_cel.hpp" @@ -823,11 +824,11 @@ int SaveItemPower(const Player &player, Item &item, ItemPower &power) break; case IPL_MANA: item._iPLMana += r << 6; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_MANA_CURSE: item._iPLMana -= r << 6; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_DUR: { int bonus = r * item._iMaxDur / 100; @@ -883,7 +884,7 @@ int SaveItemPower(const Player &player, Item &item, ItemPower &power) break; case IPL_NOMANA: item._iFlags |= ItemSpecialEffect::NoMana; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_ABSHALFTRAP: item._iFlags |= ItemSpecialEffect::HalfTrapDamage; @@ -902,14 +903,14 @@ int SaveItemPower(const Player &player, Item &item, ItemPower &power) item._iFlags |= ItemSpecialEffect::StealMana3; if (power.param1 == 5) item._iFlags |= ItemSpecialEffect::StealMana5; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IPL_STEALLIFE: if (power.param1 == 3) item._iFlags |= ItemSpecialEffect::StealLife3; if (power.param1 == 5) item._iFlags |= ItemSpecialEffect::StealLife5; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); break; case IPL_TARGAC: if (gbIsHellfire) @@ -2719,8 +2720,8 @@ void CalcPlrItemVals(Player &player, bool loadgfx) } } - drawmanaflag = true; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Mana); + RedrawComponent(PanelDrawComponent::Health); } void CalcPlrInv(Player &player, bool loadgfx) @@ -3813,25 +3814,25 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) case IMISC_HEAL: player.RestorePartialLife(); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } break; case IMISC_FULLHEAL: player.RestoreFullLife(); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } break; case IMISC_MANA: player.RestorePartialMana(); if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } break; case IMISC_FULLMANA: player.RestoreFullMana(); if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } break; case IMISC_ELIXSTR: @@ -3842,7 +3843,7 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) if (gbIsHellfire) { player.RestoreFullMana(); if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } } break; @@ -3854,7 +3855,7 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) if (gbIsHellfire) { player.RestoreFullLife(); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } } break; @@ -3862,16 +3863,16 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) player.RestorePartialLife(); player.RestorePartialMana(); if (&player == MyPlayer) { - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); } } break; case IMISC_FULLREJUV: player.RestoreFullLife(); player.RestoreFullMana(); if (&player == MyPlayer) { - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); } break; case IMISC_SCROLL: @@ -3910,7 +3911,7 @@ void UseItem(size_t pnum, item_misc_id mid, spell_id spl) Stash.RefreshItemStatFlags(); } } - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; case IMISC_MAPOFDOOM: doom_init(); diff --git a/Source/missiles.cpp b/Source/missiles.cpp index a7f7a32da..57ebfcdf1 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -14,6 +14,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/load_file.hpp" #include "engine/points_in_rectangle_range.hpp" #include "engine/random.hpp" @@ -1323,7 +1324,7 @@ void AddStealPotions(Missile &missile, AddMissileParameter & /*parameter*/) hasPlayedSFX = true; } } - force_redraw = 255; + RedrawEverything(); return false; }); @@ -1344,7 +1345,7 @@ void AddManaTrap(Missile &missile, AddMissileParameter & /*parameter*/) player._pMana = 0; player._pManaBase = player._pMana + player._pMaxManaBase - player._pMaxMana; CalcPlrInv(player, false); - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); PlaySfxLoc(TSFX_COW7, *trappedPlayerPosition); } @@ -1535,7 +1536,7 @@ void AddMana(Missile &missile, AddMissileParameter & /*parameter*/) if (player._pManaBase > player._pMaxManaBase) player._pManaBase = player._pMaxManaBase; missile._miDelFlag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } void AddMagi(Missile &missile, AddMissileParameter & /*parameter*/) @@ -1545,7 +1546,7 @@ void AddMagi(Missile &missile, AddMissileParameter & /*parameter*/) player._pMana = player._pMaxMana; player._pManaBase = player._pMaxManaBase; missile._miDelFlag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } void AddRing(Missile &missile, AddMissileParameter & /*parameter*/) @@ -2264,7 +2265,7 @@ void AddHeal(Missile &missile, AddMissileParameter & /*parameter*/) player._pHPBase = std::min(player._pHPBase + hp, player._pMaxHPBase); missile._miDelFlag = true; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } void AddHealOther(Missile &missile, AddMissileParameter & /*parameter*/) @@ -2394,7 +2395,7 @@ void AddBlodboil(Missile &missile, AddMissileParameter ¶meter) int lvl = player._pLevel * 2; missile._mirange = lvl + 10 * missile._mispllvl + 245; CalcPlrItemVals(player, true); - force_redraw = 255; + RedrawEverything(); player.Say(HeroSpeech::Aaaaargh); } @@ -3788,7 +3789,7 @@ void MI_Blodboil(Missile &missile) CalcPlrItemVals(player, true); ApplyPlrDamage(player, 0, 1, hpdif); - force_redraw = 255; + RedrawEverything(); player.Say(HeroSpeech::HeavyBreathing); } diff --git a/Source/movie.cpp b/Source/movie.cpp index 6a4dbe2a1..2f1700deb 100644 --- a/Source/movie.cpp +++ b/Source/movie.cpp @@ -7,6 +7,7 @@ #include "controls/plrctrls.h" #include "diablo.h" #include "effects.h" +#include "engine/backbuffer_state.hpp" #include "engine/demomode.h" #include "engine/sound.h" #include "hwcursor.hpp" @@ -74,10 +75,10 @@ void PlayInGameMovie(const char *pszMovie) PaletteFadeOut(8); play_movie(pszMovie, false); ClearScreenBuffer(); - force_redraw = 255; + RedrawEverything(); scrollrt_draw_game_screen(); PaletteFadeIn(8); - force_redraw = 255; + RedrawEverything(); } } // namespace devilution diff --git a/Source/msg.cpp b/Source/msg.cpp index 5b948a985..771a7555a 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -16,6 +16,7 @@ #include "control.h" #include "dead.h" #include "encrypt.h" +#include "engine/backbuffer_state.hpp" #include "engine/random.hpp" #include "engine/world_tile.hpp" #include "gamemenu.h" @@ -2235,7 +2236,7 @@ size_t OnString(const TCmd *pCmd, Player &player) size_t OnFriendlyMode(const TCmd *pCmd, Player &player) // NOLINT(misc-unused-parameters) { player.friendlyMode = !player.friendlyMode; - force_redraw = 255; + RedrawEverything(); return sizeof(*pCmd); } @@ -2262,7 +2263,7 @@ size_t OnCheatExperience(const TCmd *pCmd, size_t pnum) // NOLINT(misc-unused-pa else if (Players[pnum]._pLevel < MaxCharacterLevel) { Players[pnum]._pExperience = Players[pnum]._pNextExper; if (*sgOptions.Gameplay.experienceBar) { - force_redraw = 255; + RedrawEverything(); } NextPlrLevel(Players[pnum]); } diff --git a/Source/objects.cpp b/Source/objects.cpp index 2685e9766..813254503 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -17,6 +17,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/load_file.hpp" #include "engine/points_in_rectangle_range.hpp" @@ -2289,7 +2290,7 @@ void OperateShrineMysterious(Player &player) CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_MYSTERIOUS); } @@ -2432,7 +2433,7 @@ void OperateShrineStone(Player &player) item._iCharges = item._iMaxCharges; } - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_STONE); } @@ -2530,7 +2531,7 @@ void OperateShrineCostOfWisdom(Player &player, spell_id spellId, diablo_message player._pMaxManaBase = 0; } - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(message); } @@ -2555,7 +2556,7 @@ void OperateShrineCryptic(Player &player) InitDiabloMsg(EMSG_SHRINE_CRYPTIC); - force_redraw = 255; + RedrawEverything(); } void OperateShrineEldritch(Player &player) @@ -2586,7 +2587,7 @@ void OperateShrineEldritch(Player &player) } } - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_ELDRITCH); } @@ -2599,7 +2600,7 @@ void OperateShrineEerie(Player &player) ModifyPlrMag(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_EERIE); } @@ -2628,7 +2629,7 @@ void OperateShrineDivine(Player &player, Point spawnPosition) player._pHitPoints = player._pMaxHP; player._pHPBase = player._pMaxHPBase; - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_DIVINE); } @@ -2676,7 +2677,7 @@ void OperateShrineSpooky(const Player &player) myPlayer._pMana = myPlayer._pMaxMana; myPlayer._pManaBase = myPlayer._pMaxManaBase; - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_SPOOKY2); } @@ -2689,7 +2690,7 @@ void OperateShrineAbandoned(Player &player) ModifyPlrDex(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_ABANDONED); } @@ -2702,7 +2703,7 @@ void OperateShrineCreepy(Player &player) ModifyPlrStr(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_CREEPY); } @@ -2715,7 +2716,7 @@ void OperateShrineQuiet(Player &player) ModifyPlrVit(player, 2); CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_QUIET); } @@ -2744,7 +2745,7 @@ void OperateShrineGlimmering(Player &player) } CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_GLIMMERING); } @@ -2772,7 +2773,7 @@ void OperateShrineTainted(const Player &player) CheckStats(myPlayer); CalcPlrInv(myPlayer, true); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_TAINTED2); } @@ -2813,7 +2814,7 @@ void OperateShrineOily(Player &player, Point spawnPosition) CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); AddMissile( spawnPosition, @@ -2843,7 +2844,7 @@ void OperateShrineGlowing(Player &player) player._pExperience = 0; CheckStats(player); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_GLOWING); } @@ -2857,7 +2858,7 @@ void OperateShrineMendicant(Player &player) AddPlrExperience(player, player._pLevel, gold); TakePlrsMoney(gold); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_MENDICANT); } @@ -2884,7 +2885,7 @@ void OperateShrineSparkling(Player &player, Point spawnPosition) 3 * currlevel + 2, 0); - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_SPARKLING); } @@ -2920,7 +2921,7 @@ void OperateShrineShimmering(Player &player) player._pMana = player._pMaxMana; player._pManaBase = player._pMaxManaBase; - force_redraw = 255; + RedrawEverything(); InitDiabloMsg(EMSG_SHRINE_SHIMMERING); } @@ -2949,7 +2950,7 @@ void OperateShrineSolar(Player &player) CheckStats(player); CalcPlrInv(player, true); - force_redraw = 255; + RedrawEverything(); } void OperateShrineMurphys(Player &player) @@ -3200,7 +3201,7 @@ void OperateGoatShrine(Player &player, Object &object, _sfx_id sType) object._oVar1 = FindValidShrine(); OperateShrine(player, object, sType); object._oAnimDelay = 2; - force_redraw = 255; + RedrawEverything(); } void OperateCauldron(Player &player, Object &object, _sfx_id sType) @@ -3210,7 +3211,7 @@ void OperateCauldron(Player &player, Object &object, _sfx_id sType) OperateShrine(player, object, sType); object._oAnimFrame = 3; object._oAnimFlag = false; - force_redraw = 255; + RedrawEverything(); } bool OperateFountains(Player &player, Object &fountain) @@ -3309,7 +3310,7 @@ bool OperateFountains(Player &player, Object &fountain) default: break; } - force_redraw = 255; + RedrawEverything(); return applied; } diff --git a/Source/panels/spell_book.cpp b/Source/panels/spell_book.cpp index 21510309b..99fcdabea 100644 --- a/Source/panels/spell_book.cpp +++ b/Source/panels/spell_book.cpp @@ -3,6 +3,7 @@ #include #include "control.h" +#include "engine/backbuffer_state.hpp" #include "engine/clx_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/rectangle.hpp" @@ -202,7 +203,7 @@ void CheckSBook() } player._pRSpell = sn; player._pRSplType = st; - force_redraw = 255; + RedrawEverything(); } return; } diff --git a/Source/panels/spell_list.cpp b/Source/panels/spell_list.cpp index 4a3bd68f4..ff04a4fbf 100644 --- a/Source/panels/spell_list.cpp +++ b/Source/panels/spell_list.cpp @@ -4,6 +4,7 @@ #include "control.h" #include "engine.h" +#include "engine/backbuffer_state.hpp" #include "engine/palette.h" #include "engine/render/text_render.hpp" #include "inv_iterators.hpp" @@ -280,7 +281,7 @@ void SetSpell() myPlayer._pRSpell = pSpell; myPlayer._pRSplType = pSplType; - force_redraw = 255; + RedrawEverything(); } void SetSpeedSpell(size_t slot) @@ -331,7 +332,7 @@ void ToggleSpell(size_t slot) if ((spells & GetSpellBitmask(spellId)) != 0) { myPlayer._pRSpell = spellId; myPlayer._pRSplType = myPlayer._pSplTHotKey[slot]; - force_redraw = 255; + RedrawEverything(); } } diff --git a/Source/player.cpp b/Source/player.cpp index f01615229..c6498a2c7 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -16,6 +16,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/load_cl2.hpp" #include "engine/load_file.hpp" #include "engine/points_in_rectangle_range.hpp" @@ -841,7 +842,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) if (player._pHPBase > player._pMaxHPBase) { player._pHPBase = player._pMaxHPBase; } - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealMana3 | ItemSpecialEffect::StealMana5) && HasNoneOf(player._pIFlags, ItemSpecialEffect::NoMana)) { if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealMana3)) { @@ -858,7 +859,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) if (player._pManaBase > player._pMaxManaBase) { player._pManaBase = player._pMaxManaBase; } - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealLife3 | ItemSpecialEffect::StealLife5)) { if (HasAnyOf(player._pIFlags, ItemSpecialEffect::StealLife3)) { @@ -875,7 +876,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) if (player._pHPBase > player._pMaxHPBase) { player._pHPBase = player._pMaxHPBase; } - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } #ifdef _DEBUG if (DebugGodMode) { @@ -947,7 +948,7 @@ bool PlrHitPlr(Player &attacker, Player &target) if (attacker._pHPBase > attacker._pMaxHPBase) { attacker._pHPBase = attacker._pMaxHPBase; } - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } if (&attacker == MyPlayer) { NetSendCmdDamage(true, target.getId(), skdam); @@ -1630,7 +1631,7 @@ void ValidatePlayer() if (myPlayer._pExperience > myPlayer._pNextExper) { myPlayer._pExperience = myPlayer._pNextExper; if (*sgOptions.Gameplay.experienceBar) { - force_redraw = 255; + RedrawEverything(); } } @@ -1828,7 +1829,7 @@ void Player::RemoveSpdBarItem(int iv) SpdList[iv].clear(); CalcScrolls(); - force_redraw = 255; + RedrawEverything(); } [[nodiscard]] size_t Player::getId() const @@ -1974,7 +1975,7 @@ void Player::ReadySpellFromEquipment(inv_body_loc bodyLocation) if (item._itype == ItemType::Staff && IsValidSpell(item._iSpell) && item._iCharges > 0) { _pRSpell = item._iSpell; _pRSplType = RSPLTYPE_CHARGES; - force_redraw = 255; + RedrawEverything(); } } @@ -2629,7 +2630,7 @@ void NextPlrLevel(Player &player) player._pHPBase = player._pMaxHPBase; if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } int mana = 128; @@ -2647,7 +2648,7 @@ void NextPlrLevel(Player &player) } if (&player == MyPlayer) { - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } if (ControlMode != ControlTypes::KeyboardAndMouse) @@ -2684,7 +2685,7 @@ void AddPlrExperience(Player &player, int lvl, int exp) player._pExperience = std::min(player._pExperience + clampedExp, MaxExperience); if (*sgOptions.Gameplay.experienceBar) { - force_redraw = 255; + RedrawEverything(); } if (player._pExperience >= ExpLvlsTbl[49]) { @@ -2904,7 +2905,7 @@ void StartPlrHit(Player &player, int dam, bool forcehit) player.Say(HeroSpeech::ArghClang); - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); if (player._pClass == HeroClass::Barbarian) { if (dam >> 6 < player._pLevel + player._pLevel / 4 && !forcehit) { return; @@ -2986,7 +2987,7 @@ StartPlayerKill(Player &player, int earflag) SetPlayerOld(player); if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); if (!player.HoldItem.isEmpty()) { DeadItem(player, std::move(player.HoldItem), { 0, 0 }); @@ -3066,7 +3067,7 @@ void ApplyPlrDamage(Player &player, int dam, int minHP /*= 0*/, int frac /*= 0*/ totalDamage += totalDamage / -player.GetManaShieldDamageReduction(); } if (&player == MyPlayer) - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); if (player._pMana >= totalDamage) { player._pMana -= totalDamage; player._pManaBase -= totalDamage; @@ -3086,7 +3087,7 @@ void ApplyPlrDamage(Player &player, int dam, int minHP /*= 0*/, int frac /*= 0*/ if (totalDamage == 0) return; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); player._pHitPoints -= totalDamage; player._pHPBase -= totalDamage; if (player._pHitPoints > player._pMaxHP) { @@ -3274,7 +3275,7 @@ void ProcessPlayers() if (HasAnyOf(player._pIFlags, ItemSpecialEffect::NoMana) && player._pManaBase > 0) { player._pManaBase -= player._pMana; player._pMana = 0; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } } @@ -3633,7 +3634,7 @@ void SetPlayerHitPoints(Player &player, int val) player._pHPBase = val + player._pMaxHPBase - player._pMaxHP; if (&player == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } } diff --git a/Source/spells.cpp b/Source/spells.cpp index c0c623a0d..a02d75ad9 100644 --- a/Source/spells.cpp +++ b/Source/spells.cpp @@ -10,6 +10,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/backbuffer_state.hpp" #include "engine/point.hpp" #include "engine/random.hpp" #include "gamemenu.h" @@ -55,12 +56,12 @@ void ClearReadiedSpell(Player &player) { if (player._pRSpell != SPL_INVALID) { player._pRSpell = SPL_INVALID; - force_redraw = 255; + RedrawEverything(); } if (player._pRSplType != RSPLTYPE_INVALID) { player._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; + RedrawEverything(); } } @@ -191,7 +192,7 @@ void ConsumeSpell(Player &player, spell_id sn) int ma = GetManaAmount(player, sn); player._pMana -= ma; player._pManaBase -= ma; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); break; } if (sn == SPL_FLARE) { @@ -273,8 +274,8 @@ void DoResurrect(size_t pnum, Player &target) if (&target == MyPlayer) { MyPlayerIsDead = false; gamemenu_off(); - drawhpflag = true; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Health); + RedrawComponent(PanelDrawComponent::Mana); } ClrPlrPath(target); @@ -327,7 +328,7 @@ void DoHealOther(const Player &caster, Player &target) target._pHPBase = std::min(target._pHPBase + hp, target._pMaxHPBase); if (&target == MyPlayer) { - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } } diff --git a/Source/stores.cpp b/Source/stores.cpp index 4fc7619a8..729ae2f5d 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -11,6 +11,7 @@ #include "controls/plrctrls.h" #include "cursor.h" +#include "engine/backbuffer_state.hpp" #include "engine/load_cel.hpp" #include "engine/random.hpp" #include "engine/render/clx_render.hpp" @@ -695,7 +696,7 @@ void FillManaPlayer() } myPlayer._pMana = myPlayer._pMaxMana; myPlayer._pManaBase = myPlayer._pMaxManaBase; - drawmanaflag = true; + RedrawComponent(PanelDrawComponent::Mana); } void StartWitch() @@ -1090,7 +1091,7 @@ void HealPlayer() } myPlayer._pHitPoints = myPlayer._pMaxHP; myPlayer._pHPBase = myPlayer._pMaxHPBase; - drawhpflag = true; + RedrawComponent(PanelDrawComponent::Health); } void StartHealer() diff --git a/Source/utils/display.cpp b/Source/utils/display.cpp index 23a06aaac..31fc6abe5 100644 --- a/Source/utils/display.cpp +++ b/Source/utils/display.cpp @@ -24,6 +24,7 @@ #include "controls/devices/kbcontroller.h" #include "controls/game_controls.h" #include "controls/touch/gamepad.h" +#include "engine/backbuffer_state.hpp" #include "engine/dx.h" #include "options.h" #include "utils/log.hpp" @@ -449,7 +450,7 @@ void SetFullscreenMode() } InitializeVirtualGamepad(); #endif - force_redraw = 255; + RedrawEverything(); } void ResizeWindow() @@ -473,7 +474,7 @@ void ResizeWindow() #endif CreateBackBuffer(); - force_redraw = 255; + RedrawEverything(); } SDL_Surface *GetOutputSurface() @@ -493,15 +494,6 @@ SDL_Surface *GetOutputSurface() #endif } -bool IsDoubleBuffered() -{ -#ifdef USE_SDL1 - return (GetOutputSurface()->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF; -#else - return true; -#endif -} - bool OutputRequiresScaling() { #ifdef USE_SDL1 diff --git a/Source/utils/display.h b/Source/utils/display.h index eb8b52acb..857cdb1ac 100644 --- a/Source/utils/display.h +++ b/Source/utils/display.h @@ -40,8 +40,6 @@ bool IsFullScreen(); // SDL2, upscale: Renderer texture surface. SDL_Surface *GetOutputSurface(); -bool IsDoubleBuffered(); - // Whether the output surface requires software scaling. // Always returns false on SDL2. bool OutputRequiresScaling();