Mörsergranate: explosion radius in the tooltip
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a4d5683403
commit
91e7f844cc
2 files changed
+80
-3
No files matched your search
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user