From 7d8089c2995217072df0a493f2ae062876126a14 Mon Sep 17 00:00:00 2001 From: Torhan Bartel Date: Mon, 26 Oct 2026 02:30:41 +0100 Subject: [PATCH] Quests: escorts stay on the follower list, quest givers come back Escorts lost their place on the hero's follower list after a load, so quest checks like the one in "Furchtbares Verbrechen" failed; they are added back once after loading (town NPCs that tag along briefly are left out). Restarted quests stay available (flag 1), which their giver's dialog and accepting them need. Quest givers are read from the quest scripts: when a running quest's giver dies, the quest restarts and the giver is spawned again at her start; an accepted quest without its giver is repaired on load. Notes on the script format and opcodes in docs/quests.md. Co-Authored-By: Claude Opus 5.5 (1M context) --- CMakeLists.txt | 2 + docs/quests.md | 89 +++++++++- docs/todo.md | 33 ++++ src/game/engine/objects.h | 12 ++ src/game/engine/orders.h | 41 +++++ src/game/engine/quests.h | 66 ++++++++ src/mods/followers.cpp | 73 ++++++++ src/mods/questfail.cpp | 206 +++++++++++++++++++++-- src/mods/questfail.h | 10 ++ src/mods/questgiver.cpp | 340 ++++++++++++++++++++++++++++++++++++++ tools/names.txt | 18 ++ 11 files changed, 872 insertions(+), 18 deletions(-) create mode 100644 src/mods/followers.cpp create mode 100644 src/mods/questfail.h create mode 100644 src/mods/questgiver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7731227..f60cddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,9 @@ add_library(sacredK SHARED src/mods/mainmenu.cpp src/mods/mapreveal.cpp src/mods/questbook.cpp + src/mods/followers.cpp src/mods/questfail.cpp + src/mods/questgiver.cpp src/mods/questnpc.cpp src/mods/anticheat.cpp src/mods/loadingscreen.cpp diff --git a/docs/quests.md b/docs/quests.md index 84938e3..4ebbbc7 100644 --- a/docs/quests.md +++ b/docs/quests.md @@ -36,11 +36,38 @@ A failed quest is not final: talking to the quest giver again offers it again. `mods/questfail.cpp`: after `Quest_End` with success 0 (its failure -script has run) the quest's flags 1/2/4/8 are cleared and its book entries -removed, as if never triggered; quests failed before the mod are reset once +script has run) the quest's flags 2/4/8 are cleared and flag 1 is kept +(book entry: see below); quests failed before the mod are reset once after loading. Confirmed in the log (the four quests above reset, 2026-10-25); the giver offering them again is still to be tested in the game. +2026-10-26: the restart first cleared flag 1 as well. Measured: a quest's +script labels, its giver's dialog too, run only while flag 1 is set +(Script_RunLabel `0x46bf11`; "Dialog faild" in the game log otherwise), and +accepting it (opcode 0x14, `0x48f320`) needs it too; the game sets it when the +hero enters the quest's region (`Quest_Trigger`). A restarted quest is now +"available": flags 1. + +Marker back to the giver (request 2026-10-25: "quests that failed, should +have the blue marker point back to the quest giver"): + +- Measured: the maps draw a marker for every book entry at its compass cell + +0x18/+0x1c, colour from `0x4975d0` (thiscall (entry, kind), ret 8): open + side quest `ff006cff` (blue), main `ffffff80`, solved green, failed red. + The compass target +0x10/+0x14 is set by the scripts' QuestkompassPos + (`0x49a7a0`: a cell, radius +0x20 "rad:") and QuestkompassOBJ + (`0x49af70`: 0xffffffff + object id, or 0xeeeeeeee + script NPC id); + `0x4a6740` resolves it into +0x18/+0x1c. The game keeps no giver per quest. +- A quest is accepted in a dialog answer (`cDialogManager::Answer` 0x49a090 -> + quest script -> `QuestManager_AddBookEntry` 0x46c450). The mod keeps the map + cell of the NPC nearest to the hero then (within 400) as the giver, saved + with the hero data (`questGivers`: quest id -> cell, merged from save game + and hero file; quest ids and givers are the same in every world). +- On failure the book entry stays (state 101) with its compass on the giver + and is drawn blue; without a known giver it is removed as before (all + quests that failed before this change). Accepting the quest again removes + the failed entry. + ## Quest book (measured 2026-10-25) - Book entries (quest manager +0x424, 0x174 bytes): kind +0 (3 main quest, @@ -151,3 +178,61 @@ exists, is done (flag 0x2) and not in progress (0x4): Dialogs without a quest (healers, closed shops: `geschlossen101`, `heilen_global`) stay as the game has them. + +## Quest scripts (measured 2026-10-26) + +- Code: `binNetScriptFunkCode.bin` (`binAddonNetScript` differs only past + 0x9a653), open as the quest manager's FILE* +4 (`0xaaaf04`). Instructions + {u16 opcode, u16 size with the header, args}; args tagged: 0x01 string, + 0x0b int32, 0x09 dialog, 0x05 label (e.g. a death label), ... Jump table of + the executor `0x475970` at `0x4787b0` (opcode * 4). +- Labels: the script label table (`0xaa9688`, 0x54 bytes; from + `Vectoren.bin`): name, +0x40 code offset, +0x44 code size, +0x48 quest. + `Script_RunLabel` `0x46bd80` (cdecl (label, object, flag)) reads the code + and runs it with `0x475970` (quest manager thiscall (code, &offset, quest, + object, flag, 0), ret 0x18; any memory buffer with offset 0 works). +- Opcodes seen: 1 spawn ("Res: