From c99f7cf644907256cdde103b6cda03fe07d5a1d2 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 22 Jan 2023 07:20:10 +0100 Subject: [PATCH] Floating numbers (#5639) Co-authored-by: qndel Co-authored-by: Stephen C. Wills --- CMake/Assets.cmake | 3 +- .../fonts/{yellowdialog.trn => orange.trn} | Bin 256 -> 256 bytes Packaging/resources/assets/fonts/yellow.trn | Bin 0 -> 256 bytes Source/CMakeLists.txt | 1 + Source/DiabloUI/dialogs.cpp | 2 +- Source/DiabloUI/ui_flags.hpp | 27 +-- Source/diablo.cpp | 2 + Source/engine/render/scrollrt.cpp | 125 ++++++----- Source/engine/render/text_render.cpp | 13 +- Source/engine/render/text_render.hpp | 3 +- Source/gamemenu.cpp | 2 + Source/missiles.cpp | 73 ++++--- Source/monster.cpp | 13 +- Source/monster.h | 3 +- Source/msg.cpp | 5 +- Source/msg.h | 3 +- Source/objects.cpp | 2 +- Source/options.cpp | 2 + Source/options.h | 2 + Source/player.cpp | 14 +- Source/player.h | 2 +- Source/portal.cpp | 5 +- Source/qol/floatingnumbers.cpp | 198 ++++++++++++++++++ Source/qol/floatingnumbers.h | 20 ++ Source/spells.cpp | 4 +- 25 files changed, 387 insertions(+), 137 deletions(-) rename Packaging/resources/assets/fonts/{yellowdialog.trn => orange.trn} (54%) create mode 100644 Packaging/resources/assets/fonts/yellow.trn create mode 100644 Source/qol/floatingnumbers.cpp create mode 100644 Source/qol/floatingnumbers.h diff --git a/CMake/Assets.cmake b/CMake/Assets.cmake index c791c032d..e4731a299 100644 --- a/CMake/Assets.cmake +++ b/CMake/Assets.cmake @@ -157,10 +157,11 @@ set(devilutionx_assets fonts/goldui.trn fonts/grayuis.trn fonts/grayui.trn + fonts/orange.trn fonts/red.trn fonts/whitegold.trn fonts/white.trn - fonts/yellowdialog.trn + fonts/yellow.trn gendata/cutportlw.clx gendata/cutportrw.clx gendata/cutstartw.clx diff --git a/Packaging/resources/assets/fonts/yellowdialog.trn b/Packaging/resources/assets/fonts/orange.trn similarity index 54% rename from Packaging/resources/assets/fonts/yellowdialog.trn rename to Packaging/resources/assets/fonts/orange.trn index 172006163c1dcaaeca041dd6f9359350e894ddad..4473438fe4eb1df4e6d13a06a552920e46a5d639 100644 GIT binary patch delta 43 zcmV+`0M!400)PULz#*8JnVFiJo12`Rot>VZpP$gt($mz{*4NnC+S}aS-rwMn;U6TD B92Wop delta 43 zcmV+`0M!400)PULz#+iF!o$SH#>dFX%FE2n&d<=%($mz{*4NnC+S}aS-rxI?;U99c B9`XPH diff --git a/Packaging/resources/assets/fonts/yellow.trn b/Packaging/resources/assets/fonts/yellow.trn new file mode 100644 index 0000000000000000000000000000000000000000..bff6aedeb5711c205a0e919b9691ab124a380148 GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0 zEiNxGF)}kWH8wXmIXXK$Jw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rMYkdcv+l9Q8^ zl$Dj1mY0{%(bCh@)z;V8+1lIO-QM5V;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;;(|kAp literal 0 HcmV?d00001 diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 34af4ced3..1a3ab094c 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -140,6 +140,7 @@ set(libdevilutionx_SRCS qol/autopickup.cpp qol/chatlog.cpp + qol/floatingnumbers.cpp qol/itemlabels.cpp qol/monhealthbar.cpp qol/stash.cpp diff --git a/Source/DiabloUI/dialogs.cpp b/Source/DiabloUI/dialogs.cpp index e0414aacc..efbb68b20 100644 --- a/Source/DiabloUI/dialogs.cpp +++ b/Source/DiabloUI/dialogs.cpp @@ -86,7 +86,7 @@ bool Init(string_view caption, string_view text, bool error, bool renderBehind) vecOkDialog.push_back(std::make_unique(*dialogSprite, rect1)); SDL_Rect rect2 = MakeSdlRect(uiPosition.x + 147, uiPosition.y + 110, textWidth, 20); - vecOkDialog.push_back(std::make_unique(caption, rect2, UiFlags::AlignCenter | UiFlags::ColorDialogYellow)); + vecOkDialog.push_back(std::make_unique(caption, rect2, UiFlags::AlignCenter | UiFlags::ColorYellow)); SDL_Rect rect3 = MakeSdlRect(uiPosition.x + 147, uiPosition.y + 141, textWidth, 190); vecOkDialog.push_back(std::make_unique(wrappedText, rect3, UiFlags::AlignCenter | UiFlags::ColorDialogWhite)); diff --git a/Source/DiabloUI/ui_flags.hpp b/Source/DiabloUI/ui_flags.hpp index 1ee0da80d..9c3c9d33a 100644 --- a/Source/DiabloUI/ui_flags.hpp +++ b/Source/DiabloUI/ui_flags.hpp @@ -22,31 +22,32 @@ enum class UiFlags : uint32_t { ColorUiGoldDark = 1 << 8, ColorUiSilverDark = 1 << 9, ColorDialogWhite = 1 << 10, - ColorDialogYellow = 1 << 11, + ColorYellow = 1 << 11, ColorGold = 1 << 12, ColorBlack = 1 << 13, ColorWhite = 1 << 14, ColorWhitegold = 1 << 15, ColorRed = 1 << 16, ColorBlue = 1 << 17, - ColorButtonface = 1 << 18, - ColorButtonpushed = 1 << 19, + ColorOrange = 1 << 18, + ColorButtonface = 1 << 19, + ColorButtonpushed = 1 << 20, - AlignCenter = 1 << 20, - AlignRight = 1 << 21, - VerticalCenter = 1 << 22, + AlignCenter = 1 << 21, + AlignRight = 1 << 22, + VerticalCenter = 1 << 23, - KerningFitSpacing = 1 << 23, + KerningFitSpacing = 1 << 24, - ElementDisabled = 1 << 24, - ElementHidden = 1 << 25, + ElementDisabled = 1 << 25, + ElementHidden = 1 << 26, - PentaCursor = 1 << 26, - TextCursor = 1 << 27, - Outlined = 1 << 28, + PentaCursor = 1 << 27, + TextCursor = 1 << 28, + Outlined = 1 << 29, /** @brief Ensures that the if current element is active that the next element is also visible. */ - NeedsNextElement = 1 << 29, + NeedsNextElement = 1 << 30, // clang-format on }; use_enum_as_flags(UiFlags); diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 5f9e7fc4b..8e8c01473 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -64,6 +64,7 @@ #include "pfile.h" #include "plrmsg.h" #include "qol/chatlog.h" +#include "qol/floatingnumbers.h" #include "qol/itemlabels.h" #include "qol/monhealthbar.h" #include "qol/stash.h" @@ -2656,6 +2657,7 @@ void DisableInputEventHandler(const SDL_Event &event, uint16_t modState) void LoadGameLevel(bool firstflag, lvl_entry lvldir) { _music_id neededTrack = GetLevelMusic(leveltype); + ClearFloatingNumbers(); if (neededTrack != sgnMusicTrack) music_stop(); diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index d845b0147..db3c77277 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -34,6 +34,7 @@ #include "panels/charpanel.hpp" #include "plrmsg.h" #include "qol/chatlog.h" +#include "qol/floatingnumbers.h" #include "qol/itemlabels.h" #include "qol/monhealthbar.h" #include "qol/stash.h" @@ -984,101 +985,94 @@ Displacement tileShift; int tileColums; int tileRows; +void CalcFirstTilePosition(Point &position, Displacement &offset) +{ + // Adjust by player offset and tile grid alignment + Player &myPlayer = *MyPlayer; + offset = tileOffset; + if (myPlayer.isWalking()) + offset += GetOffsetForWalking(myPlayer.AnimInfo, myPlayer._pdir, true); + + position += tileShift; + + // Skip rendering parts covered by the panels + if (CanPanelsCoverView() && (IsLeftPanelOpen() || IsRightPanelOpen())) { + int multiplier = (*sgOptions.Graphics.zoom) ? 1 : 2; + position += Displacement(Direction::East) * multiplier; + offset.deltaX += -TILE_WIDTH * multiplier / 2 / 2; + + if (IsLeftPanelOpen() && !*sgOptions.Graphics.zoom) { + offset.deltaX += SidePanelSize.width; + // SidePanelSize.width accounted for in Zoom() + } + } + + // Draw areas moving in and out of the screen + if (myPlayer.isWalking()) { + switch (myPlayer._pdir) { + case Direction::North: + case Direction::NorthEast: + offset.deltaY -= TILE_HEIGHT; + position += Direction::North; + break; + case Direction::SouthWest: + case Direction::West: + offset.deltaX -= TILE_WIDTH; + position += Direction::West; + break; + case Direction::NorthWest: + offset.deltaX -= TILE_WIDTH / 2; + offset.deltaY -= TILE_HEIGHT / 2; + position += Direction::NorthWest; + default: + break; + } + } +} + /** * @brief Configure render and process screen rows * @param fullOut Buffer to render to - * @param position Center of view in dPiece coordinate + * @param position First tile of view in dPiece coordinate + * @param offset Amount to offset the rendering in screen space */ -void DrawGame(const Surface &fullOut, Point position) +void DrawGame(const Surface &fullOut, Point position, Displacement offset) { // Limit rendering to the view area const Surface &out = !*sgOptions.Graphics.zoom ? fullOut.subregionY(0, gnViewportHeight) : fullOut.subregionY(0, (gnViewportHeight + 1) / 2); - // Adjust by player offset and tile grid alignment - Player &myPlayer = *MyPlayer; - Displacement offset = {}; - if (myPlayer.isWalking()) - offset = GetOffsetForWalking(myPlayer.AnimInfo, myPlayer._pdir, true); - int sx = offset.deltaX + tileOffset.deltaX; - int sy = offset.deltaY + tileOffset.deltaY; - int columns = tileColums; int rows = tileRows; - position += tileShift; - // Skip rendering parts covered by the panels - if (CanPanelsCoverView()) { - if (!*sgOptions.Graphics.zoom) { - if (IsLeftPanelOpen()) { - position += Displacement(Direction::East) * 2; - columns -= 4; - sx += SidePanelSize.width - TILE_WIDTH / 2; - } - if (IsRightPanelOpen()) { - position += Displacement(Direction::East) * 2; - columns -= 4; - sx += -TILE_WIDTH / 2; - } - } else { - if (IsLeftPanelOpen()) { - position += Direction::East; - columns -= 2; - sx += -TILE_WIDTH / 2 / 2; // SPANEL_WIDTH accounted for in Zoom() - } - if (IsRightPanelOpen()) { - position += Direction::East; - columns -= 2; - sx += -TILE_WIDTH / 2 / 2; - } - } + if (CanPanelsCoverView() && (IsLeftPanelOpen() || IsRightPanelOpen())) { + columns -= (*sgOptions.Graphics.zoom) ? 2 : 4; } UpdateMissilesRendererData(); // Draw areas moving in and out of the screen - if (myPlayer.isWalking()) { - switch (myPlayer._pdir) { + if (MyPlayer->isWalking()) { + switch (MyPlayer->_pdir) { case Direction::NoDirection: break; case Direction::North: - sy -= TILE_HEIGHT; - position += Direction::North; + case Direction::South: rows += 2; break; case Direction::NorthEast: - sy -= TILE_HEIGHT; - position += Direction::North; columns++; rows += 2; break; case Direction::East: + case Direction::West: columns++; break; case Direction::SouthEast: - columns++; - rows++; - break; - case Direction::South: - rows += 2; - break; case Direction::SouthWest: - sx -= TILE_WIDTH; - position += Direction::West; - columns++; - rows++; - break; - case Direction::West: - sx -= TILE_WIDTH; - position += Direction::West; - columns++; - break; case Direction::NorthWest: - sx -= TILE_WIDTH / 2; - sy -= TILE_HEIGHT / 2; - position += Direction::NorthWest; columns++; rows++; break; @@ -1089,8 +1083,8 @@ void DrawGame(const Surface &fullOut, Point position) DunRenderStats.clear(); #endif - DrawFloor(out, position, { sx, sy }, rows, columns); - DrawTileContent(out, position, { sx, sy }, rows, columns); + DrawFloor(out, position, Point {} + offset, rows, columns); + DrawTileContent(out, position, Point {} + offset, rows, columns); if (*sgOptions.Graphics.zoom) { Zoom(fullOut.subregionY(0, gnViewportHeight)); @@ -1126,7 +1120,9 @@ void DrawView(const Surface &out, Point startPosition) #ifdef _DEBUG DebugCoordsMap.clear(); #endif - DrawGame(out, startPosition); + Displacement offset = {}; + CalcFirstTilePosition(startPosition, offset); + DrawGame(out, startPosition, offset); if (AutomapActive) { DrawAutomap(out.subregionY(0, gnViewportHeight)); } @@ -1195,6 +1191,7 @@ void DrawView(const Surface &out, Point startPosition) #endif DrawMonsterHealthBar(out); DrawItemNameLabels(out); + DrawFloatingNumbers(out, startPosition, offset); if (stextflag != STORE_NONE && !qtextflag) DrawSText(out); diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index 2fbe69bbf..3424b7ac9 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -47,14 +47,14 @@ constexpr std::array LineHeights = { 12, 26, 38, 42, 50, 22 }; constexpr int SmallFontTallLineHeight = 16; std::array BaseLineOffset = { -3, -2, -3, -6, -7, 3 }; -std::array ColorTranslations = { +std::array ColorTranslations = { "fonts\\goldui.trn", "fonts\\grayui.trn", "fonts\\golduis.trn", "fonts\\grayuis.trn", nullptr, - "fonts\\yellowdialog.trn", + "fonts\\yellow.trn", nullptr, "fonts\\black.trn", @@ -63,12 +63,13 @@ std::array ColorTranslations = { "fonts\\whitegold.trn", "fonts\\red.trn", "fonts\\blue.trn", + "fonts\\orange.trn", "fonts\\buttonface.trn", "fonts\\buttonpushed.trn", }; -std::array>, 14> ColorTranslationsData; +std::array>, 15> ColorTranslationsData; GameFontTables GetSizeFromFlags(UiFlags flags) { @@ -92,6 +93,8 @@ text_color GetColorFromFlags(UiFlags flags) return ColorWhite; else if (HasAnyOf(flags, UiFlags::ColorBlue)) return ColorBlue; + else if (HasAnyOf(flags, UiFlags::ColorOrange)) + return ColorOrange; else if (HasAnyOf(flags, UiFlags::ColorRed)) return ColorRed; else if (HasAnyOf(flags, UiFlags::ColorBlack)) @@ -108,8 +111,8 @@ text_color GetColorFromFlags(UiFlags flags) return ColorUiSilverDark; else if (HasAnyOf(flags, UiFlags::ColorDialogWhite)) return ColorDialogWhite; - else if (HasAnyOf(flags, UiFlags::ColorDialogYellow)) - return ColorDialogYellow; + else if (HasAnyOf(flags, UiFlags::ColorYellow)) + return ColorYellow; else if (HasAnyOf(flags, UiFlags::ColorButtonface)) return ColorButtonface; else if (HasAnyOf(flags, UiFlags::ColorButtonpushed)) diff --git a/Source/engine/render/text_render.hpp b/Source/engine/render/text_render.hpp index a69a10ff3..aee9961d7 100644 --- a/Source/engine/render/text_render.hpp +++ b/Source/engine/render/text_render.hpp @@ -37,7 +37,7 @@ enum text_color : uint8_t { ColorUiSilverDark, ColorDialogWhite, - ColorDialogYellow, + ColorYellow, ColorGold, ColorBlack, @@ -46,6 +46,7 @@ enum text_color : uint8_t { ColorWhitegold, ColorRed, ColorBlue, + ColorOrange, ColorButtonface, ColorButtonpushed, diff --git a/Source/gamemenu.cpp b/Source/gamemenu.cpp index acd6db70c..b528d258c 100644 --- a/Source/gamemenu.cpp +++ b/Source/gamemenu.cpp @@ -15,6 +15,7 @@ #include "loadsave.h" #include "options.h" #include "pfile.h" +#include "qol/floatingnumbers.h" #include "utils/language.h" namespace devilution { @@ -292,6 +293,7 @@ void gamemenu_load_game(bool /*bActivate*/) { EventHandler saveProc = SetEventHandler(DisableInputEventHandler); gamemenu_off(); + ClearFloatingNumbers(); NewCursor(CURSOR_NONE); InitDiabloMsg(EMSG_LOADING); RedrawEverything(); diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 438cfb69d..b3e6fbf68 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -199,7 +199,8 @@ bool MonsterMHit(int pnum, int monsterId, int mindam, int maxdam, int dist, Miss int hit = GenerateRnd(100); int hper = 0; const Player &player = Players[pnum]; - if (MissilesData[static_cast(t)].mType == 0) { + const MissileData &missileData = MissilesData[static_cast(t)]; + if (missileData.mType == 0) { hper = player.GetRangedPiercingToHit(); hper -= player.CalculateArmorPierce(monster.armorClass, false); hper -= (dist * dist) / 2; @@ -229,7 +230,7 @@ bool MonsterMHit(int pnum, int monsterId, int mindam, int maxdam, int dist, Miss dam = mindam + GenerateRnd(maxdam - mindam + 1); } - if (MissilesData[static_cast(t)].mType == 0 && MissilesData[static_cast(t)].damageType == DamageType::Physical) { + if (missileData.mType == 0 && missileData.damageType == DamageType::Physical) { dam = player._pIBonusDamMod + dam * player._pIBonusDam / 100 + dam; if (player._pClass == HeroClass::Rogue) dam += player._pDamageMod; @@ -245,7 +246,7 @@ bool MonsterMHit(int pnum, int monsterId, int mindam, int maxdam, int dist, Miss dam >>= 2; if (&player == MyPlayer) - ApplyMonsterDamage(monster, dam); + ApplyMonsterDamage(missileData.damageType, monster, dam); if (monster.hitPoints >> 6 <= 0) { M_StartKill(monster, player); @@ -253,7 +254,7 @@ bool MonsterMHit(int pnum, int monsterId, int mindam, int maxdam, int dist, Miss monster.tag(player); PlayEffect(monster, MonsterSound::Hit); } else { - if (monster.mode != MonsterMode::Petrified && MissilesData[static_cast(t)].mType == 0 && HasAnyOf(player._pIFlags, ItemSpecialEffect::Knockback)) + if (monster.mode != MonsterMode::Petrified && missileData.mType == 0 && HasAnyOf(player._pIFlags, ItemSpecialEffect::Knockback)) M_GetKnockback(monster); if (monster.type().type != MT_GOLEM) M_StartHit(monster, player, dam); @@ -287,12 +288,14 @@ bool Plr2PlrMHit(const Player &player, int p, int mindam, int maxdam, int dist, return false; } - if (HasAnyOf(target._pSpellFlags, SpellFlag::Etherealize) && MissilesData[static_cast(mtype)].mType == 0) { + const MissileData &missileData = MissilesData[static_cast(mtype)]; + + if (HasAnyOf(target._pSpellFlags, SpellFlag::Etherealize) && missileData.mType == 0) { return false; } int8_t resper; - switch (MissilesData[static_cast(mtype)].damageType) { + switch (missileData.damageType) { case DamageType::Fire: resper = target._pFireResist; break; @@ -311,7 +314,7 @@ bool Plr2PlrMHit(const Player &player, int p, int mindam, int maxdam, int dist, int hper = GenerateRnd(100); int hit; - if (MissilesData[static_cast(mtype)].mType == 0) { + if (missileData.mType == 0) { hit = player.GetRangedToHit() - (dist * dist / 2) - target.GetArmor(); @@ -340,17 +343,17 @@ bool Plr2PlrMHit(const Player &player, int p, int mindam, int maxdam, int dist, dam = target._pHitPoints / 3; } else { dam = mindam + GenerateRnd(maxdam - mindam + 1); - if (MissilesData[static_cast(mtype)].mType == 0 && MissilesData[static_cast(mtype)].damageType == DamageType::Physical) + if (missileData.mType == 0 && missileData.damageType == DamageType::Physical) dam += player._pIBonusDamMod + player._pDamageMod + dam * player._pIBonusDam / 100; if (!shift) dam <<= 6; } - if (MissilesData[static_cast(mtype)].mType != 0) + if (missileData.mType != 0) dam /= 2; if (resper > 0) { dam -= (dam * resper) / 100; if (&player == MyPlayer) - NetSendCmdDamage(true, p, dam); + NetSendCmdDamage(true, p, dam, missileData.damageType); target.Say(HeroSpeech::ArghClang); return true; } @@ -360,7 +363,7 @@ bool Plr2PlrMHit(const Player &player, int p, int mindam, int maxdam, int dist, *blocked = true; } else { if (&player == MyPlayer) - NetSendCmdDamage(true, p, dam); + NetSendCmdDamage(true, p, dam, missileData.damageType); StartPlrHit(target, dam, false); } @@ -456,8 +459,9 @@ void CheckMissileCol(Missile &missile, int minDamage, int maxDamage, bool isDama missile._miHitFlag = false; } - if (missile._mirange == 0 && MissilesData[static_cast(missile._mitype)].miSFX != -1) - PlaySfxLoc(MissilesData[static_cast(missile._mitype)].miSFX, missile.position.tile); + const MissileData &missileData = MissilesData[static_cast(missile._mitype)]; + if (missile._mirange == 0 && missileData.miSFX != -1) + PlaySfxLoc(missileData.miSFX, missile.position.tile); } bool MoveMissile(Missile &missile, tl::function_ref checkTile, bool ifCheckTileFailsDontMoveToTile = false) @@ -932,7 +936,7 @@ bool MonsterTrapHit(int monsterId, int mindam, int maxdam, int dist, MissileID t dam <<= 6; if (resist) dam /= 4; - ApplyMonsterDamage(monster, dam); + ApplyMonsterDamage(MissilesData[static_cast(t)].damageType, monster, dam); #ifdef _DEBUG if (DebugGodMode) monster.hitPoints = 0; @@ -961,7 +965,9 @@ bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, Missil return false; } - if (HasAnyOf(player._pSpellFlags, SpellFlag::Etherealize) && MissilesData[static_cast(mtype)].mType == 0) { + const MissileData &missileData = MissilesData[static_cast(mtype)]; + + if (HasAnyOf(player._pSpellFlags, SpellFlag::Etherealize) && missileData.mType == 0) { return false; } @@ -971,7 +977,7 @@ bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, Missil hit = 1000; #endif int hper = 40; - if (MissilesData[static_cast(mtype)].mType == 0) { + if (missileData.mType == 0) { int tac = player.GetArmor(); if (monster != nullptr) { hper = monster->toHit @@ -1010,7 +1016,7 @@ bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, Missil blkper = clamp(blkper, 0, 100); int8_t resper; - switch (MissilesData[static_cast(mtype)].damageType) { + switch (missileData.damageType) { case DamageType::Fire: resper = player._pFireResist; break; @@ -1064,7 +1070,7 @@ bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, Missil if (resper > 0) { dam -= dam * resper / 100; if (&player == MyPlayer) { - ApplyPlrDamage(player, 0, 0, dam, earflag); + ApplyPlrDamage(missileData.damageType, player, 0, 0, dam, earflag); } if (player._pHitPoints >> 6 > 0) { @@ -1074,7 +1080,7 @@ bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, Missil } if (&player == MyPlayer) { - ApplyPlrDamage(player, 0, 0, dam, earflag); + ApplyPlrDamage(missileData.damageType, player, 0, 0, dam, earflag); } if (player._pHitPoints >> 6 > 0) { @@ -1113,7 +1119,7 @@ void InitMissiles() if (missile.sourcePlayer() == MyPlayer) { int missingHP = myPlayer._pMaxHP - myPlayer._pHitPoints; CalcPlrItemVals(myPlayer, true); - ApplyPlrDamage(myPlayer, 0, 1, missingHP + missile.var2); + ApplyPlrDamage(DamageType::Physical, myPlayer, 0, 1, missingHP + missile.var2); } } } @@ -2622,7 +2628,7 @@ Missile *AddMissile(Point src, Point dst, Direction midir, MissileID mitype, mie Missiles.emplace_back(Missile {}); auto &missile = Missiles.back(); - const auto &missileData = MissilesData[static_cast(mitype)]; + const MissileData &missileData = MissilesData[static_cast(mitype)]; missile._mitype = mitype; missile._micaster = micaster; @@ -2688,16 +2694,17 @@ void MI_LArrow(Missile &missile) mind = GenerateRnd(10) + 1 + currlevel; maxd = GenerateRnd(10) + 1 + currlevel * 2; } - DamageType rst = MissilesData[static_cast(missile._mitype)].damageType; - MissilesData[static_cast(missile._mitype)].damageType = DamageType::Physical; + MissileData &missileData = MissilesData[static_cast(missile._mitype)]; + DamageType rst = missileData.damageType; + missileData.damageType = DamageType::Physical; MoveMissileAndCheckMissileCol(missile, mind, maxd, true, false); - MissilesData[static_cast(missile._mitype)].damageType = rst; + missileData.damageType = rst; if (missile._mirange == 0) { missile._mimfnum = 0; missile._mirange = missile._miAnimLen - 1; missile.position.StopMissile(); - rst = MissilesData[static_cast(missile._mitype)].damageType; + rst = missileData.damageType; int eMind; int eMaxd; @@ -2735,9 +2742,9 @@ void MI_LArrow(Missile &missile) break; } SetMissAnim(missile, eAnim); - MissilesData[static_cast(missile._mitype)].damageType = eRst; + missileData.damageType = eRst; CheckMissileCol(missile, eMind, eMaxd, false, missile.position.tile, true); - MissilesData[static_cast(missile._mitype)].damageType = rst; + missileData.damageType = rst; } else { if (missile.position.tile != Point { missile.var1, missile.var2 }) { missile.var1 = missile.position.tile.x; @@ -3498,16 +3505,17 @@ void MI_Weapexp(Missile &missile) const Player &player = Players[missile._misource]; int mind; int maxd; + MissileData &missileData = MissilesData[static_cast(missile._mitype)]; if (missile.var2 == 1) { // BUGFIX: damage of missile should be encoded in missile struct; player can be dead/have left the game before missile arrives. mind = player._pIFMinDam; maxd = player._pIFMaxDam; - MissilesData[static_cast(missile._mitype)].damageType = DamageType::Fire; + missileData.damageType = DamageType::Fire; } else { // BUGFIX: damage of missile should be encoded in missile struct; player can be dead/have left the game before missile arrives. mind = player._pILMinDam; maxd = player._pILMaxDam; - MissilesData[static_cast(missile._mitype)].damageType = DamageType::Lightning; + missileData.damageType = DamageType::Lightning; } CheckMissileCol(missile, mind, maxd, false, missile.position.tile, false); if (missile.var1 == 0) { @@ -3803,7 +3811,7 @@ void MI_Blodboil(Missile &missile) } CalcPlrItemVals(player, true); - ApplyPlrDamage(player, 0, 1, hpdif); + ApplyPlrDamage(DamageType::Physical, player, 0, 1, hpdif); RedrawEverything(); player.Say(HeroSpeech::HeavyBreathing); } @@ -4096,8 +4104,9 @@ void ProcessMissiles() MissilePreFlag = false; for (auto &missile : Missiles) { - if (MissilesData[static_cast(missile._mitype)].mProc != nullptr) - MissilesData[static_cast(missile._mitype)].mProc(missile); + const MissileData &missileData = MissilesData[static_cast(missile._mitype)]; + if (missileData.mProc != nullptr) + missileData.mProc(missile); if (missile._miAnimFlags == MissileDataFlags::NotAnimated) continue; diff --git a/Source/monster.cpp b/Source/monster.cpp index 74a146a4b..83e5f14d2 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -33,6 +33,7 @@ #include "missiles.h" #include "movie.h" #include "options.h" +#include "qol/floatingnumbers.h" #include "spelldat.h" #include "storm/storm_net.hpp" #include "towners.h" @@ -1086,7 +1087,7 @@ void MonsterAttackMonster(Monster &attacker, Monster &target, int hper, int mind return; int dam = (mind + GenerateRnd(maxd - mind + 1)) << 6; - ApplyMonsterDamage(target, dam); + ApplyMonsterDamage(DamageType::Physical, target, dam); if (attacker.isPlayerMinion()) { int playerId = attacker.getId(); @@ -1113,7 +1114,7 @@ int CheckReflect(Monster &monster, Player &player, int dam) NetSendCmdParam1(true, CMD_SETREFLECT, 0); // reflects 20-30% damage int mdam = dam * RandomIntBetween(20, 30, true) / 100; - ApplyMonsterDamage(monster, mdam); + ApplyMonsterDamage(DamageType::Physical, monster, mdam); if (monster.hitPoints >> 6 <= 0) M_StartKill(monster, player); else @@ -1197,13 +1198,13 @@ void MonsterAttackPlayer(Monster &monster, Player &player, int hit, int minDam, int reflectedDamage = CheckReflect(monster, player, dam); dam = std::max(dam - reflectedDamage, 0); } - ApplyPlrDamage(player, 0, 0, dam); + ApplyPlrDamage(DamageType::Physical, player, 0, 0, dam); } // Reflect can also kill a monster, so make sure the monster is still alive if (HasAnyOf(player._pIFlags, ItemSpecialEffect::Thorns) && monster.mode != MonsterMode::Death) { int mdam = (GenerateRnd(3) + 1) << 6; - ApplyMonsterDamage(monster, mdam); + ApplyMonsterDamage(DamageType::Physical, monster, mdam); if (monster.hitPoints >> 6 <= 0) M_StartKill(monster, player); else @@ -3568,8 +3569,10 @@ void AddDoppelganger(Monster &monster) } } -void ApplyMonsterDamage(Monster &monster, int damage) +void ApplyMonsterDamage(DamageType damageType, Monster &monster, int damage) { + AddFloatingNumber(damageType, monster, damage); + monster.hitPoints -= damage; if (monster.hitPoints >> 6 <= 0) { diff --git a/Source/monster.h b/Source/monster.h index fe1e2a729..f753b8765 100644 --- a/Source/monster.h +++ b/Source/monster.h @@ -21,6 +21,7 @@ #include "engine/sound.h" #include "engine/world_tile.hpp" #include "init.h" +#include "misdat.h" #include "monstdat.h" #include "spelldat.h" #include "textdat.h" @@ -456,7 +457,7 @@ void InitMonsters(); void SetMapMonsters(const uint16_t *dunData, Point startPosition); Monster *AddMonster(Point position, Direction dir, size_t mtype, bool inMap); void AddDoppelganger(Monster &monster); -void ApplyMonsterDamage(Monster &monster, int damage); +void ApplyMonsterDamage(DamageType damageType, Monster &monster, int damage); bool M_Talker(const Monster &monster); void M_StartStand(Monster &monster, Direction md); void M_ClearSquares(const Monster &monster); diff --git a/Source/msg.cpp b/Source/msg.cpp index 2489633f6..f738967ce 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -1993,7 +1993,7 @@ size_t OnPlayerDamage(const TCmd *pCmd, Player &player) Player &target = Players[message.bPlr]; if (&target == MyPlayer && leveltype != DTYPE_TOWN && gbBufferMsgs != 1) { if (player.isOnActiveLevel() && damage <= 192000 && target._pHitPoints >> 6 > 0) { - ApplyPlrDamage(target, 0, 0, damage, 1); + ApplyPlrDamage(message.damageType, target, 0, 0, damage, 1); } } @@ -3232,13 +3232,14 @@ void NetSendCmdChBeltItem(bool bHiPri, int beltIndex) NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd)); } -void NetSendCmdDamage(bool bHiPri, uint8_t bPlr, uint32_t dwDam) +void NetSendCmdDamage(bool bHiPri, uint8_t bPlr, uint32_t dwDam, DamageType damageType) { TCmdDamage cmd; cmd.bCmd = CMD_PLRDAMAGE; cmd.bPlr = bPlr; cmd.dwDam = dwDam; + cmd.damageType = damageType; if (bHiPri) NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd)); else diff --git a/Source/msg.h b/Source/msg.h index 8cd2a0cc4..6cd3789ef 100644 --- a/Source/msg.h +++ b/Source/msg.h @@ -643,6 +643,7 @@ struct TCmdDamage { _cmd_id bCmd; uint8_t bPlr; uint32_t dwDam; + DamageType damageType; }; struct TCmdMonDamage { @@ -774,7 +775,7 @@ void NetSendCmdChItem(bool bHiPri, uint8_t bLoc); void NetSendCmdDelItem(bool bHiPri, uint8_t bLoc); void NetSendCmdChInvItem(bool bHiPri, int invGridIndex); void NetSendCmdChBeltItem(bool bHiPri, int invGridIndex); -void NetSendCmdDamage(bool bHiPri, uint8_t bPlr, uint32_t dwDam); +void NetSendCmdDamage(bool bHiPri, uint8_t bPlr, uint32_t dwDam, DamageType damageType); void NetSendCmdMonDmg(bool bHiPri, uint16_t wMon, uint32_t dwDam); void NetSendCmdString(uint32_t pmask, const char *pszStr); void delta_close_portal(int pnum); diff --git a/Source/objects.cpp b/Source/objects.cpp index e4083224b..393828214 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -1722,7 +1722,7 @@ void UpdateBurningCrossDamage(Object &cross) if (myPlayer.position.tile != cross.position + Displacement { 0, -1 }) return; - ApplyPlrDamage(myPlayer, 0, 0, damage[leveltype - 1]); + ApplyPlrDamage(DamageType::Fire, myPlayer, 0, 0, damage[leveltype - 1]); if (myPlayer._pHitPoints >> 6 > 0) { myPlayer.Say(HeroSpeech::Argh); } diff --git a/Source/options.cpp b/Source/options.cpp index a275f1e4c..c85bc4a30 100644 --- a/Source/options.cpp +++ b/Source/options.cpp @@ -1025,6 +1025,7 @@ GameplayOptions::GameplayOptions() , numFullManaPotionPickup("Full Mana Potion Pickup", OptionEntryFlags::None, N_("Full Mana Potion Pickup"), N_("Number of Full Mana potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 }) , numRejuPotionPickup("Rejuvenation Potion Pickup", OptionEntryFlags::None, N_("Rejuvenation Potion Pickup"), N_("Number of Rejuvenation potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 }) , numFullRejuPotionPickup("Full Rejuvenation Potion Pickup", OptionEntryFlags::None, N_("Full Rejuvenation Potion Pickup"), N_("Number of Full Rejuvenation potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 }) + , enableFloatingNumbers("Enable floating numbers", OptionEntryFlags::None, "Enable floating numbers", N_("Enables floating numbers on gaining XP / dealing damage etc."), false) { grabInput.SetValueChangedCallback(OptionGrabInputChanged); experienceBar.SetValueChangedCallback(OptionExperienceBarChanged); @@ -1065,6 +1066,7 @@ std::vector GameplayOptions::GetEntries() &numFullManaPotionPickup, &numRejuPotionPickup, &numFullRejuPotionPickup, + &enableFloatingNumbers, }; } diff --git a/Source/options.h b/Source/options.h index 5a29d356b..dd14b406d 100644 --- a/Source/options.h +++ b/Source/options.h @@ -583,6 +583,8 @@ struct GameplayOptions : OptionCategoryBase { OptionEntryInt numRejuPotionPickup; /** @brief Number of Full Rejuvenating potions to pick up automatically */ OptionEntryInt numFullRejuPotionPickup; + /** @brief Enable floating numbers. */ + OptionEntryBoolean enableFloatingNumbers; }; struct ControllerOptions : OptionCategoryBase { diff --git a/Source/player.cpp b/Source/player.cpp index 493d5b874..b1e4c5b72 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -37,6 +37,7 @@ #include "options.h" #include "player.h" #include "qol/autopickup.h" +#include "qol/floatingnumbers.h" #include "qol/stash.h" #include "spells.h" #include "stores.h" @@ -824,7 +825,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) if (HasAnyOf(player.pDamAcFlags, ItemSpecialEffectHf::Peril)) { dam2 += player._pIGetHit << 6; if (dam2 >= 0) { - ApplyPlrDamage(player, 0, 1, dam2); + ApplyPlrDamage(DamageType::Physical, player, 0, 1, dam2); } dam *= 2; } @@ -833,7 +834,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false) dam = monster.hitPoints; /* ensure monster is killed with one hit */ } #endif - ApplyMonsterDamage(monster, dam); + ApplyMonsterDamage(DamageType::Physical, monster, dam); } int skdam = 0; @@ -951,7 +952,7 @@ bool PlrHitPlr(Player &attacker, Player &target) RedrawComponent(PanelDrawComponent::Health); } if (&attacker == MyPlayer) { - NetSendCmdDamage(true, target.getId(), skdam); + NetSendCmdDamage(true, target.getId(), skdam, DamageType::Physical); } StartPlrHit(target, skdam, false); @@ -3061,9 +3062,12 @@ void StripTopGold(Player &player) player._pGold = CalculateGold(player); } -void ApplyPlrDamage(Player &player, int dam, int minHP /*= 0*/, int frac /*= 0*/, int earflag /*= 0*/) +void ApplyPlrDamage(DamageType damageType, Player &player, int dam, int minHP /*= 0*/, int frac /*= 0*/, int earflag /*= 0*/) { int totalDamage = (dam << 6) + frac; + if (&player == MyPlayer) { + AddFloatingNumber(damageType, player, totalDamage); + } if (totalDamage > 0 && player.pManaShield) { int8_t manaShieldLevel = player._pSplLvl[SPL_MANASHIELD]; if (manaShieldLevel > 0) { @@ -3273,7 +3277,7 @@ void ProcessPlayers() if (&player == MyPlayer) { if (HasAnyOf(player._pIFlags, ItemSpecialEffect::DrainLife) && leveltype != DTYPE_TOWN) { - ApplyPlrDamage(player, 0, 0, 4); + ApplyPlrDamage(DamageType::Physical, player, 0, 0, 4); } if (HasAnyOf(player._pIFlags, ItemSpecialEffect::NoMana) && player._pManaBase > 0) { player._pManaBase -= player._pMana; diff --git a/Source/player.h b/Source/player.h index dbc046398..68b8e0edc 100644 --- a/Source/player.h +++ b/Source/player.h @@ -791,7 +791,7 @@ void NextPlrLevel(Player &player); #endif void AddPlrExperience(Player &player, int lvl, int exp); void AddPlrMonstExper(int lvl, int exp, char pmask); -void ApplyPlrDamage(Player &player, int dam, int minHP = 0, int frac = 0, int earflag = 0); +void ApplyPlrDamage(DamageType damageType, Player &player, int dam, int minHP = 0, int frac = 0, int earflag = 0); void InitPlayer(Player &player, bool FirstTime); void InitMultiView(); void PlrClrTrans(Point position); diff --git a/Source/portal.cpp b/Source/portal.cpp index 90ed612a7..a61c58531 100644 --- a/Source/portal.cpp +++ b/Source/portal.cpp @@ -49,7 +49,8 @@ void SetPortalStats(int i, bool o, int x, int y, int lvl, dungeon_type lvltype, void AddWarpMissile(int i, Point position, bool sync) { - MissilesData[static_cast(MissileID::TownPortal)].mlSFX = SFX_NONE; + MissileData &missileData = MissilesData[static_cast(MissileID::TownPortal)]; + missileData.mlSFX = SFX_NONE; auto *missile = AddMissile({ 0, 0 }, position, Direction::South, MissileID::TownPortal, TARGET_MONSTERS, i, 0, 0); if (missile != nullptr) { @@ -61,7 +62,7 @@ void AddWarpMissile(int i, Point position, bool sync) missile->_mlid = AddLight(missile->position.tile, 15); } - MissilesData[static_cast(MissileID::TownPortal)].mlSFX = LS_SENTINEL; + missileData.mlSFX = LS_SENTINEL; } void SyncPortals() diff --git a/Source/qol/floatingnumbers.cpp b/Source/qol/floatingnumbers.cpp new file mode 100644 index 000000000..6de8a445d --- /dev/null +++ b/Source/qol/floatingnumbers.cpp @@ -0,0 +1,198 @@ +#include "floatingnumbers.h" + +#include +#include +#include +#include + +#include "engine/render/text_render.hpp" +#include "options.h" + +namespace devilution { + +namespace { + +struct FloatingNumber { + Point startPos; + Displacement startOffset; + Displacement endOffset; + std::string text; + uint32_t time; + uint32_t lastMerge; + UiFlags style; + DamageType type; + int value; + int index; + bool reverseDirection; +}; + +std::deque FloatingQueue; + +void ClearExpiredNumbers() +{ + while (!FloatingQueue.empty()) { + FloatingNumber &num = FloatingQueue.front(); + if (num.time > SDL_GetTicks()) + break; + + FloatingQueue.pop_front(); + } +} + +GameFontTables GetGameFontSizeByDamage(int value) +{ + value >>= 6; + if (value >= 300) + return GameFont30; + if (value >= 100) + return GameFont24; + return GameFont12; +} + +UiFlags GetFontSizeByDamage(int value) +{ + value >>= 6; + if (value >= 300) + return UiFlags::FontSize30; + if (value >= 100) + return UiFlags::FontSize24; + return UiFlags::FontSize12; +} + +void UpdateFloatingData(FloatingNumber &num) +{ + num.text = fmt::format("{:d}", num.value >> 6); + if (num.value > 0 && num.value < 64) { + num.text = fmt::format("{:.2f}", num.value / 64.0); + } + + num.style &= ~(UiFlags::FontSize12 | UiFlags::FontSize24 | UiFlags::FontSize30); + num.style |= GetFontSizeByDamage(num.value); + + switch (num.type) { + case DamageType::Physical: + num.style |= UiFlags::ColorGold; + break; + case DamageType::Fire: + num.style |= UiFlags::ColorUiSilver; // UiSilver appears dark red ingame + break; + case DamageType::Lightning: + num.style |= UiFlags::ColorBlue; + break; + case DamageType::Magic: + num.style |= UiFlags::ColorOrange; + break; + case DamageType::Acid: + num.style |= UiFlags::ColorYellow; + break; + } +} + +void AddFloatingNumber(Point pos, Displacement offset, DamageType type, int value, int index, bool damageToPlayer) +{ + // 45 deg angles to avoid jitter caused by px alignment + Displacement goodAngles[] = { + { 0, -140 }, + { 100, -100 }, + { -100, -100 }, + }; + + Displacement endOffset = goodAngles[rand() % 3]; + + if (damageToPlayer) + endOffset = -endOffset; + + for (auto &num : FloatingQueue) { + if (num.reverseDirection == damageToPlayer && num.type == type && num.index == index && (SDL_GetTicks() - static_cast(num.lastMerge)) <= 100) { + num.value += value; + num.lastMerge = SDL_GetTicks(); + UpdateFloatingData(num); + return; + } + } + FloatingNumber num { + pos, offset, endOffset, "", SDL_GetTicks() + 2500, SDL_GetTicks(), UiFlags::Outlined, type, value, index, damageToPlayer + }; + UpdateFloatingData(num); + FloatingQueue.push_back(num); +} + +} // namespace + +void AddFloatingNumber(DamageType damageType, const Monster &monster, int damage) +{ + if (!*sgOptions.Gameplay.enableFloatingNumbers) + return; + + Displacement offset = {}; + if (monster.isWalking()) { + offset = GetOffsetForWalking(monster.animInfo, monster.direction); + if (monster.mode == MonsterMode::MoveSideways) { + if (monster.direction == Direction::West) + offset -= Displacement { 64, 0 }; + else + offset += Displacement { 64, 0 }; + } + } + if (monster.animInfo.sprites) { + const ClxSprite sprite = monster.animInfo.currentSprite(); + offset.deltaY -= sprite.height() / 2; + } + + AddFloatingNumber(monster.position.tile, offset, damageType, damage, monster.getId(), false); +} + +void AddFloatingNumber(DamageType damageType, const Player &player, int damage) +{ + if (!*sgOptions.Gameplay.enableFloatingNumbers) + return; + + Displacement offset = {}; + if (player.isWalking()) { + offset = GetOffsetForWalking(player.AnimInfo, player._pdir); + if (player._pmode == PM_WALK_SIDEWAYS) { + if (player._pdir == Direction::West) + offset -= Displacement { 64, 0 }; + else + offset += Displacement { 64, 0 }; + } + } + + AddFloatingNumber(player.position.tile, offset, damageType, damage, player.getId(), true); +} + +void DrawFloatingNumbers(const Surface &out, Point viewPosition, Displacement offset) +{ + if (!*sgOptions.Gameplay.enableFloatingNumbers) + return; + + for (auto &floatingNum : FloatingQueue) { + Displacement worldOffset = viewPosition - floatingNum.startPos; + worldOffset = worldOffset.worldToScreen() + offset + Displacement { TILE_WIDTH / 2, -TILE_HEIGHT / 2 } + floatingNum.startOffset; + + if (*sgOptions.Graphics.zoom) { + worldOffset *= 2; + } + + Point screenPosition { worldOffset.deltaX, worldOffset.deltaY }; + + int lineWidth = GetLineWidth(floatingNum.text, GetGameFontSizeByDamage(floatingNum.value)); + screenPosition.x -= lineWidth / 2; + uint32_t timeLeft = floatingNum.time - SDL_GetTicks(); + float mul = 1 - (timeLeft / 2500.0f); + screenPosition += floatingNum.endOffset * mul; + + DrawString(out, floatingNum.text, Rectangle { screenPosition, { lineWidth, 0 } }, floatingNum.style); + } + + ClearExpiredNumbers(); +} + +void ClearFloatingNumbers() +{ + srand(time(nullptr)); + + FloatingQueue.clear(); +} + +} // namespace devilution diff --git a/Source/qol/floatingnumbers.h b/Source/qol/floatingnumbers.h new file mode 100644 index 000000000..660ae9d64 --- /dev/null +++ b/Source/qol/floatingnumbers.h @@ -0,0 +1,20 @@ +/** + * @file floatingnumbers.h + * + * Adds floating numbers QoL feature + */ +#pragma once + +#include "engine/point.hpp" +#include "misdat.h" +#include "monster.h" +#include "player.h" + +namespace devilution { + +void AddFloatingNumber(DamageType damageType, const Monster &monster, int damage); +void AddFloatingNumber(DamageType damageType, const Player &player, int damage); +void DrawFloatingNumbers(const Surface &out, Point viewPosition, Displacement offset); +void ClearFloatingNumbers(); + +} // namespace devilution diff --git a/Source/spells.cpp b/Source/spells.cpp index d661cde27..23d77f362 100644 --- a/Source/spells.cpp +++ b/Source/spells.cpp @@ -196,10 +196,10 @@ void ConsumeSpell(Player &player, spell_id sn) break; } if (sn == SPL_FLARE) { - ApplyPlrDamage(player, 5); + ApplyPlrDamage(DamageType::Physical, player, 5); } if (sn == SPL_BONESPIRIT) { - ApplyPlrDamage(player, 6); + ApplyPlrDamage(DamageType::Physical, player, 6); } }