diff options
| -rw-r--r-- | engines/sci/engine/script_patches.cpp | 198 | ||||
| -rw-r--r-- | engines/sci/engine/workarounds.cpp | 2 | 
2 files changed, 199 insertions, 1 deletions
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 112e04d4d4..f291bc8bd5 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -159,8 +159,10 @@ static const char *const selectorNameTable[] = {  	"plane",        // RAMA  	"state",        // RAMA  	"getSubscriberObj", // RAMA +	"cue",          // QFG4  	"moveSpeed",    // QFG4  	"setLooper",    // QFG4 +	"setSpeed",     // QFG4  	"value",        // QFG4  #endif  	NULL @@ -245,8 +247,10 @@ enum ScriptPatcherSelectors {  	SELECTOR_plane,  	SELECTOR_state,  	SELECTOR_getSubscriberObj, +	SELECTOR_cue,  	SELECTOR_moveSpeed,  	SELECTOR_setLooper, +	SELECTOR_setSpeed,  	SELECTOR_value  #endif  }; @@ -9253,6 +9257,196 @@ static const uint16 qfg4Tarot5PriorityPatch[] = {  	PATCH_END  }; +// When crossing the cave's tightrope in room 710, a tentacle emerges, and then +// its animation freezes - in ScummVM, not the original interpreter. This +// happens because of an extraneous argument passed to setCycle(). +// +// (tentacle setCycle: RandCycle tentacle) +// +// RandCycle can accept an optional arg, but it expects a number, not an +// object. ScummVM doesn't catch the faulty arithmetic and behaves abnormally. +// We remove the bad arg. +// +// Applies to at least: English CD, English floppy, German floppy +// Responsible method: sTentacleDeath::changeState(3) in script 710 +// Fixes bug: #10615 +static const uint16 qfg4TentacleWriggleSignature[] = { +	SIG_MAGICDWORD, +	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle +	0x7a,                               // push2 +	0x51, SIG_ADDTOOFFSET(+1),          // class RandCycle +	0x36,                               // push +	0x72, SIG_ADDTOOFFSET(+2),          // loffsa tentacle +	0x36,                               // push +	0x72, SIG_ADDTOOFFSET(+2),          // loffsa tentacle +	0x4a, SIG_UINT16(0x0008),           // send 08 +	SIG_END +}; + +static const uint16 qfg4TentacleWrigglePatch[] = { +	PATCH_ADDTOOFFSET(+3), +	0x78,                               // push1 (1 setCycle arg) +	PATCH_ADDTOOFFSET(+3),              // ... +	0x35, 0x00,                         // ldi 0 (erase 2 bytes) +	0x35, 0x00,                         // ldi 0 (erase 2 bytes) +	PATCH_ADDTOOFFSET(+3),              // ... +	0x4a, PATCH_UINT16(0x0006),         // send 06 +	PATCH_END +}; + +// When crossing the cave's tightrope in room 710, a tentacle emerges. The +// tentacle is supposed to reach a state where it waits indefinitely for an +// external cue() to retract. If the speed slider is too high, a fighter +// reaches the other side and sends a cue() before the tentacle is ready to +// receive it. The tentacle never retracts. +// +// The fighter script (crossByHand) drains stamina in state 2 as hero moves +// across. A slower speed would cost extra stamina. We add a delay after that +// part is over, in state 3, just as hero is about to dismount. When state 4 +// cues, the tentacle script (sTentacleDeath) will be ready (state 3). +// +// To create that delay we set the "cycles" property for a countdown and remove +// all other advancement mechanisms. State 3 had a cue from say() and a +// self-cue(). The former's "self" arg becomes null. The latter is erased. +// +// Crossing from the left (crossByHandLeft) doesn't require fixing. +// +// Applies to at least: English CD, English floppy, German floppy +// Responsible method: crossByHand::changeState(3) in script 710 +// Fixes bug: #10615 +static const uint16 qfg4PitRopeFighterSignature[] = { +	0x65, SIG_ADDTOOFFSET(+1),          // aTop state +	SIG_ADDTOOFFSET(+269),              // ... +	0x31, 0x1e,                         // bnt 30d (set a flag and say() on 1st crossing, else cue) +	SIG_ADDTOOFFSET(+8),                // ... +	0x38, SIG_SELECTOR16(say),          // pushi say ("You just barely made it") +	0x38, SIG_UINT16(0x0005),           // pushi 5d +	0x39, 0x0a,                         // pushi 10d +	0x39, 0x06,                         // pushi 6d +	0x39, 0x20,                         // pushi 32d +	0x76,                               // push0 +	0x7c,                               // pushSelf +	SIG_ADDTOOFFSET(+5),                // ... +	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch] +	SIG_MAGICDWORD, +	0x38, SIG_SELECTOR16(cue),          // pushi cue +	0x76,                               // push0 +	0x54, SIG_UINT16(0x0004),           // self 04 +	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch] +	SIG_END +}; + +static const uint16 qfg4PitRopeFighterPatch[] = { +	PATCH_ADDTOOFFSET(+271),            // ... (2 + 269) +	0x31, 0x1b,                         // bnt 26d (skip the say w/o ending the switch) +	PATCH_ADDTOOFFSET(+21),             // ... (8 + 13) +	0x76,                               // push0 (null caller, so say won't cue crossByHand) +	PATCH_ADDTOOFFSET(+5),              // ... +                                        // (no jmp, self-cue becomes cycles) +	0x35, 0x20,                         // ldi 32d +	0x65, PATCH_GETORIGINALBYTE(1)+6,   // aTop cycles (property offset = @state + 6d) +	0x33, 0x04,                         // jmp 4d [skip waste bytes, end the switch] +	PATCH_END +}; + +// As above, mages at high speed can get across the pit in room 710 before +// tentacle is ready to receive a cue(). +// +// As luck would have it, hero's speed is cached and restored. Twice actually. +// +// Overview of the mage script (sLevitateOverPit) +//  State 1-3:  Cache hero's slider-based speed. +//              Set a temporary speed as hero unfurls the cloth. +//              Restore the original value. +//              Call handsOn(). Wait for an external cue(). +//  State 4:    Call handsOff(). +//              If cued by the Levitate spell (script 21), go to 5-7. +//              A plain cue() from anywhere else, leads to state 8. +//  State 5-7:  Move across the pit. Skip to state 9. +//  State 8:    An abort message. +//  State 9-10: Cache hero's speed again. +//              Set a temporary speed as hero folds up the cloth. +//              Restore the original value, and normalize hero. Call handsOn(). +// +// Patch 1: We overwrite some derelict code in state 5, caching the +// slider-based speed again, in case the player adjusted it before casting +// Levitate, then setting a fixed speed of our own for the crossing. +// +// Patch 2: Patch 1 already cached and clobbered the speed. We remove the +// original attempt to cache again in state 9. +// +// The result is caching/restoration at the beginning, aborting or caching and +// crossing with our fixed value, and a restoration at the end (whichever value +// was last cached). The added travel time has no side effect for mages. +// +// Mages have no other script to levitate across from left to right. At some +// point in development, the meaning of "register" changed. The derelict +// state 5 code thought 0/1 meant move right/left. Whereas state 4 decides 0/1 +// means abort/cross, only ever moving left. The rightward MoveTo never runs. +// +// Applies to at least: English CD, English floppy, German floppy +// Responsible method: sLevitateOverPit::changeState(5) in script 710 +// Fixes bug: #10615 +static const uint16 qfg4PitRopeMageSignature1[] = { +	0x30, SIG_UINT16(0x0017),           // bnt 23d [if register == 0 (never), move right] +	SIG_ADDTOOFFSET(+20),               // ... (move left) +	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch] + +	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion (move right) +	0x38, SIG_UINT16(0x0004),           // pushi 4d +	0x51, SIG_ADDTOOFFSET(1),           // class MoveTo +	0x36,                               // push +	SIG_MAGICDWORD, +	0x38, SIG_UINT16(0x00da),           // pushi 218d +	0x39, 0x30,                         // pushi 48d +	0x7c,                               // pushSelf +	0x81, 0x00,                         // lag global[0] (hero) +	0x4a, SIG_UINT16(0x000c),           // send 12d +	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch] +	SIG_END +}; + +static const uint16 qfg4PitRopeMagePatch1[] = { +	0x34, PATCH_UINT16(0x0000),         // ldi 0 (erase the branch) +	PATCH_ADDTOOFFSET(+20),             // ... + +	0x38, SIG_SELECTOR16(cycleSpeed),   // pushi cycleSpeed +	0x76,                               // push0 +	0x81, 0x00,                         // lag global[0] (hero) +	0x4a, SIG_UINT16(0x0004),           // send 4d +	0xa3, 0x02,                         // sal local[2] (cache again) +                                        // +	0x38, SIG_SELECTOR16(setSpeed),     // pushi setSpeed +	0x78,                               // push1 +	0x39, 0x08,                         // pushi 8d (set our fixed speed) +	0x81, 0x00,                         // lag global[0] (hero) +	0x4a, SIG_UINT16(0x0006),           // send 6d +	0x5c,                               // selfID (erase 1 byte to keep disasm aligned) +	PATCH_END +}; + +// Responsible method: sLevitateOverPit::changeState(9) in script 710 +static const uint16 qfg4PitRopeMageSignature2[] = { +	SIG_MAGICDWORD, +	0x35, 0x09,                         // ldi 9d (case 9 label) +	0x38, SIG_SELECTOR16(cycleSpeed),   // pushi cycleSpeed +	SIG_ADDTOOFFSET(+6),                // ... +	0xa3, 0x02,                         // sal local[2] (original re-cache) +	SIG_ADDTOOFFSET(+48),               // ... +	0x38, SIG_SELECTOR16(cycleSpeed),   // pushi cycleSpeed +	0x78,                               // push1 +	0x8b, 0x02,                         // lsl local[2] (restore cached speed) +	SIG_END +}; + +static const uint16 qfg4PitRopeMagePatch2[] = { +	PATCH_ADDTOOFFSET(+11),             // (don't cache our fixed speed) +	0x35, 0x00,                         // ldi 0 (erase 2 bytes) +	PATCH_ADDTOOFFSET(+48),             // ... +	0x38, PATCH_SELECTOR16(setSpeed),   // pushi setSpeed (keep cycleSpeed & moveSpeed sync'd) +	PATCH_END +}; +  //          script, description,                                     signature                      patch  static const SciScriptPatcherEntry qfg4Signatures[] = {  	{  true,     0, "prevent autosave from deleting save games",   1, qg4AutosaveSignature,          qg4AutosavePatch }, @@ -9290,6 +9484,10 @@ static const SciScriptPatcherEntry qfg4Signatures[] = {  	{  false,  663, "CD: fix crest bookshelf",                     1, qfg4CrestBookshelfCDSignature, qfg4CrestBookshelfCDPatch },  	{  false,  663, "Floppy: fix crest bookshelf",                 1, qfg4CrestBookshelfFloppySignature,   qfg4CrestBookshelfFloppyPatch },  	{  true,   663, "CD/Floppy: fix crest bookshelf motion",       1, qfg4CrestBookshelfMotionSignature,   qfg4CrestBookshelfMotionPatch }, +	{  true,   710, "fix tentacle wriggle cycler",                 1, qfg4TentacleWriggleSignature,  qfg4TentacleWrigglePatch }, +	{  true,   710, "fix tentacle retraction for fighter",         1, qfg4PitRopeFighterSignature,   qfg4PitRopeFighterPatch }, +	{  true,   710, "fix tentacle retraction for mage (1/2)",      1, qfg4PitRopeMageSignature1,     qfg4PitRopeMagePatch1 }, +	{  true,   710, "fix tentacle retraction for mage (2/2)",      1, qfg4PitRopeMageSignature2,     qfg4PitRopeMagePatch2 },  	{  true,   800, "fix setScaler calls",                         1, qfg4SetScalerSignature,        qfg4SetScalerPatch },  	{  true,   800, "fix grapnel removing hero's scaler",          1, qfg4RopeScalerSignature,       qfg4RopeScalerPatch },  	{  true,   803, "fix sliding down slope",                      1, qfg4SlidingDownSlopeSignature, qfg4SlidingDownSlopePatch }, diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 184f4adb89..0bff1f62f2 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -87,7 +87,7 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = {  	{ GID_QFG1VGA,        301,  928,  0,              "Blink", "init",                            NULL,     0,     0, { WORKAROUND_FAKE,   0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object  	{ GID_QFG2,           200,  200,  0,              "astro", "messages",                        NULL,     0,     0, { WORKAROUND_FAKE,   0 } }, // op_lsi: when getting asked for your name by the astrologer - bug #5152  	{ GID_QFG3,           780,  999,  0,                   "", "export 6",                        NULL,     0,     0, { WORKAROUND_FAKE,   0 } }, // op_add: trying to talk to yourself at the top of the giant tree - bug #6692 -	{ GID_QFG4,           710,64941,  0,          "RandCycle", "doit",                            NULL,     0,     0, { WORKAROUND_FAKE,   1 } }, // op_gt: when the tentacle appears in the third room of the caves +//	{ GID_QFG4,           710,64941,  0,          "RandCycle", "doit",                            NULL,     0,     0, { WORKAROUND_FAKE,   1 } }, // op_gt: when the tentacle appears in the third room of the caves  	{ GID_TORIN,        51400,64928,  0,              "Blink", "init",                            NULL,     0,     0, { WORKAROUND_FAKE,   1 } }, // op_div: when Lycentia knocks Torin out after he removes her collar  	SCI_WORKAROUNDENTRY_TERMINATOR  };  | 
