- Options window in three tabs; the auto-pickup settings move to Beute; sliders move with their page; black sides like the save game screen. - Tastenbelegung: every binding settable (click, press key; Esc cancels, Backspace unbinds), "Alle zurücksetzen" as a game button, saved on OK. - Potions bindable (default unbound), named by the game's potion texts; the game's own potion hotkeys are off; bar tooltips show the bound key. - Map (M), overview map (Tab, held) and labels (Alt, held) bindable; the game's own M / Tab / Alt handling is switched off. - Save game key calls the game's save window code instead of a posted key. - Teleportationstrank: fixed text replacement from startup (also the Ausdauertrank items, potion kind 5 as well). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
103 lines
3.9 KiB
C++
103 lines
3.9 KiB
C++
// Potion counts on the potion slots of the bottom bar.
|
|
//
|
|
// Request (2026-10-17): the potion selector always shows how many potions of
|
|
// each kind the hero carries (only when there are any).
|
|
//
|
|
// The bar shows 5 potion kinds: cUI_Taskbar2::RenderPotionSlots (0x6e39e0)
|
|
// draws control k-1 (embedded at +0x258, stride 0xbc) when
|
|
// cInventoryEntry::GetPotionRef(k) finds a potion of kind k, else the empty
|
|
// control behind it (+0x3ac). GetPotionRef walks the inventory grid and asks
|
|
// ItemPotionKind for each item's kind; the count here does the same and adds
|
|
// up the potion stacks (itemstack.cpp).
|
|
//
|
|
// Their tooltips (game::PotionTooltipText, set on the slots by the taskbar,
|
|
// 0x6e6060; shown as "\dFFFFFF80Heiltrank \wFFFFFFFF[SPC]") name the key
|
|
// bound to the kind (mods/input.cpp) instead of the game's own, which is
|
|
// switched off; no key part while unbound.
|
|
|
|
#include <cstdint>
|
|
#include <cwchar>
|
|
#include <string>
|
|
|
|
#include "game/engine/items.h"
|
|
#include "game/engine/objects.h"
|
|
#include "game/ui/text.h"
|
|
#include "game/ui/texts.h"
|
|
#include "mods/input.h"
|
|
#include "mods/itemstack.h"
|
|
#include "mods/skillslots.h"
|
|
#include "re/hook.h"
|
|
|
|
namespace mods::potioncounts {
|
|
namespace {
|
|
|
|
// cUI_Taskbar2 fields.
|
|
constexpr size_t kInventoryIndex = 0x158; // u16: the hero's inventory
|
|
constexpr size_t kPotionSlots = 0x258; // game::kPotionKinds embedded controls
|
|
constexpr size_t kPotionSlotSize = 0xbc;
|
|
|
|
constexpr uint16_t kCountFont = 1; // as the art cooldown labels (skillbar.cpp)
|
|
constexpr uint32_t kCountColor = 0xffffffff;
|
|
constexpr int kCountInset = 3; // from the slot's bottom-right corner
|
|
|
|
void RenderCounts(uint8_t* taskbar, void* renderer) {
|
|
const uint8_t* inventory =
|
|
skillslots::Inventory(*reinterpret_cast<const uint16_t*>(taskbar + kInventoryIndex));
|
|
if (!inventory || !game::GetPlayerHero()) {
|
|
return;
|
|
}
|
|
unsigned counts[game::kPotionKinds + 1] = {};
|
|
game::ForEachGridItem(inventory, [&](uint32_t id) {
|
|
const int kind = game::PotionKindOf(id);
|
|
if (kind >= 1 && kind <= game::kPotionKinds) {
|
|
counts[kind] += itemstack::StackCount(id);
|
|
}
|
|
});
|
|
const int lineHeight = game::FontLineHeight(kCountFont);
|
|
for (int kind = 1; kind <= game::kPotionKinds; ++kind) {
|
|
if (counts[kind] == 0) {
|
|
continue;
|
|
}
|
|
game::UiRect rect = game::ControlScreenRect(taskbar + kPotionSlots + (kind - 1) * kPotionSlotSize);
|
|
rect.y += rect.h - lineHeight - kCountInset;
|
|
rect.w = static_cast<int16_t>(rect.w - kCountInset);
|
|
wchar_t text[8];
|
|
swprintf_s(text, L"%u", counts[kind]);
|
|
game::DrawText(renderer, kCountFont, rect, text, kCountColor, game::kTextRight);
|
|
}
|
|
}
|
|
|
|
// The game's tooltip with its "[key]" end replaced by the bound key's.
|
|
std::wstring TooltipWithKey(uint32_t text, int vk) {
|
|
std::wstring tooltip = game::ShownText(text);
|
|
if (const size_t key = tooltip.rfind(L'['); key != std::wstring::npos && tooltip.back() == L']') {
|
|
tooltip.erase(key);
|
|
}
|
|
if (vk) {
|
|
tooltip += L"[" + input::KeyLabel(vk) + L"]";
|
|
}
|
|
return tooltip;
|
|
}
|
|
|
|
void UpdateTooltips() {
|
|
for (int kind = 1; kind <= game::kPotionKinds; ++kind) {
|
|
if (const uint32_t text = game::PotionTooltipText(kind)) {
|
|
game::SetTextOverride(text, TooltipWithKey(text, input::PotionKey(kind)));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace mods::potioncounts
|
|
|
|
// cUI_Taskbar2::RenderPotionSlots (0x6e39e0, ret 4): then the counts.
|
|
void __fastcall UiTaskbar_RenderPotionSlots(void* self, void* edx, void* renderer);
|
|
RE_HOOK(0x006e39e0, UiTaskbar_RenderPotionSlots);
|
|
void __fastcall UiTaskbar_RenderPotionSlots(void* self, void* edx, void* renderer) {
|
|
UiTaskbar_RenderPotionSlots_original(self, edx, renderer);
|
|
if (self) {
|
|
mods::potioncounts::RenderCounts(static_cast<uint8_t*>(self), renderer);
|
|
mods::potioncounts::UpdateTooltips();
|
|
}
|
|
}
|