The game's list builder runs on a reordered copy of the book: the story, then one section per region (id blocks of 100 grouped by the region their quests lie in, named as the megamap names regions), unknown quests as "???", the rest joined to the nearest area, undiscovered counts. Act tabs hidden, page turning kept. "Erledigte anzeigen" row toggles completed quests ([questbook] ShowCompleted). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
104 lines
6.3 KiB
Markdown
104 lines
6.3 KiB
Markdown
# Quests
|
|
|
|
## Quest data (measured 2026-10-17 / 2026-10-25)
|
|
|
|
- Quest table `g_quests` (vector begin/end `0xaa9e98` / `0xaa9e9c`), 0x124
|
|
bytes per quest: id +0, internal name +4 (ANSI), scripts +0x10c (run on
|
|
trigger), +0x114 (one-time, cleared after it ran), +0x118 success end,
|
|
+0x11c failure end (only quests with one can fail), flags +0x120:
|
|
1 triggered, 2 done, 4 in progress, 8 busy (while the trigger runs its
|
|
scripts). `game/engine/quests.h`.
|
|
- Quest manager: static object `0xaaaf00` (script interpreter). Quest book
|
|
vector +0x424..+0x428, plain 0x174-byte entries (serialized field by field):
|
|
state +4 (2 active, 100 solved, 101 failed), quest id +8, map position
|
|
+0x14..+0x20; per player (16) a selected entry index at +0x3a8 + player * 8.
|
|
- Script opcodes (keyword table `0x452540`): LoseQuest `0x36` and ExitQuest
|
|
`0x0f` -> `0x48f110` -> `Quest_End` `0x46ced0` (thiscall (index, ?,
|
|
success), ret 0xc; LoseQuest success 0, ExitQuest 1). QuestDone `0x0a` tests
|
|
flag 2, QuestInProgress `0x0c` flag 4.
|
|
- `Quest_Trigger` `0x46de10` (cdecl (index, ?, ?, ?), plain ret): does
|
|
nothing if flag 1 is set; else sets 1 | 8, runs +0x114 (then cleared) and
|
|
+0x10c, clears 8. Talking to a giver triggered a quest (flags 0 -> 1, no
|
|
book entry yet); accepting it set flag 4 and added a book entry, state 2.
|
|
- `Quest_End`: if the quest is in progress and not done, runs the end script
|
|
(success / failure) and sets done (flags 3); the book entry's state becomes
|
|
100 / 101. Quests with ids 2600..3599 (the generic "Bringe NPC zu ...",
|
|
"Eskortiere ...", ...) are copied to the end of the table under a new id
|
|
(largest + 1, at least 10000): the game repeats those itself.
|
|
- Compiled quest scripts: `bin\sgf.bin` (labels such as `LoseQuest_10255`,
|
|
texts `res:...`; random side quests use `DQ_` names, time limits
|
|
`DQ_ZEITFENSTER_TAGE`).
|
|
- Example (Gladiator, Silber, 2026-10-25): failed Hetzjagd 1502, Pony Express
|
|
5005, Rette Jungfrau 2504, Eskortiere Händler zu geheimen Treffen 1403, all
|
|
flags 3, book state 101.
|
|
|
|
## Failed quests restartable (request 2026-10-17)
|
|
|
|
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
|
|
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.
|
|
|
|
## Quest book (measured 2026-10-25)
|
|
|
|
- Book entries (quest manager +0x424, 0x174 bytes): kind +0 (3 main quest,
|
|
4 side quest: ids 1..99 are main quests, 0x46c450), state +4, quest id +8,
|
|
title / text ids +0x24 / +0x28, progress log text ids +0x2c.., map position
|
|
+0x18 / +0x1c (0x497830), chapter +0x16c (u16). A new entry (0x46c450,
|
|
thiscall (quest index, ?), ret 8, called by the script interpreter
|
|
0x48f602) copies its chapter from the player's current main quest entry
|
|
(+0x3a4 + slot * 8; selected entry +0x3a8 + slot * 8, read by 0x49dd40).
|
|
- cUI_Book (one per book mode, 0x7a8[mode] of the book window; quest mode
|
|
+0x166 = 0): list builder 0x6b04c0 (thiscall, from the rebuild 0x6b1670),
|
|
page-full check 0x6b1a70 (thiscall (tab, page), ret 8), render 0x6b27f0
|
|
(thiscall (renderer, bookmarks only), ret 8: the left page's rows, then the
|
|
selected row's detail record on the right, 0x6b1cd0 thiscall (renderer,
|
|
record, sub-page), ret 0xc), events 0x6b3620 (thiscall (event), ret 4).
|
|
Current tab +0x15e, page +0x162; per tab pages +0x97c + tab * 0xc (0x1c
|
|
bytes: title text +0, selected row +4, rows +0x10) and detail records
|
|
+0x9d0 + tab * 0xc (0x40 bytes). Row (0x14 bytes): u16 flags (4 spacer,
|
|
0x40 done, 0x80 entry flag 1), title +4, detail record +8, book entry +0xc,
|
|
icon +0x10. Act tabs: chapter text table 0x9de7e4 (9000 "Pfade des
|
|
Schicksals", 9001 "Unter blutigen Bannern", 9002 "Die rastlosen Toten",
|
|
9003 "Zeitenwende"); bookmarks: 7 controls at +0x168, 0xbc apart.
|
|
- The book's list and the quest scripts run on the same thread (logged).
|
|
- Quest table (Gladiator, Silber): 28 main quests, 87 side quests in id
|
|
blocks of 100 (1xx .. 25xx, 2 to 7 each; their accepted quests lie together
|
|
on the map), about 120 other side quests (5xxx, 6xxx, 9xxx, 102xx, 15xxx),
|
|
352 generic ones (2600..3599) and bookkeeping entries (0, 99 "MP-Start",
|
|
5081..5089 "Region 1 befreit").
|
|
|
|
## Quest book as one list (request 2026-10-25)
|
|
|
|
"Show all quests, unknown ones as ???, so a player knows how many quests an
|
|
area holds"; then: "get rid of the tabs ... a section for story, and then a
|
|
section per region", the game's page turning kept. `mods/questbook.cpp`: the
|
|
game's builder runs on a reordered copy of the book (story, then a section
|
|
"<region> (known/all)" with "???" rows, other side quests joined to the
|
|
nearest area, "Unentdeckt" counts), all in the first tab; rows are mapped
|
|
back to the real entries afterwards (ours to none, which clicks ignore). The
|
|
bookmarks are hidden, pages titled "Quests", each page selects its first
|
|
quest, a click on a heading keeps the selection. Counted: side quests whose
|
|
name is unique in the table.
|
|
|
|
Area names (measured 2026-10-25): an area is an id block (1xx .. 25xx); its
|
|
name is the game's name of the region most of its known quests lie in, as the
|
|
megamap names the hero's region: book cell +0x18/+0x1c (x 53.66563 = world
|
|
units) -> `World_SectorAt` 0x634970 -> `Sector_Region` 0x622e30 ->
|
|
`Region_NameKey` 0x425370 -> text (`game/engine/regions.h`). Several blocks
|
|
lie in one named region (regions 1, 2, 3 are all `UI_REGION_SOUTHKERN`), so
|
|
blocks with the same name make one section; blocks without a known position
|
|
are "Gebiet N".
|
|
|
|
"Erledigte anzeigen" (request 2026-10-25: "some kind of 'Show completed'
|
|
checkmark. If unchecked, completed quests are not in the list (as well as
|
|
completed regions)"): the list's first row, an own entry whose state draws the
|
|
solved mark (100) when on and no icon (0) when off; its row carries the marker
|
|
entry 0xfffffffe. A click toggles it (`sacredK.ini` `[questbook]
|
|
ShowCompleted`, default 1) and calls `cUI_Book::Rebuild` 0x6b1670 (thiscall,
|
|
no arguments; keeps tab and page if the page still exists, else ours clamps
|
|
it). Off leaves out solved entries (main and side) and areas whose quests are
|
|
all known and solved; failed quests stay (restartable).
|