From 9cbe08d2471f027b1960aca5e51718de89d28afe Mon Sep 17 00:00:00 2001 From: mojsior Date: Sun, 25 Jan 2026 13:12:17 +0100 Subject: [PATCH] Add hotkeys to speak HP% and EXP to level --- Source/diablo.cpp | 112 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 4819b9629..4712d443e 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -110,10 +110,11 @@ #include "tables/playerdat.hpp" #include "towners.h" #include "track.h" -#include "utils/console.h" -#include "utils/display.h" -#include "utils/is_of.hpp" -#include "utils/language.h" +#include "utils/console.h" +#include "utils/display.h" +#include "utils/format_int.hpp" +#include "utils/is_of.hpp" +#include "utils/language.h" #include "utils/parse_int.hpp" #include "utils/paths.h" #include "utils/proximity_audio.hpp" @@ -3365,6 +3366,45 @@ void SpeakNearestUnexploredTileKeyPressed() SpeakText(message, true); } +void SpeakPlayerHealthPercentageKeyPressed() +{ + if (!CanPlayerTakeAction()) + return; + if (MyPlayer == nullptr) + return; + + const int maxHp = MyPlayer->_pMaxHP; + if (maxHp <= 0) + return; + + const int currentHp = std::max(MyPlayer->_pHitPoints, 0); + int hpPercent = static_cast((static_cast(currentHp) * 100 + maxHp / 2) / maxHp); + hpPercent = std::clamp(hpPercent, 0, 100); + SpeakText(fmt::format("{:d}%", hpPercent), /*force=*/true); +} + +void SpeakExperienceToNextLevelKeyPressed() +{ + if (!CanPlayerTakeAction()) + return; + if (MyPlayer == nullptr) + return; + + const Player &myPlayer = *MyPlayer; + if (myPlayer.isMaxCharacterLevel()) { + SpeakText(_("Max level."), /*force=*/true); + return; + } + + const uint32_t nextExperienceThreshold = myPlayer.getNextExperienceThreshold(); + const uint32_t currentExperience = myPlayer._pExperience; + const uint32_t remainingExperience = currentExperience >= nextExperienceThreshold ? 0 : nextExperienceThreshold - currentExperience; + const int nextLevel = myPlayer.getCharacterLevel() + 1; + SpeakText( + fmt::format(fmt::runtime(_("{:s} to Level {:d}")), FormatInteger(remainingExperience), nextLevel), + /*force=*/true); +} + void InventoryKeyPressed() { if (IsPlayerInStore()) @@ -3847,19 +3887,35 @@ void InitKeymapActions() InventoryKeyPressed, nullptr, CanPlayerTakeAction); - options.Keymapper.AddAction( - "Character", - N_("Character"), - N_("Open Character screen."), - 'C', - CharacterSheetKeyPressed, - nullptr, - CanPlayerTakeAction); - options.Keymapper.AddAction( - "Party", - N_("Party"), - N_("Open side Party panel."), - 'Y', + options.Keymapper.AddAction( + "Character", + N_("Character"), + N_("Open Character screen."), + 'C', + CharacterSheetKeyPressed, + nullptr, + CanPlayerTakeAction); + options.Keymapper.AddAction( + "SpeakPlayerHealthPercentage", + N_("Health percentage"), + N_("Speaks the player's health as a percentage."), + 'Z', + SpeakPlayerHealthPercentageKeyPressed, + nullptr, + CanPlayerTakeAction); + options.Keymapper.AddAction( + "SpeakExperienceToNextLevel", + N_("Experience to level"), + N_("Speaks how much experience remains to reach the next level."), + 'X', + SpeakExperienceToNextLevelKeyPressed, + nullptr, + CanPlayerTakeAction); + options.Keymapper.AddAction( + "Party", + N_("Party"), + N_("Open side Party panel."), + 'Y', PartyPanelSideToggleKeyPressed, nullptr, CanPlayerTakeAction); @@ -3914,17 +3970,17 @@ void InitKeymapActions() }, nullptr, IsGameRunning); - options.Keymapper.AddAction( - "Zoom", - N_("Zoom"), - N_("Zoom Game Screen."), - 'Z', - [] { - GetOptions().Graphics.zoom.SetValue(!*GetOptions().Graphics.zoom); - CalcViewportGeometry(); - }, - nullptr, - CanPlayerTakeAction); + options.Keymapper.AddAction( + "Zoom", + N_("Zoom"), + N_("Zoom Game Screen."), + SDLK_UNKNOWN, + [] { + GetOptions().Graphics.zoom.SetValue(!*GetOptions().Graphics.zoom); + CalcViewportGeometry(); + }, + nullptr, + CanPlayerTakeAction); options.Keymapper.AddAction( "PauseGame", N_("Pause Game"),