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
|
(`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
|
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.
|
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 <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "game/engine/art.h"
|
||||||
#include "game/engine/arts.h"
|
#include "game/engine/arts.h"
|
||||||
#include "game/engine/objects.h"
|
#include "game/engine/objects.h"
|
||||||
#include "game/engine/orders.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
|
// The explosion radius of a grenade thrown by `owner` (world units; a
|
||||||
// creature is hit when within it plus its own size).
|
// creature is hit when within it plus its own size).
|
||||||
int32_t ImpactRadius(void* owner) {
|
int32_t ImpactRadiusAt(unsigned level) {
|
||||||
const unsigned level = owner ? game::ArtLevel(owner, game::kArtMoerser) : 0;
|
|
||||||
const int32_t size = TypeSize(kGrenadeImpactType);
|
const int32_t size = TypeSize(kGrenadeImpactType);
|
||||||
return static_cast<int32_t>(static_cast<float>(size) * (1.0f + kRadiusGrowth * (level / kLevelsPerStep)));
|
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),
|
// 0x5ab400 calls 0x5ff820 (thiscall (out, position, type, only, radius),
|
||||||
// ret 0x14) with the effect object in ebx.
|
// ret 0x14) with the effect object in ebx.
|
||||||
__declspec(naked) void GatherTargetsThunk() {
|
__declspec(naked) void GatherTargetsThunk() {
|
||||||
@@ -362,7 +430,10 @@ void Startup() {
|
|||||||
RE_STARTUP(DwarfArtsStartup, Startup);
|
RE_STARTUP(DwarfArtsStartup, Startup);
|
||||||
|
|
||||||
struct Registration {
|
struct Registration {
|
||||||
Registration() { game::OnCreatureOrder(&OnOrder); }
|
Registration() {
|
||||||
|
game::OnCreatureOrder(&OnOrder);
|
||||||
|
game::AddTooltipFilter(game::kArtMoerser, &RewriteTooltip);
|
||||||
|
}
|
||||||
} g_registration;
|
} g_registration;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in new issue
Block a user