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).
|
||||
@@ -42,6 +42,15 @@ Known so far: the periodic-hit aura (effect kind 0x12) uses
|
||||
mapped yet. Other arts' ranges are not mapped. Fits the data-driven arts
|
||||
item above (range as a property of an art, one place to compute it).
|
||||
|
||||
Mörsergranate impact indicator (asked 2026-10-25, while reworking the dwarf's
|
||||
Kanonenschuss / Mörsergranate): show exactly where the grenade will land and
|
||||
its explosion radius, a red circle with low alpha on the ground. The landing
|
||||
point seems to be decided at launch (cCreature combat move code `0x5145d0`,
|
||||
case 0x43e: the target point, changed by a random offset when closer than
|
||||
200; passed as the position of the grenade's cEventFX 0x96), so the circle
|
||||
could be drawn from the launch until the explosion; to be confirmed with the
|
||||
diagnostic log. To be built after the radius-per-level change.
|
||||
|
||||
## Rename life leech to "omnivamp"
|
||||
|
||||
Asked 2026-10-15: life leech (property 0x349, "Lebensentzug") becomes
|
||||
@@ -231,6 +240,30 @@ turn towards the player; once their quest is finished they should stop
|
||||
turning and be drawn blue (like other NPCs). Done 2026-10-25
|
||||
(docs/quests.md, "Quest NPCs after the quest").
|
||||
|
||||
## Audit how every quest can fail (2026-10-26)
|
||||
|
||||
Request: "at some point we need to check ALL the quests and how they possibly
|
||||
can fail". Context: "Jagd auf die Wilderer" (1501) - the giver's death label
|
||||
`ex1501` sets the quest variable and runs `tot1501`, which ends the quest as
|
||||
solved without the reward once the poachers are dead; her survival is not
|
||||
needed for the quest itself. `src/mods/questgiver.cpp` currently skips a dead
|
||||
giver's label, restarts the quest and respawns the giver. Plan: from
|
||||
FunkCode.bin (label table in Vectoren.bin, 0x54-byte records: name, +0x40
|
||||
code offset, +0x44 size, +0x48 quest; instructions {u16 op, u16 size, args})
|
||||
list per quest: death labels given to NPCs (spawn / follow instructions with
|
||||
a 0x05 label), the labels that end it (opcode 0x0f) or lose it, and which
|
||||
NPCs must survive (escorts). Then decide per kind: respawn the giver only,
|
||||
or restart the quest.
|
||||
|
||||
## Quest giver death rule untested (2026-10-26)
|
||||
|
||||
`src/mods/questgiver.cpp` restarts a running quest whose giver died and
|
||||
spawns the giver again at her start (docs/quests.md, "Quest givers come
|
||||
back"). Built and checked against the scripts, not yet seen in the game: test
|
||||
with an escort whose giver dies (e.g. "Jagd auf die Wilderer" on the
|
||||
Gladiator, or "Hetzjagd"). Borf's case (the quest ended without the reward)
|
||||
was repaired once by hand.
|
||||
|
||||
## Inventory drawn without its dark backgrounds (2026-10-25, intermittent)
|
||||
|
||||
Reported twice: on opening the inventory the item list had no dark
|
||||
|
||||
Reference in new issue
Block a user