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) <noreply@anthropic.com>
This commit is contained in:
1 parent
2cab6c216d
commit
7d8089c299
11 files changed
+872
-18
No files matched your search
+87
-2
@@ -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:<script id>", type, position, 0x09 dialog,
|
||||
0x05 death label), 3 NPC mode (0x09 dialog, 0x0a follow + death label),
|
||||
4 trigger area, 0x0f end quest, 0x14 start quest (#id), 0x17 named
|
||||
position, 0x1a dialog text, 0x35 book text, 0x3a / 0x42 if / else if
|
||||
(0x49 var == n, 0x40 "@npc"), 0x3c dialog answer (text, label), 0x3f
|
||||
compass, 0x44 set variable, 0x48 move NPC, 0x56 dialog state.
|
||||
- Script NPC names: "Res:<n>" is a script id (object +0x3c, `0x44a390`); the
|
||||
quest manager maps it to the object (`0x464ec0`, map +0x7668); a spawn sets
|
||||
the entry.
|
||||
- Example "Jagd auf die Wilderer" (1501): `Region15Init` spawns the elf
|
||||
(Res:17013, type 134, dialog auftrag1501); accepting (`trigger1501`: 0x14)
|
||||
runs `QIS_OnEnter1501`: poachers with death label `tot1501`, the elf follows
|
||||
with death label `ex1501` (variable "1501" = 1, then `tot1501`); `tot1501`
|
||||
once all poachers are dead: variable 0 -> back to the elf, 1 -> quest ended
|
||||
without the reward. "Hetzjagd" (1502): the daughter (Res:17015, the giver)
|
||||
is spawned only when the hero walks into trigger area `hetz1_1502`, with
|
||||
three cultists; her father (Res:17016) only asks for her.
|
||||
|
||||
## Escorts on the follower list (request 2026-10-26)
|
||||
|
||||
"Furchtbares Verbrechen" (9008) did not go on at its target point: the
|
||||
script's test there ("@npc", `0x498aa0` case 0x40) looks the NPC up in the
|
||||
hero's follower list (+0x39c, 9-byte entries), which was empty after a load,
|
||||
while the NPC still had its leader (+0x251) and follow bit (+0x1f4: 4 for the
|
||||
follow order, 0x100 for script escorts). `mods/followers.cpp` adds such NPCs
|
||||
back with `cCreature::AddFollower` `0x54b070` once after loading. Town NPCs
|
||||
that run after the hero for a few seconds (status 0x404) are left out.
|
||||
Confirmed 2026-10-26.
|
||||
|
||||
## Quest givers come back (request 2026-10-26)
|
||||
|
||||
"Change the death script to spawn her back to her starting point", and "on
|
||||
load we need a repair ... if not completed or correctly running".
|
||||
`mods/questgiver.cpp`: a quest's giver is the NPC whose spawn instruction
|
||||
carries a dialog with an answer that accepts the quest (85 givers). When a
|
||||
label of a running quest runs for its dead giver, it is skipped, the quest
|
||||
restarts (failed entry at the giver, the variables only its labels set back to
|
||||
0), and once the body is gone the spawn instruction runs again. After a load,
|
||||
an accepted quest (flag 4) without its giver gets the same. Not yet seen in
|
||||
the game (the escort of "Hetzjagd" arrived alive); see `docs/todo.md` for the
|
||||
audit of how quests can fail.
|
||||
|
||||
Lesson (2026-10-26): flag 1 means available, not running; a first version
|
||||
took it for running and reset 16 available quests and spawned their NPCs on
|
||||
Borf (repaired once, then saved).
|
||||
Reference in new issue
Block a user