aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/engine/script_patches.cpp197
1 files changed, 197 insertions, 0 deletions
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 8d42818020..4fe212fb55 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -13486,6 +13486,200 @@ static const uint16 qfg4EmptyBurgoRoomPatch[] = {
PATCH_END
};
+// Ad Avis and his necrotaurs chase and catch hero to take him to the dungeon,
+// but this one-time event can repeat along with the entire dungeon sequence.
+//
+// The chase code is complex and spread across many scripts with many flags.
+// There is no code that resets all the flags upon capture and no code that
+// correctly tests if capture has occurred. This results in many ways to leave
+// at least one flag set and repeat the chase. What these code paths all have
+// in common is that the player has to walk through the town gate, room 290.
+//
+// We fix this by adding code to the start of rm290:init that clears all of the
+// relevant chase flags if capture has already occurred. Fortunately, this
+// method starts with unused debugging code that can be overwritten.
+//
+// Applies to: All versions
+// Responsible method: rm290:init
+// Fixes bug: #11056
+static const uint16 qfg4ChaseRepeatsSignature[] = {
+ SIG_MAGICDWORD,
+ 0x81, 0xc9, // lag c9 [ debug mode ]
+ 0x31, 0x4e, // bnt 4e [ skip debug code ]
+ 0x78, // push1
+ 0x72, SIG_ADDTOOFFSET(+2), // lofsa prompt
+ 0x36, // push
+ 0x46, SIG_UINT16(0xfaff), // calle proc64255_1 02
+ SIG_UINT16(0x0001),
+ SIG_UINT16(0x0002),
+ 0xa3, 0x02, // sal 02
+ 0x39, 0x05, // pushi 05
+ 0x36, // push
+ 0x78, // push1
+ 0x7a, // push2
+ 0x39, 0x03, // pushi 03
+ 0x39, 0x04, // pushi 04
+ 0x46, SIG_UINT16(0xfde7), // calle proc64999_5 0a
+ SIG_UINT16(0x0005),
+ SIG_UINT16(0x000a),
+ SIG_END,
+};
+
+static const uint16 qfg4ChaseRepeatsPatch[] = {
+ 0x78, // push1
+ 0x39, 0x6e, // pushi 6e [ flag 110 ]
+ 0x45, 0x04, PATCH_UINT16(0x0002), // callb proc0_4 02 [ have you been captured? ]
+ 0x31, 0x49, // bnt 49 [ don't clear chase flags ]
+ 0x78, // push1
+ 0x39, 0x50, // pushi 50 [ flag 80 ]
+ 0x45, 0x03, PATCH_UINT16(0x0002), // callb proc0_3 02 [ clear chase flag 80 ]
+ 0x78, // push1
+ 0x39, 0x51, // pushi 51 [ flag 81 ]
+ 0x45, 0x03, PATCH_UINT16(0x0002), // callb proc0_3 02 [ clear chase flag 81 ]
+ 0x78, // push1
+ 0x38, PATCH_UINT16(0x00a4), // pushi 00a4 [ flag 164 ]
+ 0x45, 0x03, PATCH_UINT16(0x0002), // callb proc0_3 02 [ clear chase flag 164 ]
+ 0x33, 0x31, // jmp 31 [ continue room init ]
+ PATCH_END,
+};
+
+// When the necrotaurs catch hero in the woods to take him to the dungeon, two
+// different script bugs randomly cause interpreter errors. This patch fixes
+// calling kUpdateScreenItem on a necrotaur with a deleted screen item.
+//
+// When hero is caught, the screen turns black before going to the dungeon.
+// There is an inconsistent delay and the necrotaurs will sometimes reappear
+// briefly on the black screen. These symptoms hint at the script's problems.
+// sBlackOut sets a 300 cycle delay but this rarely has an effect. Instead each
+// actor is hidden while their motions continue and sBlackOut advances when an
+// invisible necrotaur completes its JumpTo motion. JumpTo stores its client's
+// signal on initialization and unconditionally restores it upon completion. If
+// JumpTo completes after View:hide calls kDeleteScreenItem and sets the hidden
+// signal bit then signal is reverted and Actor:doit calls kUpdateScreenItem.
+//
+// We fix this by disposing of each cast member before painting black instead of
+// hiding them. This hides them and terminates their motions. This exposes the
+// previously unused 300 cycle delay, which is much longer than normal, so we
+// also change that to 2 seconds. This is consistent with existing behavior
+// and it's the same delay used in room 290's working version of this script.
+// The majority of this patch is to free up the single byte needed to change
+// the 8-bit hide selector to 16-bit dispose.
+//
+// Applies to: All versions
+// Responsible method: sBlackOut:changeState(3)
+// Fixes bug: #11056
+static const uint16 qfg4NecrotaurBlackoutSignature[] = {
+ SIG_MAGICDWORD,
+ 0x39, SIG_SELECTOR8(hide), // pushi hide
+ 0x81, 0x05, // lag 05
+ 0x4a, SIG_UINT16(0x0006), // send 06 [ cast eachElementDo: hide ]
+ 0x78, // push1 [ kUpdatePlane param count ]
+ 0x39, SIG_SELECTOR8(back), // pushi back
+ 0x78, // push1
+ 0x76, // push0
+ 0x39, SIG_ADDTOOFFSET(+1), // pushi picture
+ 0x78, // push1
+ 0x39, 0xff, // pushi ff
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi yourself [ returns self ]
+ 0x76, // push0
+ 0x76, // push0 [ plane ]
+ 0x76, // push0
+ 0x81, 0x02, // lag 02
+ 0x4a, SIG_UINT16(0x0004), // send 04 [ room plane? ]
+ 0x4a, SIG_UINT16(0x0010), // send 10 [ room:plane back: 0 picture: -1 yourself: ]
+ 0x36, // push [ room:plane for kUpdatePlane ]
+ SIG_ADDTOOFFSET(+29),
+ 0x34, SIG_UINT16(0x012c), // ldi 012c
+ 0x65, // aTop cycles [ cycles = 300 ]
+ SIG_END
+};
+
+static const uint16 qfg4NecrotaurBlackoutPatch[] = {
+ 0x38, PATCH_SELECTOR16(dispose), // pushi dispose
+ 0x81, 0x05, // lag 05
+ 0x4a, PATCH_UINT16(0x0006), // send 06 [ cast eachElementDo: dispose ]
+ 0x78, // push1 [ kUpdatePlane param count ]
+ 0x76, // push0 [ plane ]
+ 0x76, // push0
+ 0x81, 0x02, // lag 02
+ 0x4a, PATCH_UINT16(0x0004), // send 04 [ room plane? ]
+ 0x36, // push [ room:plane for kUpdatePlane ]
+ 0x39, PATCH_SELECTOR8(back), // pushi back
+ 0x39, 0x01, // pushi 01
+ 0x39, 0x00, // pushi 00
+ 0x39, PATCH_GETORIGINALBYTE(+13), // pushi picture
+ 0x39, 0x01, // pushi 01
+ 0x39, 0xff, // pushi ff
+ 0x4a, PATCH_UINT16(0x000c), // send 0c [ room:plane back: 0 picture: -1 ]
+ PATCH_ADDTOOFFSET(+29),
+ 0x34, PATCH_UINT16(0x0002), // ldi 0002
+ 0x65, PATCH_GETORIGINALBYTEADJUST(+65, +2), // aTop seconds [ seconds = 2 ]
+ PATCH_END
+};
+
+// When the necrotaurs catch hero in the woods to take him to the dungeon an
+// effectively random crash from the Grooper class can occur in the CD version.
+// See the inn door's script patch for details on these CD regressions.
+//
+// The error occurs when sBlackOut sets the motion of a necrotaur while it has
+// no cycler. In this case the cycler should still be Walk but CD regressions
+// can cause it to be cleared at unexpected times. We prevent this by setting
+// necrotaur cyclers to Walk when setting their final PChase motions.
+//
+// Applies to: PC CD
+// Responsible method: sBlackOut:changeState(0)
+// Fixes bug: #11056
+static const uint16 qfg4NecrotaurCaptureSignature[] = {
+ SIG_MAGICDWORD,
+ 0x18, // not
+ 0x31, 0x38, // bnt 38
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi distanceTo
+ 0x78, // push1
+ 0x72, SIG_ADDTOOFFSET(+2), // lofsa nec (nec1 or nec2 or nec3)
+ 0x36, // push
+ 0x81, 0x00, // lag 00
+ 0x4a, SIG_UINT16(0x0006), // send 06 [ hero distanceTo: nec ]
+ 0x36, // push
+ 0x35, 0x19, // ldi 19
+ 0x1e, // gt?
+ 0x31, 0x19, // bnt 19
+ 0x38, SIG_SELECTOR16(setMotion), // pushi setMotion
+ 0x38, SIG_UINT16(0x0004), // pushi 0004
+ 0x51, 0x6c, // class PChase
+ 0x36, // push
+ 0x89, 0x00, // lsg 00
+ 0x39, 0x19, // pushi 19
+ 0x72, SIG_ADDTOOFFSET(+2), // lofsa nec
+ 0x36, // push
+ 0x72, // lofsa nec
+ SIG_END
+};
+
+static const uint16 qfg4NecrotaurCapturePatch[] = {
+ 0x2f, 0x39, // bt 39
+ 0x38, PATCH_GETORIGINALUINT16(+4), // pushi distanceTo
+ 0x78, // push1
+ 0x74, PATCH_GETORIGINALUINT16(+8), // lofss nec (nec1 or nec2 or nec3)
+ 0x81, 0x00, // lag 00
+ 0x4a, PATCH_UINT16(0x0006), // send 06 [ hero distanceTo: nec ]
+ 0x39, 0x19, // pushi 19
+ 0x24, // le?
+ 0x31, 0x1c, // bnt 1c
+ 0x38, PATCH_SELECTOR16(setCycle), // pushi setCycle
+ 0x78, // push1
+ 0x51, 0x17, // class Walk
+ 0x36, // push
+ 0x38, PATCH_SELECTOR16(setMotion), // pushi setMotion
+ 0x39, 0x04, // pushi 04
+ 0x51, 0x6c, // class PChase
+ 0x36, // push
+ 0x89, 0x00, // lsg 00
+ 0x39, 0x19, // pushi 19
+ 0x72, PATCH_GETORIGINALUINT16(+8), // lofsa nec
+ 0x36, // push
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry qfg4Signatures[] = {
{ true, 0, "prevent autosave from deleting save games", 1, qfg4AutosaveSignature, qfg4AutosavePatch },
@@ -13503,6 +13697,8 @@ static const SciScriptPatcherEntry qfg4Signatures[] = {
{ true, 41, "fix conditional void calls", 3, qfg4ConditionalVoidSignature, qfg4ConditionalVoidPatch },
{ true, 50, "fix random revenant kopeks", 1, qfg4SearchRevenantSignature, qfg4SearchRevenantPatch },
{ true, 51, "Floppy: fix ad avis capture lockup", 1, qfg4AdAvisCaptureSignature, qfg4AdAvisCapturePatch },
+ { true, 51, "fix necrotaur blackout", 1, qfg4NecrotaurBlackoutSignature,qfg4NecrotaurBlackoutPatch },
+ { true, 51, "CD: fix necrotaur capture", 3, qfg4NecrotaurCaptureSignature, qfg4NecrotaurCapturePatch },
{ true, 53, "NRS: fix wraith lockup", 1, qfg4WraithLockupNrsSignature, qfg4WraithLockupNrsPatch },
{ true, 83, "fix incorrect array type", 1, qfg4TrapArrayTypeSignature, qfg4TrapArrayTypePatch },
{ true, 140, "fix character selection", 1, qfg4CharacterSelectSignature, qfg4CharacterSelectPatch },
@@ -13510,6 +13706,7 @@ static const SciScriptPatcherEntry qfg4Signatures[] = {
{ true, 260, "CD: fix inn door crash", 1, qfg4InnDoorCDSignature, qfg4InnDoorCDPatch },
{ true, 270, "fix town gate after a staff dream", 1, qfg4DreamGateSignature, qfg4DreamGatePatch },
{ true, 270, "fix town gate doormat at night", 1, qfg4TownGateDoormatSignature, qfg4TownGateDoormatPatch },
+ { true, 290, "fix chase repeating", 1, qfg4ChaseRepeatsSignature, qfg4ChaseRepeatsPatch },
{ true, 300, "fix empty burgomeister room teller", 1, qfg4EmptyBurgoRoomSignature, qfg4EmptyBurgoRoomPatch },
{ true, 320, "fix pathfinding at the inn", 1, qfg4InnPathfindingSignature, qfg4InnPathfindingPatch },
{ true, 320, "fix talking to absent innkeeper", 1, qfg4AbsentInnkeeperSignature, qfg4AbsentInnkeeperPatch },