Mörsergranate: explosion radius in the tooltip

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
khallaandClaude Opus 5.5 committed 2026-10-26 22:21:50 +01:00
1 parent a4d5683403
commit 91e7f844cc
2 files changed
+80 -3

No files matched your search

+6
View File
@@ -70,3 +70,9 @@ Mod: the construct call notes grenade, owner, landing point and radius
(`ImpactRadius`, shared with the hit check); after `0x62d530` a disc (alpha
0x40) with a 2 px rim (alpha 0xa0) is drawn there, without depth test, until
the impact of that owner's grenade at that point gathers its targets.
Tooltip (2026-10-26, "the skills tooltip does not show the current radius"):
a game::Art tooltip filter adds "Explosionsradius: N" after the last value
line of each level section (current level, and the next level's in the skill
book), in that line's colour, from the same `ImpactRadiusAt(level)` as the
hit check and the marker.
+74 -3
View File
@@ -44,6 +44,7 @@
#include <map>
#include <vector>
#include "game/engine/art.h"
#include "game/engine/arts.h"
#include "game/engine/objects.h"
#include "game/engine/orders.h"
@@ -100,12 +101,79 @@ int32_t TypeSize(uint32_t type) {
// The explosion radius of a grenade thrown by `owner` (world units; a
// creature is hit when within it plus its own size).
int32_t ImpactRadius(void* owner) {
const unsigned level = owner ? game::ArtLevel(owner, game::kArtMoerser) : 0;
int32_t ImpactRadiusAt(unsigned level) {
const int32_t size = TypeSize(kGrenadeImpactType);
return static_cast<int32_t>(static_cast<float>(size) * (1.0f + kRadiusGrowth * (level / kLevelsPerStep)));
}
int32_t ImpactRadius(void* owner) {
return ImpactRadiusAt(owner ? game::ArtLevel(owner, game::kArtMoerser) : 0);
}
// The tooltip (game::Art::Tooltip filter, as for Kettenblitz in
// mods/chainlightning.cpp): sections start with the level line ("Aktuelle
// Stufe:", in the skill book also "Nächste erlernte Stufe:") followed by
// "Name: value" lines; each section gets "Explosionsradius: N" after its last
// value, in that line's colour.
constexpr const wchar_t* kCurrentLevelLine = L"Aktuelle Stufe:";
constexpr const wchar_t* kNextLevelLine = L"Nächste erlernte Stufe:";
constexpr const wchar_t* kValueSeparator = L": ";
constexpr size_t kColourCodeLength = 10; // "\cAARRGGBB"
std::wstring LeadingColourCodes(const std::wstring& line) {
size_t end = 0;
while (line.compare(end, 2, L"\\c") == 0 && end + kColourCodeLength <= line.size()) {
end += kColourCodeLength;
}
return line.substr(0, end);
}
void RewriteTooltip(const game::Art& art, void* creature, std::wstring& text) {
std::vector<std::wstring> lines;
for (size_t start = 0; start < text.size();) {
size_t end = text.find(L'\n', start);
end = end == std::wstring::npos ? text.size() : end + 1;
lines.push_back(text.substr(start, end - start));
start = end;
}
const unsigned current = game::ArtLevel(creature, art.Id());
std::vector<std::wstring> out;
unsigned level = current;
bool inSection = false;
size_t lastValue = SIZE_MAX; // index in `out`
const auto closeSection = [&] {
if (!inSection || lastValue == SIZE_MAX) {
return;
}
std::wstring& after = out[lastValue];
const bool last = after.empty() || after.back() != L'\n';
if (last) {
after += L'\n';
}
out.insert(out.begin() + static_cast<ptrdiff_t>(lastValue) + 1,
LeadingColourCodes(after) + L"Explosionsradius: " + std::to_wstring(ImpactRadiusAt(level)) +
(last ? L"" : L"\n"));
};
for (const std::wstring& line : lines) {
const bool currentHeader = line.find(kCurrentLevelLine) != std::wstring::npos;
const bool nextHeader = line.find(kNextLevelLine) != std::wstring::npos;
if (currentHeader || nextHeader) {
closeSection();
inSection = true;
level = nextHeader ? current + 1 : current;
lastValue = SIZE_MAX;
} else if (inSection && line.find(kValueSeparator) != std::wstring::npos) {
lastValue = out.size();
}
out.push_back(line);
}
closeSection();
text.clear();
for (const std::wstring& line : out) {
text += line;
}
}
// 0x5ab400 calls 0x5ff820 (thiscall (out, position, type, only, radius),
// ret 0x14) with the effect object in ebx.
__declspec(naked) void GatherTargetsThunk() {
@@ -362,7 +430,10 @@ void Startup() {
RE_STARTUP(DwarfArtsStartup, Startup);
struct Registration {
Registration() { game::OnCreatureOrder(&OnOrder); }
Registration() {
game::OnCreatureOrder(&OnOrder);
game::AddTooltipFilter(game::kArtMoerser, &RewriteTooltip);
}
} g_registration;
} // namespace