diff --git a/CMakeLists.txt b/CMakeLists.txt
index 701b4b1..b23fc08 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -84,6 +84,7 @@ add_library(sacredK SHARED
src/mods/questgiver.cpp
src/mods/questitems.cpp
src/mods/questnpc.cpp
+ src/mods/questscript.cpp
src/mods/anticheat.cpp
src/mods/loadingscreen.cpp
)
diff --git a/docs/quest-audit.md b/docs/quest-audit.md
new file mode 100644
index 0000000..4ef9cfc
--- /dev/null
+++ b/docs/quest-audit.md
@@ -0,0 +1,278 @@
+# Quest audit: how quests fail or get stuck (2026-10-30)
+
+Request (2026-10-26, started 2026-10-30): "iterate through all quests in the
+game, and check their conditions for potential failing or stuck up points
+given the current implementation and our attempted reset". Done one quest at
+a time from the compiled scripts with `tools/questscript.mjs` (full dump:
+`
`; compact: ` --facts `).
+
+Scripts: Ancaria per hero class `bin\TYPE_NPC_\` (the game writes the
+one in use to `bin\sgf.bin`; this audit reads `TYPE_NPC_GLADIATOR`, the class
+variants differ in a few class quests), Underworld `bin\Addon\NetScriptCamp\`.
+Ancaria: 603 quests in the table (87 + 13 side quests, ~37 main quests, 352
+generic `DQ_` quests from 16 templates, multiplayer / unnamed sets);
+Underworld: 190.
+
+## What our mods do (the "reset")
+
+- **M1 failure restart** (`questfail.cpp`, `Quest_End` hook): after
+ `LoseQuest` ran the quest's failure script (`QIS_OnLose`), the flags go
+ back to available (1), the book entry stays failed with its compass on the
+ giver. Quest variables and NPC states are left as they are.
+- **M2 giver death** (`questgiver.cpp`): a label of a running quest run for
+ its dead giver (the giver: the NPC spawned with a dialog that has the accept
+ button) is skipped, unless the giver is hostile to the hero (2026-10-30);
+ the quest restarts (M1 plus the variables only this quest writes: 0, the
+ one named after the quest: 1) and the giver's spawn instruction runs again
+ once the body is gone.
+- **M3 load repair** (`questgiver.cpp`): a running quest whose giver is not
+ alive after loading gets M2, unless the quest morphs its giver.
+- **M4 followers** (`followers.cpp`): escorts back on the hero's follower
+ list after loading; followers more than 40 cells away are teleported to the
+ hero.
+
+## General findings
+
+- **G1 M1 keeps the quest variables.** Givers' dialogs choose their branch by
+ bits of the quest variable (`IF 49(bit, "")`); bits set while the quest
+ ran stay set after a failure, so the giver shows a "still running" branch
+ without the accept button and the quest can never be taken again. Example
+ 702: the girl's death runs `lose702`; variable 702 keeps bit 1, the giver
+ answers "Qoffen01" with `ok702`. Fix: M1 resets the quest's own variables
+ like M2.
+- **G2 generic quests (2600..3599)** are the game's random quest pool;
+ `Quest_End` already copies them under a new id. M1 should leave them (and
+ their runtime copies) to the game.
+- **G3 one-time world progress.** Many targets are spawned once by the start
+ script or `RegionInit`, not by the quest. When M2/M3 reset the quest's
+ variables after such a target died, the progress is lost and the target
+ never comes back: stuck. Either keep the variables on M2/M3 or respawn the
+ quest's one-time NPCs (see G6).
+- **G4 duplicates on re-accept.** `QIS_OnEnter` creates the quest's NPCs and
+ objects again; after a failure the old ones may still be alive.
+- **G5 givers deleted by the script** (`DelNPC` on the giver): M3 restarts a
+ quest whose giver is gone on purpose.
+- **G6 restart without the NPCs.** The restart is pointless when the NPCs the
+ quest needs are one-time spawns and dead or deleted. Idea: respawn them from
+ their spawn instructions like the giver.
+- **G7 quests without a giver** (trigger areas): M1 "restarts" them and erases
+ the failed book entry.
+- **G8 givers turned hostile by `SetNPCState ... 08 12`** instead of `Morph`
+ are not in `g_giverTurns`; M3 restarts those quests wrongly, M1 points the
+ book at an enemy.
+- **G9 shared variables.** M2 doesn't reset quest variables another quest's
+ label also writes, so the respawned giver shows a running branch.
+- **G10 giver killed after the quest ended** (by its own script): M2 doesn't
+ act, the giver stays dead.
+- **G11 failure continues a chain.** Some failures are part of a quest chain
+ (`LoseQuest` together with `SetUpQuest` of the next quest, giver's dialog
+ moved on); M1 must not restart those.
+- **G12 state in dialogs (Underworld).** Underworld quests keep their state
+ in the NPCs' dialogs; a failure sets a "done/lost" dialog (or none), and M1
+ doesn't put the accept dialog back (7614, 10260).
+- **G13 named ids.** Spawns with named ids (`res:AUFTRAGGEBER_10254`,
+ `res:Sam1_Knappe`) or the id after the type argument aren't read by
+ `InstructionScriptId`; those givers aren't in `g_givers`.
+- **V1/V2 vanilla bugs:** 1101's time-out never ends the quest; Res:17163 is
+ used by both 602 and 1202, and 602 deletes 1202's patient.
+
+## Per quest
+
+Columns: how it can fail (vanilla), what happens with our mods, verdict
+(ok / G / finding).
+
+| Quest | Fails by | With our mods | Verdict |
+|---|---|---|---|
+| 151 Tierischer Wilddieb | never (no LoseQuest); wolf Res:17081 (start script, death `stirb151`: bit 2, triggers the quest) | M2/M3 on giver death: variable 151 reset to 1; if the wolf is already dead, bit 2 is lost and the wolf never respawns: stuck | G3 |
+| 152 Botendienst | time limit (`SetTimer` 4320 -> `zeit152`: bit 4, `LoseQuest`) | M1: bits 1 + 4 stay, the giver answers "Qfail" / "Qpostfail" without the accept button: stuck. With G1 reset: offered again; re-accept spawns the parcel (Res:6027) and later the recipient Res:17077 again (the old recipient stays, dialog off) | G1, G4 |
+| 154 verschwundener Schmied | never | the script deletes the giver Res:17626 (`talkedbride154`, `take154`): after the next load M3 restarts the running quest and spawns the giver again | G5 |
+| 201 Vermisste Tochter nach Ausritt | daughter Res:18006 dies (`Tochtertot201`: bit 8, `LoseQuest`); she is spawned once (`Region2Init`) | M1: bit 8 stays, giver answers `okt201` then `ok201`: stuck. With G1: offered again, but the daughter stays dead: cannot be finished | G1, G6 |
+| 202 Eskorte für Händler | merchant Res:18012 (the giver) and bodyguard Res:18013 both dead, or the bodyguard alone at the bridge | the merchant's death is M2 (restart, respawn; his dialog has no bit tests); the bodyguard is not needed | ok |
+| 203 Schatzkarte | never | giver death -> M2; re-accept creates map + chest again (old chest stays) | ok (G4) |
+| 204 Die Bücher der Gelehrigkeit | never | giver death -> M2; the boss and the book come from `QIS_OnEnter204`, so a restart can be redone | ok |
+| 301 Orks auf Bauernhof | time limit (`SetTimer` 1440 -> `lose301`); farmer Res:17033 and daughter Res:17032 both dead | no giver (trigger area, deleted on start); `QIS_OnLose301` deletes both NPCs. M1 makes it available but nothing can start it again, and with no known giver its failed book entry is erased | G7 |
+| 302 Von Räubern und Rindern | six cattle dead (`302a` = 6, `LoseQuest`); the giver Res:17029 (escorted) dies (`tot1_302`) | giver death -> M2 (but `302a` is also set by `startcode`, so it is not "only this quest's" and keeps its count). Cattle failure -> M1: the dialog's `48(6,"302a")` branch sets bit 3 -> "ok" forever: stuck, also with G1 unless G1 counts start-script initialisation as the quest's own. Re-accept spawns the six cattle again | G1 (start-script variables), G4 |
+| 303 Bodycount Orks | never; ogre Res:17031 is a one-time spawn (`Region3Init`, death `tot303`: bit 2) | as 151: giver death resets bit 2, a dead ogre never returns: stuck | G3 |
+| 401 Jagd auf wildernde Wölfe | never; wolves one-time (`Region4Init`, death `stirb401`: bit 2) | as 151 | G3 |
+| 402 Kräuter für Kräuterhexe | never | herbs from a permanent `MouseEvent` (`Region4Init`), can be picked again after M2 | ok |
+| 403 Kühe vor Wölfen retten | all cows dead (bit 4), then the giver's answer runs `lose403` | M1: bits 4 + 5 stay, giver "ok403" forever: stuck. With G1: offered again, but the cows (`Region4Init`) are gone | G1, G6 |
+| 501 Geister auf Landsitz | never by `LoseQuest`; choice `change501` / `nochange501` | `postchange501` turns giver Res:17071 and Res:17070 hostile with `SetNPCState ... 08 12` (not Morph): M2 sees the hostility at death, but M3 after a load would restart the quest (only Morph is known as "turns") | G8 |
+| 502 Verstecktes Waffenlager | never | enemies and boss from `QIS_OnEnter502` (variant by `Globalchange1`): redoable after M2 | ok |
+| 503 Botschaft an Geliebte | never | giver Res:17105's death label `weg503` belongs to no quest (M2 does not react); not needed: the quest ends at the lover Res:17104 | ok |
+| 601 Kunstraub von Händler | never | thief Res:17135 gives the item by dialog; redoable after M2 | ok |
+| 602 Wegelagerer | never | the knight Res:17136 is beaten, not killed (`ritter602` sets his HP back); after M2 his dialog offers the fight again | ok |
+| 603 Entlaufener Bräutigam | never | the groom Res:17140 is beaten (`braut603` heals him), the fight can be repeated after M2 | ok |
+| 701 Vergifteter Brunnen | never (`lose701` is not called) | boss Res:17063 one-time (`Region7Init`, drops the items); M2 resets bit 20 (item taken): stuck if the boss is dead | G3 |
+| 702 Mädchen bei Ausritt verschwunden ("Familienbande") | the girl Res:17060 dies (`lose702`) | giver turned hostile and killed: handled (7ec0fa2). Girl's death -> M1: bit 1 stays, giver "Qoffen01": stuck; with G1 redoable (the girl comes from `QIS_OnEnter702`) | G1 |
+| 703 Objektschutz Wegbrücke | leaving the bridge area / region before the bandits are dead (`zeit703`: bit 4), then the giver's answer runs `lose703` | M1: bits 4 + 5 stay, giver "ok703": stuck. With G1: redoable (bandits not all dead, else the quest would have been won) | G1 |
+| 801 Holzfällerstreit mit den Elfen | the giver Res:17144 dies (`tod801` -> `lose801`) | giver death -> M2 (restart, respawn) instead of the failure | ok |
+| 802 Königliche Jäger Verschollen | never | hunters, herbs and amulet are one-time (`Region8Init`); M2 resets 802 (progress bits): stuck when they are used up | G3 |
+| 803 Kopfgeldjagd | never | targets one-time (`Region8Init`). On giver death M2 does not reset `803` (quest 801's elf dialog also sets a bit in it), so the respawned giver shows the running branch without accept and the flags say "available": stuck | G3, G9 |
+| 901 Soldaten verschleppen Frauen | time limit (`zeit901`, also kills quest 902's women); the women Res:17051..17054 die (`lose901`: bit 5), then the giver's answer runs `lose901` | M1: bit 6 stays, giver "ok901": stuck. With G1: redoable after the time limit; after the women died they are gone (`Region9Init`) | G1, G6 |
+| 902 Rache an Reichem Händler | never | boss Res:17049 one-time (`Region9Init`, death `tot902`: bit 2) | G3 |
+| 903 Robin Hood | never | Res:17045 is beaten and healed (`re903`), the choice can be made again after M2 | ok |
+| 1001 Übergriffe auf die Bevölkerung | never | boss Res:17041 one-time (`Region10Init`, drops the proof) | G3 |
+| 1002 Spionage | seen by the spy's group (tag 0x60 `lose1002` on the NPCs from `QIS_OnEnter1002`: bit 4, `LoseQuest`) | M1: giver sets bit 5, "ok" forever: stuck. With G1 redoable; the old group (now hostile) stays next to the new one | G1, G4 |
+| 1003 Schlägertypen | the giver Res:17034 dies (`verreckt1003`); seven villagers die (bit 4), then the giver's answer runs `lose1003` | giver death -> M2. Villagers -> M1: bit 5, "ok" forever: stuck. With G1 redoable (the attackers come from the accept trigger again) | G1 |
+| 1101 Sektenpriester opfern Jungfrauen | the virgin Res:17112 dies (`Verloren1101` -> `lose11a1`); time limit (`Zeitum1101`: demon instead of the virgin, bit 3) | death -> M1: giver sets bit 5, "ok": stuck; with G1 redoable (all from `QIS_OnEnter1101`, old priests stay). Time limit: vanilla never ends the quest (the giver's bit 3 branch has no `LoseQuest`): running forever | G1, V1 |
+| 1102 Kompromittierende Situatuion | time limit (`Zeitum1102`: bit 3, `LoseQuest`) | M1: bit 1 stays, giver "Ok1102": stuck; with G1 redoable (the lady and the letter come from `QIS_OnEnter1102`). After the time limit the lady's bit 3 answer kills the giver (`SetHP 0`); the quest is no longer running then, so M2 does not respawn him | G1, G10 |
+| 1103 Verhandelnder Räuber | never | the robber Res:17118 is beaten and healed (`tot1103`) | ok |
+| 1111 DeModreys Steuerpresser | the escorted Res:17121 dies (`lose1111` -> `geld1111`: bit 5, `LoseQuest`) | M1: giver sets bit 6, "ok": stuck. With G1: the boss Res:17123 is one-time (`Region111Init`) and `geld1111` deleted his proof Res:6051: cannot be finished | G1, G6 |
+| 1112 Animagus terrorisiert Bauernhof | the spared Animagus Res:17127 dies (`ex1112` with bit 4: `LoseQuest`) | M1: bit 4 stays, the giver's answer then runs `postchange1112` (turns the giver hostile) instead of an offer. With G1: the Animagus is one-time (`Region111Init`). `postchange1112` turns the giver hostile by `SetNPCState` (M3 after a load: G8) | G1, G6, G8 |
+| 1113 Elfischer Kunstraub | never | thief Res:17129 and the goal object Res:6052 one-time (`Region111Init`) | G3 |
+| 1201 Sekte beschwört kleine Dämonen | never | cult one-time (`Region13Init`, group dead: bit 2) | G3 |
+| 1202 Heilmittel für Händler | time limit (`zeit1202` kills the patient Res:17163, `LoseQuest`) | M1: giver sets bit 5, "ok": stuck. With G1 the patient (`Region13Init`) is dead: cannot be finished. Vanilla: quest 602's `ok1_602` / `ritterweg_602` walk away and delete `res:17163` too (the same id): finishing 602 removes 1202's patient | G1, G6, V2 |
+| 1203 Der Richter und sein Henker | never | judge Res:17166 and executioner Res:17167 one-time (`Region13Init`) | G3 |
+| 1301 Entlaufener Gladiatoren-Minotaurus | never | the giver's death label is the quest's end (`exit1301`): `attack1301` turns him hostile (M2 lets it run since 7ec0fa2), the quest ends at once (no M3). Minotaur from `QIS_OnEnter1301` | ok |
+| 1302 Das Soldaten-Tunier | timer → `lose1302` | after M1 bits 4/5 and `1302a` stay (G1); opponent Res:17173 is deleted by `attacke1302_1` and created only by region init (G6). Vanilla typos: `DelNPC res17173`/`res17174` (no colon), `attacke1302_2` attacks 17174 | G1 + G6 |
+| 1303 Händler - legaler Warentransport | timer → `lose1303` | after M1 bits 3/4 stay (G1); re-accept creates recipient Res:17177 again (G4) | G1 + G4 |
+| 1401 Händler - Warenschmuggel | the side chosen dies (`lose1401`) | after M1 bits 1-6 and `1401a`/`1401b` stay (G1); re-accept creates Res:17180/17181 again (G4). Vanilla: soldier's death label `attack1401` doesn't exist (`attacke1401`) | G1 + G4 |
+| 1402 Junge Dame vor Übergriffen eines Offiziers schützen | never | officer and soldiers from `QIS_OnEnter1402` | ok |
+| 1403 Eskortiere Händler zu geheimen Treffen | recipient after the escorted giver died (`losequest1403`) | giver death is caught by M2 first (restart, giver back); re-accept creates recipient Res:17187 again (G4). Vanilla typo `ok1024` | G4 |
+| 1404 Eskortiere Dame zu Treffen mit Geliebten | the lady dies (`lose1404`) | after M1 bits 1/4 stay (G1); lady Res:17189 and boss Res:17190 are one-time (region init), dead lady makes a restart pointless (G6) | G1 + G6 |
+| 1405 Sekte von Dämonenanbetern | never | sect leader Res:17195, cultists and the three captives are one-time (region init); M2 reset after the leader died loses the progress | G3 |
+| 1501 Menschlicher Wilderer | never | the giver follows; vanilla his death (`ex1501`) lets the quest go on, M2 restarts it and brings him back instead. Re-accept creates the poachers again (G4) | M2 changes vanilla + G4 |
+| 1502 Hetzjagd | a label deletes the giver Res:17015, then `losequest1502` | the giver is spawned once by trigger area `hetz1_1502` (deleted after use); after M1 the book points at a deleted giver and nothing spawns him again (G6). Vanilla: his death label `lose1502` doesn't exist | G6 |
+| 1503 Arak-Attack | never | spiders and the target from `QIS_OnEnter1503` | ok |
+| 1601 Kunstraub | never | thief from `QIS_OnEnter1601` | ok |
+| 1602 Attentäter abfangen | the target dies (`target1602`) | after M1 bit 4 stays (G1); target Res:17205 and assassin Res:17201 are one-time (region init), the target is dead (G6) | G1 + G6 |
+| 1603 Blockadebrecher | never | | ok |
+| 1701 Getrennte Liebende | the lover dies (`lose1701`); afterwards the giver turns hostile and fails it again (`ende1701`) | after M1 bit 6 stays, the giver's dialog runs `ende1701` (G1); lover Res:17209 one-time (G6); `ende1701` makes the giver hostile by `SetNPCState 08 12`, M1 then points at an enemy (G8) | G1 + G6 + G8 |
+| 1702 Korruption | never | official Res:17215 one-time (region init) | G3 |
+| 1703 Unschuldiger am Galgen | judge after the time-out (`lose1703`) | after M1 bits 6/7 stay (G1); victim Res:17218 and boss Res:17221 are deleted by `zeit1703`, the victim is one-time (G6). `lose1703` kills the giver by `SetHP 0` on purpose; M2 skips his death label `less1703` and brings him back | G1 + G6, M2 changes vanilla |
+| 1801 Monströsere Wilderer | never | poachers one-time (region init) | G3 |
+| 1802 Dunkle Priester beschwören Dämon | never | priests from `QIS_OnEnter1802` | ok |
+| 1803 Dunkelelfengift | time-out (`zeitum1803`) | after M1 bit 4 stays, the giver's dialog runs `losequest1803` again (G1); boss Res:17229 one-time (region init), once killed the poison can't drop again (G6) | G1 + G6 |
+| 1901 Rache an bösem Anführer in Dungeon | never | boss Res:17232 one-time (region init) | G3 |
+| 1902 Monster haben Item verschleppt in Dungeon | never | monster Res:17020 one-time (region init) | G3 |
+| 1903 Entlaufene 'Tochter' | the girl dies (`lose1903`) | after M1 bit 5 stays, the giver's dialog morphs him into the monster at once (G1); girl Res:17237 one-time (G6) | G1 + G6 |
+| 2001 Untote Kühe auf Wiesen | never | boss Res:17239 spawns on the 4th trigger area; `2001a` starts at 1 from the start script, a reset to 0 matches no branch of `move2001` and the boss never comes (G1 start-script variable); trigger bits in `2001` | G3 (start-script variable) |
+| 2002 Kämpfe gegen Wilde Monster | never | repeatable arena, waves from `monster2_2002` | ok |
+| 2003 Anführer der Dämonensekte | never | the giver is the cult leader: `QIS_OnEnter2003` makes him hostile by `SetNPCState 08 12` (G8, M3 would restart). His death runs (hostile, 7ec0fa2). Vanilla typo `res:174242` | G8 |
+| 2101 Hands of my Dragon | never | chain with 2102/2103 (and 2303's egg) on dragon Res:17245 (one-time); prince Res:17246 and his guards from `QIS_OnEnter2101`. Shared variables `riesendiamant`/`drachenei` are written by several of these quests (G9 if one is restarted) | ok |
+| 2102 Drache - Zahnstocher | never | prince is the giver; boss Res:17249 from `QIS_OnEnter2102` | ok |
+| 2103 Return for Refill | never | knights created one after the other by `ziel2103` | ok |
+| 2201 Kuss der Spinnefrau | the girl dies (`lose2201`) | after M1 bits stay (G1); girl Res:17283 one-time (G6); `postchange2201` turns the giver Res:17281 hostile by `SetNPCState 08 12` (G8) | G1 + G6 + G8 |
+| 2202 Narzisstische Zauberin | never | `change2202` turns the giver Res:17255 hostile by `SetNPCState 08 12` (G8) | G8 |
+| 2203 Hinterhältiger Giftmischer | never | poisoner Res:17260 and the six barrels (counter `2303a`) are one-time (region init) | G3 |
+| 2301 Besorge Reagentium | never | bush is a fixed MouseEvent, item re-created in OnEnter | ok |
+| 2302 Larbortiere | never | the four monsters are one-time (region init), counted in `2302b` | G3 |
+| 2401 Botschaft für Scouts | never | chain 2401-2404 on giver Res:17268 (his death label `mann2404` belongs to 2404); scouts one-time (region init), moved/morphed by the quest | G3 |
+| 2402 Verlaufene Sielder finden | the settler dies after joining (`losequest2402`) | the failure continues the chain (`SetUpQuest 2403`, giver's dialog set to 2403); M1 restarting 2402 is wrong (G11) | G11 |
+| 2403 Verschleppte Sielder befreien | all settlers die (`losequest2403`) | like 2402: the failure continues the chain with 2404 (G11) | G11 |
+| 2404 Verteidige Lager | never | vanilla the giver's death (`mann2404`) hands the reward to Res:17280; M2 restarts and brings him back instead | M2 changes vanilla |
+| 2411 Das Orkische Relikt | never | relic from `QIS_OnEnter2411` | ok |
+| 2412 Der Feige Hund | never | dog Res:17584 one-time (region init) | G3 |
+| 2413 Kleine Wuselorks | never | all from `QIS_OnEnter2413` | ok |
+| 2501 Finde Drachnschuppe für Rüstung | never | dragon Res:17292 one-time (region init); counts towards `finale` | G3 |
+| 2502 Eierdieb | the time-out, reported to the giver (`losequest2502`) | the failure counts towards the region's `finale` (bit 2 + `finalestart`) like a success (G11); after M1 bits 4/5 stay (G1) | G11 |
+| 2503 Drachentöter vertreiben | never | dragon slayers one-time (region init) | G3 |
+| 2504 Rette Jungfrau | the maiden (= giver) dies (`lose2504`) | M2 catches her death first: restart and respawn instead of failure + `finale` bit; redoable | M2 changes vanilla |
+| 9003 Versteckter Händler | `rg3_goodie1_lose` (merchant's death) | progress lives in the shared variable `goodies` (8 quests), M1/M2 can't reset it (G1 + G9): after M1 bit 1 stays and the dialog offers the reward branch (`ExitQuest`) without the quest running | G1 + G9 |
+| 9004 Vermisstes Kind | `rg1_goodie_lose` (child's death) | `goodies` bit 4 stays, the giver only answers "ok" (G1 + G9); child Res:17513 one-time (G6) | G1 + G6 + G9 |
+| 9005 Der Waldläufer | never | | ok |
+| 9006 Der unbekannte Krieger | never | progress in shared `goodies` | ok |
+| 9007 Jacquaire's Waffen | never | guards from `QIS_OnEnter9007` | ok |
+| 9008 Furchtbares Verbrechen | escorted giver Res:17539 dies (`losequest9008`) | M2 catches the death (restart, respawn); `QIS_OnEnter9008` sets the trigger area again | M2 changes vanilla |
+| 9009 Whiskeylieferung | never | | ok |
+| 9010 Vergrabene Sklaven | escorted giver Res:17575 dies (`lose9010`) | M2 restarts; but `9010` is a counter (0 = offer, needs key Res:17580 in the inventory), M2's reset to 1 shows the "follow" branch, and `QIS_OnEnter9010` deleted the key, so it can't be accepted again (G6) | G6 |
+| 9011 Jacquaires Vergangenheit | never | boss and target from `QIS_OnEnter9011` | ok |
+| main quests 1-81, 670 | never | started by trigger areas (`SelfTriggerQuest`), no giver, so M1-M3 don't touch them; 31's giver is only set by `SetNPCState` (not in `g_givers`) | ok |
+| 65 Trockenlegung durch Zwergenstaudamm (main) | never | giver Res:18306 (Torfstecher, start script) is in `g_givers`. Vanilla his death leaves 65 unfinishable (61 is activated by `Exit65`); M2 brings him back, but resets `65` incl. bit 5 (pump switched by `SwitchPump65`); if the pump can't be used twice the quest is stuck (G3). Re-accept creates the rune Res:17569 again (G4) | G3 + G4 |
+| 210 Verschleppte Novizinnen in Räuberöhle | never | boss Res:17083 one-time (start script) | G3 |
+| 9000 Das Untotenlager | the shaman's drop is picked up while the quest runs | refusing makes the giver Res:17500 attackable by `SetNPCState 08 14` (not Morph, G8); killing him drops the item whose label opens the Khorad Nur gate (`ablehnensieg_goodie_wueste`). If M2 sees him as not hostile it skips `drop_goodie_wueste`: **no item, the gate (main quest 55's way) never opens**. Check `IsHostileTo` for `08 14` | G8, critical |
+| 5003 Opferritual | the hero refuses at the end (`nein5003`) | a story choice: the shaman Res:17699 and his tribe turn hostile (`SetNPCState 08 12`) and the quest fails. M1 restarts it with bit 6 kept, so the (now hostile) shaman would offer the choice again (G11 + G8) | G8 + G11 |
+| 5005 Pony Express | a time limit runs out (`losepe`) | `losepe` deletes the giver Res:17753 (spawned once by main quest label `NachOrkland55`) and all stations; after M1 the book points at nobody and the quest can't be taken again (G5/G6); `5005c` stays (G1) | G1 + G5 |
+| 6001 Die Invasion der Eiselfen | all elves of the camp die (`od_welfen6001`) | after M1 bits stay (G1); the elves (incl. giver Res:17706) are one-time, dead (G6); re-accept creates the invaders again (G4). Accept label starts with `SetNPCState`, so the giver isn't in `g_givers` (no M2/M3) | G1 + G4 + G6 |
+| 6002 Das Orklager | never | | ok |
+| 6003 Elfenbeschützer | the escorted elf Res:17750 dies (`lose6003`) | after M1 bit 3 stays (G1); the elf is one-time (G6); her dialog is set by `SetNPCState`, so she is probably not in `g_givers` (no M2) | G1 + G6 |
+| 2600-3599 generic quests (16 templates × 22 regions) | `DQ_RG_..._LOSE_` (and `_ABORT_` for escorts) | no giver (runtime-assigned NPC), so no M2/M3; M1 restarts them although `Quest_End` already copies them (G2) | G2 |
+
+### Underworld (`bin\Addon\NetScriptCamp`)
+
+Underworld quests keep their state in the NPCs' dialogs (`SetNPCState ...
+dialog"dlg___"`), rarely in variables, and most givers are
+spawned by `QIS_OnSetUp` (run from the start script) without a dialog.
+
+| Quest | Fails when | Finding | Class |
+|---|---|---|---|
+| 7612 Eskorte | escorted Nanoduk Res:19547 dies (`od_nanoduk`) | he is the giver and one-time (`QIS_OnSetUp7612`); after M1 nobody offers the quest (G6) | G6 |
+| 7614 Vermisstes Kind (Underworld) | the child Res:19549 dies (`od_erdriac`) | the giver's dialog is set to the "child is dead" step `_d` (only "ok"); M1 doesn't restore the accept dialog `_a`, so it can't be taken again (G12). The child comes from `QIS_OnEnter7614` | G12 |
+| 7617 Eskorte in den Süden | escorted giver Res:19586 dies (`od_7617`) | he is spawned with his accept dialog, so M2 catches his death: restart and respawn | M2 changes vanilla |
+| 7619 Tod den Toten | the giver Res:19545 dies (`od_threepwood_7619`) | his accept dialog is only set by `SetNPCState` in `QIS_OnSetUp7619` (not in `g_givers`); after M1 he is dead (G6) | G6 |
+| 9518 Unter der Herrschaft von Dämonen | the mother Res:19530 dies while following (`od_lose9518`) | after M1 bits 1/2/4 stay, the giver only answers "ok" (G1); re-accept creates mother and demons again (G4) | G1 + G4 |
+| 9520 Cookies Quest | the hero gives the cookies to Von Endlich, then the girl ends it (`btn_FinishLose9520`, gold for experience) | a story choice (G11); after M1 bit 4 stays, so the girl offers `btn_FinishLose9520` again and again (repeatable gold-for-experience trade) | G11 |
+| 10254 Der Hilferuf | the giver dies (`od_Auftraggeber_10254`) | the giver is spawned once when the letter is picked up (`ot_Brief_10254`); after M1 he's dead and the letter gone (G6). The 10250-10261 givers have named ids (`res:AUFTRAGGEBER_10254`, after the type argument), which `InstructionScriptId` doesn't read: none of them is in `g_givers` (G13) | G6 + G13 |
+| 10255 Panik vor Spinnen | never (its "lose" is a label name; the giver offers a restart himself) | | ok |
+| 10260 Das Rätsel in der Wüßte | the wrong chest is opened (`fillchest_chestQJ_0x`) | the giver's dialog is removed (`0a#0`); after M1 nobody offers it (G12) | G12 |
+| other Underworld quests (main 11-56, 7600-7630, 9511-9522, 9900, 11101-11110, 15001-15084) | never | main quests have no giver (untouched). Side quests: givers spawned with their accept dialog in `QIS_OnSetUp` are in `g_givers` (7600, 7601, 7605, 7609, 7614, 7617, 7618, 7620, 7622-7624, 7626, 7630, 9511, 9513, 9515, 9518, 9520, 9521); a chain's later quests on the same giver (dialog by `SetNPCState`) are not. Their targets also come from `QIS_OnSetUp` (one-time), so M2 after a target died loses progress only where variables hold it (G3, rare here); named ids (`res:Sam1_Knappe`, `res:AUFTRAGGEBER_…`) are skipped (G13) | ok / G3 |
+
+## Summary and proposed fixes
+
+Counted over the side quests (Ancaria 151-2504, 9003-9011, 210, 65, the
+5003-6003 set and the Underworld quests that can fail): about a third are
+fine, the rest fall into the classes above. In order of impact:
+
+1. **G8 critical, 9000** (Khorad Nur gate): verify by a log whether a creature
+ set by `SetNPCState ... 08 14` is hostile to the hero (`IsHostileTo`). If
+ not, M2 skips the shaman's death label and the gate never opens. Fix
+ either way: treat a giver as "turned" when any of its quest's labels set
+ `08 12`/`08 14` on it (extends `ScanGiverTurns` beyond Morph); that also
+ fixes M3 for 1112, 1301, 1701, 2003, 2201, 2202, 501, 5003.
+2. **G1 reset on failure:** M1 resets the quest's own variables like M2
+ (including the ones only start/region scripts initialise, with their
+ initial values, e.g. 302a, 2001a = 1). Fixes most "giver only says ok"
+ cases (152, 702, 703, 1002, 1003, 1302, 1303, 1401, 1404, 1602, 1803,
+ 1903, 9518, ...).
+3. **G11 failures that are part of a story or chain** (2402, 2403, 2502,
+ 5003, 9520): M1 must not restart a quest whose failure label also calls
+ `SetUpQuest`/`finalestart`, or sets the giver hostile.
+4. **G2 generic quests:** M1 leaves 2600-3599 (and their runtime copies) to
+ the game.
+5. **G6 restart without NPCs / G3 lost progress:** the bigger design
+ question. Options: (a) on restart, respawn the quest's one-time NPCs from
+ their spawn instructions in start/region scripts (like the giver), (b) keep
+ the variables on M2/M3 (no progress loss, but the giver may answer with a
+ running branch), (c) don't restart quests whose needed NPCs are gone and
+ leave them failed. Recommendation: (a) for NPCs the quest's dialogs or
+ death labels name, else (c).
+6. **G4 duplicates:** before re-running `QIS_OnEnter`, delete the quest's
+ still-alive named NPCs/objects it is about to create.
+7. **G5/G7** (154, 301, 1502, 5005): M3 skips quests whose labels `DelNPC`
+ their giver; M1 skips quests without a giver.
+8. **G9/G12/G13** (803, 9003/9004 `goodies`, 7614, 10260, named ids): lower
+ priority; G12 needs the accept dialog put back (it is in the giver's spawn
+ instruction or `QIS_OnSetUp`).
+
+## Fixes (2026-10-30)
+
+Request: "all, but leave 1, as i cant play for now" (2-8 above; G8 waits for a
+measurement in the game). Not yet tested in the game.
+
+- `mods/questscript.cpp` (new) reads the script code once and keeps, per
+ quest: the giver (spawned with the accept dialog, else given it by an NPC
+ state instruction: the Underworld's `QIS_OnSetUp`), its variables (set
+ only by its labels, plus the one named after it: G9), what `QIS_OnEnter`
+ creates, the NPCs it needs (its labels set their state, don't create them),
+ whether it morphs or deletes its giver, and the failure labels that end its
+ story (`LoseQuest` with `SetUpQuest`/`ActivateQuest`, experience or gold, a
+ call into another quest's label, or an NPC offering the quest turned hostile
+ or set to 0 hit points: 1701 `ende1701`, 1703, 2402, 2403, 2502, 2504, 5003,
+ 9520). It also keeps the labels running (for `Quest_End`) and spawns NPCs
+ once their bodies are gone.
+- `questfail.cpp`: at `Quest_Trigger` (first time) the quest's variables are
+ kept ("questVariables" with the hero). A failed quest stays failed when it
+ is a generic quest or a copy (G2), has no giver (G7), its failure ends the
+ story (G11) or its giver / an NPC it needs is gone without a spawn
+ instruction (G5/G6). Else the restart also: resets its variables to the kept
+ values (G1; without them: 1 for the one named after the quest, 0 else),
+ deletes what `QIS_OnEnter` created (G4), spawns dead needed NPCs and the giver
+ again (G6, from region start labels, another quest's label or
+ `QIS_OnSetUp`), and gives a living giver the accept dialog again (G12).
+ After a load, failed quests restarted earlier get their variables reset
+ (repairs saves stuck by G1).
+- `questgiver.cpp` uses the same scan; the load repair skips quests that
+ delete their giver (G5); the giver comes back through `questfail::Restart`.
+- Not done: G8 (fix 1), G3 for progress held in one-time objects (2203's
+ barrels), G10, G13 (named ids such as `res:AUFTRAGGEBER_10254`: the game's
+ name table is unknown, those givers can't be checked or respawned).
diff --git a/docs/quests.md b/docs/quests.md
index 886c675..86bc682 100644
--- a/docs/quests.md
+++ b/docs/quests.md
@@ -48,6 +48,12 @@ 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.
+2026-10-30 (quest audit, `docs/quest-audit.md`): a restart also resets the
+quest's variables, deletes what its `QIS_OnEnter` created, brings back the
+giver and the NPCs it needs, and gives the giver the accept dialog again; some
+failures stay final (generic quests, no giver, story endings, NPCs gone for
+good). The script facts come from `mods/questscript.cpp`.
+
Marker back to the giver (request 2026-10-25: "quests that failed, should
have the blue marker point back to the quest giver"):
diff --git a/src/game/engine/quests.h b/src/game/engine/quests.h
index 15fa44c..1d7ecbc 100644
--- a/src/game/engine/quests.h
+++ b/src/game/engine/quests.h
@@ -83,6 +83,28 @@ inline uint32_t ScriptLabelCodeSize(const uint8_t* label) {
// (0x49cc20); conditions 0x49 / 0x4a test bit n set / clear, 0x48 the value
// (0x498aa0; a missing variable is false).
constexpr uint16_t kScriptSpawn = 0x01;
+// More opcodes (keyword table 0x452540, name i <-> opcode; docs/quest-audit.md):
+// 3 NPC state ("Res:", 0x09 dialog, flag 0x08 with 0x12 hostile, ...),
+// 8 create an object, 0x0f / 0x36 end the quest (ExitQuest / LoseQuest), 0x11 /
+// 0x12 give experience / gold, 0x14 start quest #id, 0x15 set up quest #id,
+// 0x16 call a label, 0x34 set hit points ("Res:", percent), 0x37 delete an
+// NPC or object, 0x3a / 0x42 if / else if, 0x3c dialog answer (text, label),
+// 0x4f change creature type, 0x75 activate quest #id.
+constexpr uint16_t kScriptSetNpcState = 0x03;
+constexpr uint16_t kScriptCreateObject = 0x08;
+constexpr uint16_t kScriptGiveExperience = 0x11;
+constexpr uint16_t kScriptGiveGold = 0x12;
+constexpr uint16_t kScriptStartQuest = 0x14;
+constexpr uint16_t kScriptSetUpQuest = 0x15;
+constexpr uint16_t kScriptCallLabel = 0x16;
+constexpr uint16_t kScriptSetHitPoints = 0x34;
+constexpr uint16_t kScriptLoseQuest = 0x36;
+constexpr uint16_t kScriptDelete = 0x37;
+constexpr uint16_t kScriptIf = 0x3a;
+constexpr uint16_t kScriptAnswer = 0x3c;
+constexpr uint16_t kScriptElseIf = 0x42;
+constexpr uint16_t kScriptChangeType = 0x4f;
+constexpr uint16_t kScriptActivateQuest = 0x75;
constexpr uint16_t kScriptSetVarValue = 0x43;
constexpr uint16_t kScriptSetVarBit = 0x44;
constexpr uint16_t kScriptClearVarBit = 0x45;
@@ -94,6 +116,11 @@ inline bool IsScriptVariableWrite(uint16_t opcode) {
constexpr uint8_t kScriptArgString = 0x01;
constexpr uint8_t kScriptArgInt = 0x0b;
constexpr uint8_t kScriptArgDialog = 0x09;
+constexpr uint8_t kScriptArgAttitude = 0x08; // flag, followed by 0x12 (hostile) or 0x14
+
+// The generic quests (Quest_End copies them under a new id; docs/quests.md).
+constexpr int32_t kGenericQuestFirst = 2600;
+constexpr int32_t kGenericQuestLast = 3599;
// CRT fseek (0x84866d) / fread (0x848726) of the game, on its own FILE*.
inline void* ScriptFile() { return *reinterpret_cast(0x00aaaf04); }
diff --git a/src/mods/questfail.cpp b/src/mods/questfail.cpp
index fa8abb3..a3f968e 100644
--- a/src/mods/questfail.cpp
+++ b/src/mods/questfail.cpp
@@ -22,12 +22,24 @@
// compass on the giver, or is removed if the giver is not known. Accepting it
// again removes the failed entry. Quests that failed before the mod are reset
// the same way once after loading.
+//
+// Quest audit (2026-10-30, docs/quest-audit.md): a restart also puts back what
+// the quest's script needs to be offered again (questscript.cpp): its variables
+// as they were when it became available (kept at Quest_Trigger, saved with the
+// hero; for quests triggered earlier: 1 for the one named after the quest, else
+// 0), the giver's accept dialog, the giver and the NPCs it needs from their
+// spawn instructions; what its QIS_OnEnter created is deleted (accepting again
+// creates it anew). A quest stays failed when the game repeats it itself
+// (generic quests), when it has no giver (trigger areas), when its failure ends
+// the story (2402 sets up 2403, 5003's shaman turns hostile, ...) or when an NPC
+// it needs is gone for good.
#include
#include
#include