From 4018e23e6b36ae576863d07e38e295eb78a238c4 Mon Sep 17 00:00:00 2001 From: Torhan Bartel Date: Sun, 18 Oct 2026 03:26:10 +0200 Subject: [PATCH] Docs: how the survival bonus is calculated and stored Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/survival-bonus.md | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/survival-bonus.md diff --git a/docs/survival-bonus.md b/docs/survival-bonus.md new file mode 100644 index 0000000..86b4ae2 --- /dev/null +++ b/docs/survival-bonus.md @@ -0,0 +1,50 @@ +# Survival bonus (Überlebensbonus) + +Request (2026-10-18): "how is survival bonus calculated? please reset the +death counter on battlemage savegame and restore the survivability bonus". + +## Where it shows + +The tooltip of the Waffe / Kampfkunst / Resistenz block in the character +window (`cUI_StatisticsChar::Render` 0x6a9be0), only while the bonus is above +0. Text key "1159" (id 0x4407730). Text keys are numbers or names hashed by +`TextKeyHash` 0x80e010: `h = (h * 113 + toupper(c)) % 999999991`, then +`& 0x7fffffff`. `TextManager::GetTextByKey` 0x672fd0 looks them up. + +## Formula + +`cStatsManager` (one record at 0xaa3518, `cStatsManager::Instance` 0x425160) +keeps the time survived in ms at +0x570c. With m = whole minutes survived: + + bonus % = 0 for m < 1 + = 100 * (1 - 1 / ((m - 1) / 120 + 1)) otherwise + +That is `DiminishingCurve` 0x565c30 (min 0, half 120, max 50, doubled). It +gives 50 % after 2 h and 75 % after 6 h, approaching 100 %. The experience for +a kill uses the same curve (0x534454), scaled by a global factor at 0xad362c. +The window adds 10 × a difficulty factor (1.34 / 1.67 / 2 / 2.34) when a hero +byte (+0x4f8 array, index 0x1c) is at least 6; what that byte means is not +known yet. + +Measured: the survival time only grows in combat. `cStatsManager::AddTime` +(0x424930, (float s, alive), ret 8) is called from the engine tick (0x60e732) +with alive = hero +0x200 bit 0x8000. 0x547ce6 sets that bit while 0xad1c64 is +below 350 (or 1000), and 0x547ed8 clears it otherwise. 0xad1c64 is most +likely the distance to the nearest enemy. The play time (+0x56f8 h, +0x56fc +min, +0x5700 ms) always grows. + +A death (`cStatsManager::OnHeroDeath` 0x4245a0, called from +`cCreature::ReceiveOrder`) counts one at +0x56f4 and zeroes the survival time. + +## In the save game (GAME0x.PAK, uncompressed) + +- Header ("AMS", version 0x1b) at the start: survival time in seconds at + 0x44, loaded by `cEngine::load` 0x61b790 through + `cStatsManager::SetSurvivalSeconds` 0x424540. +- The statistics block (`cStatsManager::Serialize` 0x424ce0) ends with the + magic 0xF00DF00D. The deaths are 26 bytes before the magic, the play + hours 22, the minutes 18 and the ms 14. +- `ItemDataMgr::saveHero` 0x6046f0 appends a 0x22c-byte hero header (name in + UTF-16 at +0x48) and a 0x40-byte block (deaths, survival seconds, spawn + counter). The block is all zeros in single-player saves and is not where + the survival time is loaded from.