From 46e755f680280eaae09c641b227f00ccf0eacc02 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Tue, 22 Nov 2022 01:15:13 +0000 Subject: [PATCH] Simplify spell icon handling Hide the internals of spell icon rendering behind simple functions. --- Source/control.cpp | 4 +- Source/controls/modifier_hints.cpp | 6 +- Source/controls/plrctrls.cpp | 3 +- Source/engine.cpp | 19 ++++++ Source/engine.h | 9 +++ Source/panels/spell_book.cpp | 33 ++-------- Source/panels/spell_book.hpp | 7 --- Source/panels/spell_icons.cpp | 99 ++++++++++++++++-------------- Source/panels/spell_icons.hpp | 51 ++++++++++----- Source/panels/spell_list.cpp | 24 +------- 10 files changed, 127 insertions(+), 128 deletions(-) diff --git a/Source/control.cpp b/Source/control.cpp index 11dff1089..faca05ff5 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -646,7 +646,7 @@ void InitControlPan() pLifeBuff.emplace(88, 88); LoadCharPanel(); - LoadSpellIcons(); + LoadLargeSpellIcons(); { const OwnedClxSpriteList sprite = LoadCel("ctrlpan\\panel8", GetMainPanel().size.width); ClxDraw(*pBtmBuff, { 0, (GetMainPanel().size.height + 16) - 1 }, sprite[0]); @@ -962,7 +962,7 @@ void FreeControlPan() pBtmBuff = std::nullopt; pManaBuff = std::nullopt; pLifeBuff = std::nullopt; - FreeSpellIcons(); + FreeLargeSpellIcons(); FreeSpellBook(); pPanelButtons = std::nullopt; multiButtons = std::nullopt; diff --git a/Source/controls/modifier_hints.cpp b/Source/controls/modifier_hints.cpp index b1cec39a1..9ab4b495f 100644 --- a/Source/controls/modifier_hints.cpp +++ b/Source/controls/modifier_hints.cpp @@ -139,11 +139,7 @@ void DrawSpellsCircleMenuHint(const Surface &out, const Point &origin) } SetSpellTrans(splType); -#ifdef UNPACKED_MPQS - DrawSpellCel(out, spellIconPositions[slot], (*pSBkIconsForeground)[SpellITbl[splId]], (*pSBkIconsBackground)[0]); -#else - DrawSpellCel(out, spellIconPositions[slot], (*pSBkIconCels)[SpellITbl[splId]]); -#endif + DrawSmallSpellIcon(out, spellIconPositions[slot], splId); RenderClxSprite(out, (*hintBox)[0], hintBoxPositions[slot]); } } diff --git a/Source/controls/plrctrls.cpp b/Source/controls/plrctrls.cpp index f21e345ce..04720eb80 100644 --- a/Source/controls/plrctrls.cpp +++ b/Source/controls/plrctrls.cpp @@ -30,6 +30,7 @@ #include "levels/trigs.h" #include "minitext.h" #include "missiles.h" +#include "panels/spell_icons.hpp" #include "panels/spell_list.hpp" #include "panels/ui_panels.hpp" #include "qol/chatlog.h" @@ -40,8 +41,6 @@ #include "utils/log.hpp" #include "utils/str_cat.hpp" -#define SPLICONLENGTH 56 - namespace devilution { ControlTypes ControlMode = ControlTypes::None; diff --git a/Source/engine.cpp b/Source/engine.cpp index 5078ede99..d1429cf1e 100644 --- a/Source/engine.cpp +++ b/Source/engine.cpp @@ -163,6 +163,25 @@ void DrawHalfTransparentRectTo(const Surface &out, int sx, int sy, int width, in DrawHalfTransparentBlendedRectTo(out, sx, sy, width, height); } +void UnsafeDrawBorder2px(const Surface &out, Rectangle rect, uint8_t color) +{ + const size_t width = rect.size.width; + const size_t height = rect.size.height; + uint8_t *buf = &out[rect.position]; + std::memset(buf, color, width); + buf += out.pitch(); + std::memset(buf, color, width); + buf += out.pitch(); + for (size_t i = 4; i < height; ++i) { + buf[0] = buf[1] = color; + buf[width - 2] = buf[width - 1] = color; + buf += out.pitch(); + } + std::memset(buf, color, width); + buf += out.pitch(); + std::memset(buf, color, width); +} + Direction GetDirection(Point start, Point destination) { Direction md; diff --git a/Source/engine.h b/Source/engine.h index cf464bf57..df31bcbb7 100644 --- a/Source/engine.h +++ b/Source/engine.h @@ -104,6 +104,15 @@ void UnsafeDrawVerticalLine(const Surface &out, Point from, int height, std::uin */ void DrawHalfTransparentRectTo(const Surface &out, int sx, int sy, int width, int height); +/** + * Draws a 2px inset border. + * + * @param out Target buffer + * @param rect The rectangle that border pixels are rendered inside of. + * @param color Border color. + */ +void UnsafeDrawBorder2px(const Surface &out, Rectangle rect, uint8_t color); + /** * @brief Calculate the best fit direction between two points * @param start Tile coordinate diff --git a/Source/panels/spell_book.cpp b/Source/panels/spell_book.cpp index 5afd309ca..2205e87b3 100644 --- a/Source/panels/spell_book.cpp +++ b/Source/panels/spell_book.cpp @@ -21,13 +21,6 @@ namespace devilution { -#ifdef UNPACKED_MPQS -OptionalOwnedClxSpriteList pSBkIconsBackground; -OptionalOwnedClxSpriteList pSBkIconsForeground; -#else -OptionalOwnedClxSpriteList pSBkIconCels; -#endif - namespace { OptionalOwnedClxSpriteList pSBkBtnCel; @@ -88,12 +81,7 @@ void InitSpellBook() { pSpellBkCel = LoadCel("data\\spellbk", static_cast(SidePanelSize.width)); pSBkBtnCel = LoadCel("data\\spellbkb", gbIsHellfire ? 61 : 76); -#ifdef UNPACKED_MPQS - pSBkIconsBackground = LoadClx("data\\spelli2_bg.clx"); - pSBkIconsForeground = LoadClx("data\\spelli2_fg.clx"); -#else - pSBkIconCels = LoadCel("data\\spelli2", 37); -#endif + LoadSmallSpellIcons(); Player &player = *MyPlayer; if (player._pClass == HeroClass::Warrior) { @@ -113,12 +101,7 @@ void InitSpellBook() void FreeSpellBook() { -#ifdef UNPACKED_MPQS - pSBkIconsForeground = std::nullopt; - pSBkIconsBackground = std::nullopt; -#else - pSBkIconCels = std::nullopt; -#endif + FreeSmallSpellIcons(); pSBkBtnCel = std::nullopt; pSpellBkCel = std::nullopt; } @@ -149,18 +132,10 @@ void DrawSpellBook(const Surface &out) spell_type st = GetSBookTrans(sn, true); SetSpellTrans(st); const Point spellCellPosition = GetPanelPosition(UiPanels::Spell, { 11, yp + SpellBookDescription.height }); -#ifdef UNPACKED_MPQS - DrawSpellCel(out, spellCellPosition, (*pSBkIconsForeground)[SpellITbl[sn]], (*pSBkIconsBackground)[0]); -#else - DrawSpellCel(out, spellCellPosition, (*pSBkIconCels)[SpellITbl[sn]]); -#endif + DrawSmallSpellIcon(out, spellCellPosition, sn); if (sn == player._pRSpell && st == player._pRSplType) { SetSpellTrans(RSPLTYPE_SKILL); -#ifdef UNPACKED_MPQS - DrawSpellBorder(out, spellCellPosition, (*pSBkIconsForeground)[SpellITbl[sn]]); -#else - DrawSpellBorder(out, spellCellPosition, (*pSBkIconCels)[SpellITbl[sn]]); -#endif + DrawSmallSpellIconBorder(out, spellCellPosition); } const Point line0 { 0, yp + textPaddingTop }; diff --git a/Source/panels/spell_book.hpp b/Source/panels/spell_book.hpp index 119cfa4f7..d65c1a742 100644 --- a/Source/panels/spell_book.hpp +++ b/Source/panels/spell_book.hpp @@ -5,13 +5,6 @@ namespace devilution { -#ifdef UNPACKED_MPQS -extern OptionalOwnedClxSpriteList pSBkIconsBackground; -extern OptionalOwnedClxSpriteList pSBkIconsForeground; -#else -extern OptionalOwnedClxSpriteList pSBkIconCels; -#endif - void InitSpellBook(); void FreeSpellBook(); void CheckSBook(); diff --git a/Source/panels/spell_icons.cpp b/Source/panels/spell_icons.cpp index 63bf494be..a12937f3e 100644 --- a/Source/panels/spell_icons.cpp +++ b/Source/panels/spell_icons.cpp @@ -1,5 +1,6 @@ #include "panels/spell_icons.hpp" +#include "engine.h" #include "engine/load_cel.hpp" #include "engine/load_clx.hpp" #include "engine/palette.h" @@ -10,17 +11,19 @@ namespace devilution { namespace { + #ifdef UNPACKED_MPQS -OptionalOwnedClxSpriteList pSpellIconsBackground; -OptionalOwnedClxSpriteList pSpellIconsForeground; -#else -OptionalOwnedClxSpriteList pSpellCels; +OptionalOwnedClxSpriteList LargeSpellIconsBackground; +OptionalOwnedClxSpriteList SmallSpellIconsBackground; #endif +OptionalOwnedClxSpriteList SmallSpellIcons; +OptionalOwnedClxSpriteList LargeSpellIcons; + uint8_t SplTransTbl[256]; -} // namespace -const char SpellITbl[] = { +/** Maps from spell_id to spelicon.cel frame number. */ +const uint8_t SpellITbl[] = { 26, 0, 1, @@ -75,76 +78,82 @@ const char SpellITbl[] = { 34, }; -void LoadSpellIcons() +} // namespace + +void LoadLargeSpellIcons() { if (!gbIsHellfire) { #ifdef UNPACKED_MPQS - pSpellIconsForeground = LoadClx("ctrlpan\\spelicon_fg.clx"); - pSpellIconsBackground = LoadClx("ctrlpan\\spelicon_bg.clx"); + LargeSpellIcons = LoadClx("ctrlpan\\spelicon_fg.clx"); + LargeSpellIconsBackground = LoadClx("ctrlpan\\spelicon_bg.clx"); #else - pSpellCels = LoadCel("ctrlpan\\spelicon", SPLICONLENGTH); + LargeSpellIcons = LoadCel("ctrlpan\\spelicon", SPLICONLENGTH); #endif } else { #ifdef UNPACKED_MPQS - pSpellIconsForeground = LoadClx("data\\spelicon_fg.clx"); - pSpellIconsBackground = LoadClx("data\\spelicon_bg.clx"); + LargeSpellIcons = LoadClx("data\\spelicon_fg.clx"); + LargeSpellIconsBackground = LoadClx("data\\spelicon_bg.clx"); #else - pSpellCels = LoadCel("data\\spelicon", SPLICONLENGTH); + LargeSpellIcons = LoadCel("data\\spelicon", SPLICONLENGTH); #endif } SetSpellTrans(RSPLTYPE_SKILL); } -void FreeSpellIcons() +void FreeLargeSpellIcons() { #ifdef UNPACKED_MPQS - pSpellIconsBackground = std::nullopt; - pSpellIconsForeground = std::nullopt; -#else - pSpellCels = std::nullopt; + LargeSpellIconsBackground = std::nullopt; #endif + LargeSpellIcons = std::nullopt; } -void DrawSpellCel(const Surface &out, Point position, int nCel) +void LoadSmallSpellIcons() { #ifdef UNPACKED_MPQS - DrawSpellCel(out, position, (*pSpellIconsForeground)[nCel], (*pSpellIconsBackground)[0]); + SmallSpellIcons = LoadClx("data\\spelli2_fg.clx"); + SmallSpellIconsBackground = LoadClx("data\\spelli2_bg.clx"); #else - DrawSpellCel(out, position, (*pSpellCels)[nCel]); + SmallSpellIcons = LoadCel("data\\spelli2", 37); #endif } -#ifdef UNPACKED_MPQS -void DrawSpellCel(const Surface &out, Point position, ClxSprite sprite, ClxSprite background) +void FreeSmallSpellIcons() { - ClxDrawTRN(out, position, background, SplTransTbl); - ClxDrawTRN(out, position, sprite, SplTransTbl); +#ifdef UNPACKED_MPQS + SmallSpellIconsBackground = std::nullopt; +#endif + SmallSpellIcons = std::nullopt; } -#else -void DrawSpellCel(const Surface &out, Point position, ClxSprite sprite) + +void DrawLargeSpellIcon(const Surface &out, Point position, spell_id spell) { - ClxDrawTRN(out, position, sprite, SplTransTbl); +#ifdef UNPACKED_MPQS + ClxDrawTRN(out, position, (*LargeSpellIconsBackground)[0], SplTransTbl); +#endif + ClxDrawTRN(out, position, (*LargeSpellIcons)[SpellITbl[spell]], SplTransTbl); } + +void DrawSmallSpellIcon(const Surface &out, Point position, spell_id spell) +{ +#ifdef UNPACKED_MPQS + ClxDrawTRN(out, position, (*SmallSpellIconsBackground)[0], SplTransTbl); #endif + ClxDrawTRN(out, position, (*SmallSpellIcons)[SpellITbl[spell]], SplTransTbl); +} -void DrawSpellBorder(const Surface &out, Point position, ClxSprite sprite) +void DrawLargeSpellIconBorder(const Surface &out, Point position, uint8_t color) { - const uint8_t color = SplTransTbl[PAL8_YELLOW + 2]; - const size_t width = sprite.width(); - const size_t height = sprite.height(); - position.y -= static_cast(height); - uint8_t *buf = &out[position]; - std::memset(buf, color, width); - buf += out.pitch(); - std::memset(buf, color, width); - for (size_t i = 4; i < sprite.height(); ++i) { - buf[0] = buf[1] = color; - buf[width - 2] = buf[width - 1] = color; - buf += out.pitch(); - } - std::memset(buf, color, width); - buf += out.pitch(); - std::memset(buf, color, width); + const int width = (*LargeSpellIcons)[0].width(); + const int height = (*LargeSpellIcons)[0].height(); + UnsafeDrawBorder2px(out, Rectangle { Point { position.x, position.y - height + 1 }, Size { width, height } }, color); +} + +void DrawSmallSpellIconBorder(const Surface &out, Point position) +{ + const int width = (*SmallSpellIcons)[0].width(); + const int height = (*SmallSpellIcons)[0].height(); + UnsafeDrawBorder2px(out, Rectangle { Point { position.x, position.y - height + 1 }, Size { width, height } }, SplTransTbl[PAL8_YELLOW + 2]); } void SetSpellTrans(spell_type t) diff --git a/Source/panels/spell_icons.hpp b/Source/panels/spell_icons.hpp index 9f6f1d73b..69ddfcfb4 100644 --- a/Source/panels/spell_icons.hpp +++ b/Source/panels/spell_icons.hpp @@ -9,34 +9,51 @@ namespace devilution { -/** Maps from spell_id to spelicon.cel frame number. */ -extern const char SpellITbl[]; +/** + * Draw a large (56x56) spell icon onto the given buffer. + * + * @param out Output buffer. + * @param position Buffer coordinates (bottom-left). + * @param spell Spell ID. + */ +void DrawLargeSpellIcon(const Surface &out, Point position, spell_id spell); /** - * Draw spell icon onto the given buffer. + * Draw a small (37x38) spell icon onto the given buffer. + * * @param out Output buffer. - * @param position Buffer coordinates. - * @param nCel Index of the cel frame to draw. 0 based. + * @param position Buffer coordinates (bottom-left). + * @param spell Spell ID. */ -void DrawSpellCel(const Surface &out, Point position, int nCel); +void DrawSmallSpellIcon(const Surface &out, Point position, spell_id spell); /** - * Draw spell icon onto the given buffer. + * Draw an inset 2px border for a large (56x56) spell icon. + * * @param out Output buffer. - * @param position Buffer coordinates. - * @param sprite Icons sprite sheet. + * @param position Buffer coordinates (bottom-left). + * @param spell Spell ID. */ -#ifdef UNPACKED_MPQS -void DrawSpellCel(const Surface &out, Point position, ClxSprite sprite, ClxSprite background); -#else -void DrawSpellCel(const Surface &out, Point position, ClxSprite sprite); -#endif +void DrawLargeSpellIconBorder(const Surface &out, Point position, uint8_t color); -void DrawSpellBorder(const Surface &out, Point position, ClxSprite sprite); +/** + * Draw an inset 2px border for a small (37x38) spell icon. + * + * @param out Output buffer. + * @param position Buffer coordinates (bottom-left). + * @param spell Spell ID. + */ +void DrawSmallSpellIconBorder(const Surface &out, Point position); +/** + * @brief Set the color mapping for the `Draw(Small|Large)SpellIcon(Border)` calls. + */ void SetSpellTrans(spell_type t); -void LoadSpellIcons(); -void FreeSpellIcons(); +void LoadLargeSpellIcons(); +void FreeLargeSpellIcons(); + +void LoadSmallSpellIcons(); +void FreeSmallSpellIcons(); } // namespace devilution diff --git a/Source/panels/spell_list.cpp b/Source/panels/spell_list.cpp index ff04a4fbf..6807ac1c6 100644 --- a/Source/panels/spell_list.cpp +++ b/Source/panels/spell_list.cpp @@ -25,24 +25,7 @@ namespace { void PrintSBookSpellType(const Surface &out, Point position, string_view text, uint8_t rectColorIndex) { - Point rect { position }; - rect += Displacement { 0, -SPLICONLENGTH + 1 }; - - // Top - DrawHorizontalLine(out, rect, SPLICONLENGTH, rectColorIndex); - DrawHorizontalLine(out, rect + Displacement { 0, 1 }, SPLICONLENGTH, rectColorIndex); - - // Bottom - DrawHorizontalLine(out, rect + Displacement { 0, SPLICONLENGTH - 2 }, SPLICONLENGTH, rectColorIndex); - DrawHorizontalLine(out, rect + Displacement { 0, SPLICONLENGTH - 1 }, SPLICONLENGTH, rectColorIndex); - - // Left Side - DrawVerticalLine(out, rect, SPLICONLENGTH, rectColorIndex); - DrawVerticalLine(out, rect + Displacement { 1, 0 }, SPLICONLENGTH, rectColorIndex); - - // Right Side - DrawVerticalLine(out, rect + Displacement { SPLICONLENGTH - 2, 0 }, SPLICONLENGTH, rectColorIndex); - DrawVerticalLine(out, rect + Displacement { SPLICONLENGTH - 1, 0 }, SPLICONLENGTH, rectColorIndex); + DrawLargeSpellIconBorder(out, position, rectColorIndex); // Align the spell type text with bottom of spell icon position += Displacement { SPLICONLENGTH / 2 - GetLineWidth(text) / 2, (IsSmallFontTall() ? -19 : -15) }; @@ -122,9 +105,8 @@ void DrawSpell(const Surface &out) st = RSPLTYPE_INVALID; SetSpellTrans(st); - const int nCel = SpellITbl[spl]; const Point position = GetMainPanel().position + Displacement { 565, 119 }; - DrawSpellCel(out, position, nCel); + DrawLargeSpellIcon(out, position, spl); std::optional hotkeyName = GetHotkeyName(spl, myPlayer._pRSplType); if (hotkeyName) @@ -152,7 +134,7 @@ void DrawSpellList(const Surface &out) } SetSpellTrans(transType); - DrawSpellCel(out, spellListItem.location, SpellITbl[static_cast(spellId)]); + DrawLargeSpellIcon(out, spellListItem.location, spellId); std::optional hotkeyName = GetHotkeyName(spellId, spellListItem.type);