diff options
-rw-r--r-- | engines/mohawk/riven.cpp | 4 | ||||
-rw-r--r-- | engines/mohawk/riven_card.cpp | 192 | ||||
-rw-r--r-- | engines/mohawk/riven_card.h | 14 | ||||
-rw-r--r-- | engines/mohawk/riven_scripts.cpp | 1 | ||||
-rw-r--r-- | engines/mohawk/riven_stack.cpp | 76 | ||||
-rw-r--r-- | engines/mohawk/riven_stack.h | 25 | ||||
-rw-r--r-- | engines/mohawk/riven_stacks/jspit.cpp | 2 | ||||
-rw-r--r-- | engines/mohawk/riven_video.cpp | 4 |
8 files changed, 293 insertions, 25 deletions
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index 8fd28052e0..6c2b1a28c1 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -218,7 +218,7 @@ void MohawkEngine_Riven::doFrame() { _inventory->checkClick(_eventMan->getMousePos()); break; case Common::EVENT_KEYUP: - _stack->keyForceUp(); + _stack->keyResetAction(); break; case Common::EVENT_KEYDOWN: switch (event.kbd.keycode) { @@ -255,7 +255,7 @@ void MohawkEngine_Riven::doFrame() { } break; default: - _stack->onKeyPressed(event.kbd.keycode); + _stack->onKeyPressed(event.kbd); break; } break; diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp index d3166f31fc..a116d8817f 100644 --- a/engines/mohawk/riven_card.cpp +++ b/engines/mohawk/riven_card.cpp @@ -30,6 +30,8 @@ #include "mohawk/resource.h" #include "mohawk/riven.h" +#include "common/memstream.h" + namespace Mohawk { RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) : @@ -44,6 +46,7 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) : loadCardMovieList(id); loadCardHotspotEnableList(id); loadCardWaterEffectList(id); + applyPatches(id); } RivenCard::~RivenCard() { @@ -63,13 +66,89 @@ void RivenCard::loadCardResource(uint16 id) { _zipModePlace = inStream->readUint16BE(); _scripts = _vm->_scriptMan->readScripts(inStream); - // Apply script patches for this card + delete inStream; +} + +void RivenCard::applyPatches(uint16 id) { uint32 globalId = _vm->getStack()->getCardGlobalId(id); + + // Apply properties patches + + // On Jungle Island on the back side of the "beetle" gate, the forward hotspot + // is always enabled, preventing keyboard navigation from automatically opening + // the gate. + // We patch the card so that the forward opcode is enabled only when the gate is open. + // + // New hotspot enable entries: + // == Hotspot enable 5 == + // hotspotId: 3 + // enabled: 1 + // + // == Hotspot enable 6 == + // hotspotId: 3 + // enabled: 0 + // + // Additional load script fragment: + // switch (jgate) { + // case 0: + // activateBLST(6); + // break; + // case 1: + // activateBLST(5); + // break; + if (globalId == 0x8EB7) { + HotspotEnableRecord forwardEnabled; + forwardEnabled.index = _hotspotEnableList.back().index + 1; + forwardEnabled.hotspotId = 3; + forwardEnabled.enabled = 1; + _hotspotEnableList.push_back(forwardEnabled); + + HotspotEnableRecord forwardDisabled; + forwardDisabled.index = _hotspotEnableList.back().index + 1; + forwardDisabled.hotspotId = 3; + forwardDisabled.enabled = 0; + _hotspotEnableList.push_back(forwardDisabled); + + uint16 jGateVariable = _vm->getStack()->getIdFromName(kVariableNames, "jgate"); + uint16 patchData[] = { + 1, // Command count in script + kRivenCommandSwitch, + 2, // Unused + jGateVariable, + 2, // Branches count + + 0, // jgate == 0 branch (gate closed) + 1, // Command count in sub-script + kRivenCommandActivateBLST, + 1, // Argument count + forwardDisabled.index, + + 1, // jgate == 1 branch (gate open) + 1, // Command count in sub-script + kRivenCommandActivateBLST, + 1, // Argument count + forwardEnabled.index + }; + + // Script data is expected to be in big endian + for (uint i = 0; i < ARRAYSIZE(patchData); i++) { + patchData[i] = TO_BE_16(patchData[i]); + } + + Common::MemoryReadStream patchStream((const byte *)(patchData), ARRAYSIZE(patchData) * sizeof(uint16)); + RivenScriptPtr patchScript = _vm->_scriptMan->readScript(&patchStream); + + // Append the patch to the existing script + RivenScriptPtr loadScript = getScript(kCardLoadScript); + loadScript += patchScript; + + debugC(kRivenDebugPatches, "Applied fix always enabled forward hotspot in card %x", globalId); + } + + // Apply script patches for (uint i = 0; i < _scripts.size(); i++) { _scripts[i].script->applyCardPatches(_vm, globalId, _scripts[i].type, 0xFFFF); } - - delete inStream; } void RivenCard::enter(bool unkMovies) { @@ -255,6 +334,7 @@ void RivenCard::loadHotspots(uint16 id) { uint32 globalId = _vm->getStack()->getCardGlobalId(id); for (uint16 i = 0; i < hotspotCount; i++) { _hotspots[i] = new RivenHotspot(_vm, inStream); + _hotspots[i]->applyPropertiesPatches(globalId); _hotspots[i]->applyScriptPatches(globalId); } @@ -280,16 +360,20 @@ Common::Array<RivenHotspot *> RivenCard::getHotspots() const { return _hotspots; } -RivenHotspot *RivenCard::getHotspotByName(const Common::String &name) const { +RivenHotspot *RivenCard::getHotspotByName(const Common::String &name, bool optional) const { int16 nameId = _vm->getStack()->getIdFromName(kHotspotNames, name); for (uint i = 0; i < _hotspots.size(); i++) { - if (_hotspots[i]->getNameId() == nameId) { + if (_hotspots[i]->getNameId() == nameId && nameId != -1) { return _hotspots[i]; } } - error("Card %d does not have an hotspot named %s", _id, name.c_str()); + if (optional) { + return nullptr; + } else { + error("Card %d does not have an hotspot named %s", _id, name.c_str()); + } } RivenHotspot *RivenCard::getHotspotByBlstId(const uint16 blstId) const { @@ -598,6 +682,76 @@ void RivenCard::playMovie(uint16 index, bool queue) { } } +RivenScriptPtr RivenCard::onKeyAction(RivenKeyAction keyAction) { + RivenHotspot *directionHotspot = nullptr; + switch (keyAction) { + case kKeyActionMoveForward: + directionHotspot = findEnabledHotspotByName(17, + "forward", "forward1", "forward2", "forward3", + "opendoor", "openhatch", "opentrap", "opengate", "opengrate", + "open", "door", "drop", "go", "enterprison", "exit", + "forwardleft", "forwardright" + ); + break; + case kKeyActionMoveForwardLeft: + directionHotspot = findEnabledHotspotByName(1, "forwardleft"); + break; + case kKeyActionMoveForwardRight: + directionHotspot = findEnabledHotspotByName(1, "forwardright"); + break; + case kKeyActionMoveLeft: + directionHotspot = findEnabledHotspotByName(3, "left", "afl", "prevpage"); + break; + case kKeyActionMoveRight: + directionHotspot = findEnabledHotspotByName(3, "right", "afr", "nextpage"); + break; + case kKeyActionMoveBack: + directionHotspot = findEnabledHotspotByName(1, "back"); + break; + case kKeyActionLookUp: + directionHotspot = findEnabledHotspotByName(1, "up"); + break; + case kKeyActionLookDown: + directionHotspot = findEnabledHotspotByName(1, "down"); + break; + default: + break; + } + + if (!directionHotspot) { + return RivenScriptPtr(new RivenScript()); + } + + _hoveredHotspot = directionHotspot; + + RivenScriptPtr clickScript = directionHotspot->getScript(kMouseDownScript); + if (!clickScript || clickScript->empty()) { + clickScript = directionHotspot->getScript(kMouseUpScript); + } + if (!clickScript || clickScript->empty()) { + clickScript = RivenScriptPtr(new RivenScript()); + } + + return clickScript; +} + +RivenHotspot *RivenCard::findEnabledHotspotByName(uint n, ...) const { + va_list ap; + va_start(ap, n); + + for (uint i = 0; i < n; i++) { + const char *name = va_arg(ap, const char *); + RivenHotspot *hotspot = getHotspotByName(name, true); + if (hotspot && hotspot->isEnabled()) { + return hotspot; + } + } + + va_end(ap); + + return nullptr; +} + RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) : _vm(vm) { loadFromStream(stream); @@ -644,6 +798,32 @@ RivenScriptPtr RivenHotspot::getScript(uint16 scriptType) const { return RivenScriptPtr(); } +void RivenHotspot::applyPropertiesPatches(uint32 cardGlobalId) { + // In Jungle island, one of the bridge hotspots does not have a name + // This breaks keyboard navigation. Set the proper name. + if (cardGlobalId == 0x214a0 && _blstID == 9) { + _nameResource = _vm->getStack()->getIdFromName(kHotspotNames, "forward"); + debugC(kRivenDebugPatches, "Applied missing hotspot name patch to card %x", cardGlobalId); + } + + // In the lab in Book Making island the card showing one of the doors has + // two "forward" hotspots. One of them goes backwards. Disable it, and make sure + // it cannot be found by the keyboard navigation code. + if (cardGlobalId == 0x1fa79 && _blstID == 3) { + enable(false); + _nameResource = -1; + debugC(kRivenDebugPatches, "Applied disable buggy forward hotspot to card %x", cardGlobalId); + } + + // On Temple Island, in front of the back door to the rotating room, + // change the name of the hotspot to look at the bottom of the door to + // "down" instead of "forwardleft". That way the keyboard navigation + // does not spoil that you can go below the door. + if (cardGlobalId == 0x87ac && _blstID == 10) { + _nameResource = _vm->getStack()->getIdFromName(kHotspotNames, "down"); + debugC(kRivenDebugPatches, "Applied change hotspot name to 'down' patch to card %x", cardGlobalId); + } +} void RivenHotspot::applyScriptPatches(uint32 cardGlobalId) { for (uint16 i = 0; i < _scripts.size(); i++) { diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h index cfcacdb8b8..c5a5795eaa 100644 --- a/engines/mohawk/riven_card.h +++ b/engines/mohawk/riven_card.h @@ -99,7 +99,10 @@ public: RivenHotspot *getHotspotContainingPoint(const Common::Point &point) const; /** Get the hotspot with the specified name */ - RivenHotspot *getHotspotByName(const Common::String &name) const; + RivenHotspot *getHotspotByName(const Common::String &name, bool optional = false) const; + + /** Find an enabled hotspot with a name matching one of the arguments */ + RivenHotspot *findEnabledHotspotByName(uint n, ...) const; /** Get the hotspot with the specified BLST id */ RivenHotspot *getHotspotByBlstId(const uint16 blstId) const; @@ -125,6 +128,9 @@ public: /** Handle a mouse move event */ RivenScriptPtr onMouseMove(const Common::Point &mouse); + /** Handle a keyboard action */ + RivenScriptPtr onKeyAction(RivenKeyAction keyAction); + /** General frame update handler */ RivenScriptPtr onFrame(); @@ -145,6 +151,7 @@ private: void loadCardMovieList(uint16 id); void loadCardHotspotEnableList(uint16 id); void loadCardWaterEffectList(uint16 id); + void applyPatches(uint16 id); void setCurrentCardVariable(); RivenScriptPtr getScript(uint16 scriptType) const; @@ -254,9 +261,12 @@ public: /** Write all of the hotspot's data to standard output */ void dump() const; - /** Apply patches to hotspot's scripts to fix bugs in the original game scripts */ + /** Apply patches to the hotspot's scripts to fix bugs in the original game scripts */ void applyScriptPatches(uint32 cardGlobalId); + /** Apply patches to the hotspot's properties to fix bugs in the original game scripts */ + void applyPropertiesPatches(uint32 cardGlobalId); + private: enum { kFlagZip = 1, diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index cfe6eeb861..21c18fa468 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -309,6 +309,7 @@ void RivenScript::applyCardPatches(MohawkEngine_Riven *vm, uint32 cardGlobalId, arguments.push_back(256); arguments.push_back(0); _commands.push_back(RivenCommandPtr(new RivenSimpleCommand(vm, kRivenCommandPlaySound, arguments))); + debugC(kRivenDebugPatches, "Applied missing closing sound patch to card %x", cardGlobalId); } if (shouldApplyPatches) { diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp index 847a98f5ab..c90642adf1 100644 --- a/engines/mohawk/riven_stack.cpp +++ b/engines/mohawk/riven_stack.cpp @@ -41,7 +41,7 @@ RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) : _id(id), _mouseIsDown(false), _shouldRefreshMouseCursor(false), - _keyPressed(Common::KEYCODE_INVALID) { + _keyAction(kKeyActionNone) { removeTimer(); loadResourceNames(); @@ -305,16 +305,78 @@ void RivenStack::onFrame() { _vm->_scriptMan->runScript(script, true); } -Common::KeyCode RivenStack::keyGetPressed() const { - return _keyPressed; +RivenKeyAction RivenStack::keyGetAction() const { + return _keyAction; } -void RivenStack::keyForceUp() { - _keyPressed = Common::KEYCODE_INVALID; +void RivenStack::keyResetAction() { + _keyAction = kKeyActionNone; } -void RivenStack::onKeyPressed(const Common::KeyCode keyCode) { - _keyPressed = keyCode; +void RivenStack::onKeyPressed(const Common::KeyState &keyState) { + _keyAction = mapKeyStateToKeyAction(keyState); + + if (_vm->getCard() && !_vm->_scriptMan->hasQueuedScripts()) { + RivenScriptPtr script = _vm->getCard()->onKeyAction(_keyAction); + + if (!script->empty()) { + _vm->_scriptMan->runScript(script, true); + keyResetAction(); + } + } +} + +RivenKeyAction RivenStack::mapKeyStateToKeyAction(const Common::KeyState &keyState) { + switch (keyState.keycode) { + case Common::KEYCODE_ESCAPE: + return kKeyActionSkip; + case Common::KEYCODE_KP8: + if (keyState.flags & Common::KBD_NUM) { + break; + } + // Fallthrough + case Common::KEYCODE_UP: + return kKeyActionMoveForward; + case Common::KEYCODE_KP7: + if (keyState.flags & Common::KBD_NUM) { + break; + } + return kKeyActionMoveForwardLeft; + case Common::KEYCODE_KP9: + if (keyState.flags & Common::KBD_NUM) { + break; + } + return kKeyActionMoveForwardRight; + case Common::KEYCODE_KP4: + if (keyState.flags & Common::KBD_NUM) { + break; + } + // Fallthrough + case Common::KEYCODE_LEFT: + return kKeyActionMoveLeft; + case Common::KEYCODE_KP6: + if (keyState.flags & Common::KBD_NUM) { + break; + } + // Fallthrough + case Common::KEYCODE_RIGHT: + return kKeyActionMoveRight; + case Common::KEYCODE_KP2: + if (keyState.flags & Common::KBD_NUM) { + break; + } + // Fallthrough + case Common::KEYCODE_DOWN: + return kKeyActionMoveBack; + case Common::KEYCODE_PAGEUP: + return kKeyActionLookUp; + case Common::KEYCODE_PAGEDOWN: + return kKeyActionLookDown; + default: + break; + } + + return kKeyActionNone; } Common::Point RivenStack::getMousePosition() const { diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h index 740dff8adc..ce7d2e203d 100644 --- a/engines/mohawk/riven_stack.h +++ b/engines/mohawk/riven_stack.h @@ -70,6 +70,20 @@ private: Common::Array<uint16> _index; }; +/** Actions that can be performed using the keyboard */ +enum RivenKeyAction { + kKeyActionNone, + kKeyActionSkip, + kKeyActionMoveForward, + kKeyActionMoveForwardLeft, + kKeyActionMoveForwardRight, + kKeyActionMoveLeft, + kKeyActionMoveRight, + kKeyActionMoveBack, + kKeyActionLookUp, + kKeyActionLookDown +}; + /** * A game level * @@ -152,13 +166,13 @@ public: void mouseForceUp(); /** Handle a key press event */ - void onKeyPressed(const Common::KeyCode keyCode); + void onKeyPressed(const Common::KeyState &keyState); - /** Get the pressed keyboard key if any */ - Common::KeyCode keyGetPressed() const; + /** Get the action for the pressed keyboard key, if any */ + RivenKeyAction keyGetAction() const; /** Force the keyboard to be considered unpressed until the next key press */ - void keyForceUp(); + void keyResetAction(); // Common external commands void xflies(const ArgumentArray &args); // Start the "flies" effect @@ -210,7 +224,8 @@ private: CommandsMap _commands; - Common::KeyCode _keyPressed; + RivenKeyAction _keyAction; + RivenKeyAction mapKeyStateToKeyAction(const Common::KeyState &keyState); bool _mouseIsDown; Common::Point _mousePosition; diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp index 6a29145e4a..eeff81005b 100644 --- a/engines/mohawk/riven_stacks/jspit.cpp +++ b/engines/mohawk/riven_stacks/jspit.cpp @@ -552,7 +552,7 @@ void JSpit::sunnersPlayVideo(RivenVideo *video, uint32 destCardGlobalId, bool su while (!video->endOfVideo() && !_vm->hasGameEnded()) { _vm->doFrame(); - if (mouseIsDown()) { + if (mouseIsDown() || keyGetAction() == kKeyActionMoveForward) { video->stop(); if (sunnersShouldFlee) { diff --git a/engines/mohawk/riven_video.cpp b/engines/mohawk/riven_video.cpp index 6923fbd4d1..14af3e566b 100644 --- a/engines/mohawk/riven_video.cpp +++ b/engines/mohawk/riven_video.cpp @@ -241,14 +241,14 @@ void RivenVideo::playBlocking(int32 endTime) { _vm->doFrame(); // Handle skipping - if (playTillEnd && _vm->getStack()->keyGetPressed() == Common::KEYCODE_ESCAPE) { + if (playTillEnd && _vm->getStack()->keyGetAction() == kKeyActionSkip) { continuePlaying = false; // Seek to the last frame _video->seek(_video->getDuration().addMsecs(-1)); _vm->getStack()->mouseForceUp(); - _vm->getStack()->keyForceUp(); + _vm->getStack()->keyResetAction(); } } |