aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/mohawk/riven.cpp4
-rw-r--r--engines/mohawk/riven_card.cpp192
-rw-r--r--engines/mohawk/riven_card.h14
-rw-r--r--engines/mohawk/riven_scripts.cpp1
-rw-r--r--engines/mohawk/riven_stack.cpp76
-rw-r--r--engines/mohawk/riven_stack.h25
-rw-r--r--engines/mohawk/riven_stacks/jspit.cpp2
-rw-r--r--engines/mohawk/riven_video.cpp4
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();
}
}