[Scummvm-git-logs] scummvm master -> 4bdd58948d295848d7bcbf80b5d3935ed8e61e14

sev- sev at scummvm.org
Mon Jul 3 08:51:06 CEST 2017


This automated email contains information about 92 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
9b9fa7e0a5 MOHAWK: Refactor the script manager to read data to Command classes
f944b62988 MOHAWK: Change Riven's scripts not to have a type
d625b4dbd6 MOHAWK: Rename Riven's script types
bc01a276f9 MOHAWK: Add documentation to Riven's script module
3c2ca08877 MOHAWK: Add a script queue to Riven's script manager
0aaa3760c2 MOHAWK: Move Riven's Card to a separate object
abe6889bbe MOHAWK: Remove the current card id from the Riven engine class
1bb5424ddd MOHAWK: Move PLST handling to the RivenCard class
2fbe284a31 MOHAWK: Chane Riven's graphics manager to automatically handle screen updates
1b062d1e39 MOHAWK: Move the sound lists to RivenCard
23bbf05c91 MOHAWK: Start converting RivenHotspot into a class
6b988670e8 MOHAWK: Turn the active hotspot into a pointer
17f1903833 MOHAWK: Remove the RivenHotspot enabled field
67d9a3c71b MOHAWK: Make the RivenHotspot fields private
099b3b3d8f MOHAWK: Move the hotspot list to RivenCard
4bdf88496d MOHAWK: Move Riven's name lists to a separate class
c1e0b8684b MOHAWK: Move BLST list handling to RivenCard
871516a969 MOHAWK: Move the effect list to RivenCard
c1331e124f MOHAWK: Move the current hotspot to RivenCard
9b2c90c0b3 MOHAWK: The ignoreNextMouseUp workaround is not necessary anymore
aa0c89da03 MOHAWK: Move running the card leave script to the RivenCard destructor
f752066a8e MOHAWK: Introduce a new RivenStack class
3c8decec0a MOHAWK: Move the resource names to RivenStack
670a3c4558 MOHAWK: Move card id remapping to RivenStack
05bed84a85 MOHAWK: Rename the card and stack accessors
9926475937 MOHAWK: Update the card and stack variables when entering new locations
ab9b241e50 MOHAWK: Card event handlers could be called recursively
9ab0d53cd3 MOHAWK: Add console commands to dump Riven cards and stacks to stdout
c623a76767 MOHAWK: Ensure constructing and deleting cards does not have side effects
3f1f407c14 MOHAWK: Remove VideoHandle usage
1a5b2a1e50 MOHAWK: Move MLST loading to RivenCard
8fcebc12c6 MOHAWK: Fix dumping Riven external commands' arguments
e9b67081c3 MOHAWK: Introduce the effects intermediary screen
85712e56c8 MOHAWK: Only allow a single pixel format in Riven to simplify the implementation
ab2d151541 MOHAWK: Implement the (fire)flies effect mainly used in jungle island
e2c5609e81 MOHAWK: Prepare empty classes for the Riven stacks
14bbf8aab4 MOHAWK: Move the external commands to their respective stacks
6d4260719c MOHAWK: Add a convenience method for creating a script with a single command
e7146c9bf7 MOHAWK: Move the changeToStack command to a dedicated class
c04edb8f54 MOHAWK: Change the back from book commands to use scripts
efcf38f95f MOHAWK: Factor out stack name-id mapping
22926a1835 MOHAWK: Move the timer callbacks to the stacks
0ba035eea6 MOHAWK: Move Riven inventory code to a new class
79e086ba7b MOHAWK: Turn script commands into SharedPtrs
92e03a7b68 MOHAWK: Add a command to check if background scripts are running
286cdef658 MOHAWK: Add basic mouse handling to RivenStack
7a9b91dfcd MOHAWK: Improve script debug output
313d53234b MOHAWK: Be case insensitive when matching resource names
14990dc91b MOHAWK: Remove a hack that should not be needed anymore
f334e6e38a MOHAWK: Add sound effect related methods
7609ec0de8 MOHAWK: Use explicit bitmap names for the dome sliders
f0267d542f MOHAWK: Keep turning pages while the mouse is pressed in Atrus' book
ae6f248616 MOHAWK: Move Riven's sunner alert handling to the jungle stack
1286e7fcf0 MOHAWK: Use an enum for Riven's transition types
3f58a795e7 MOHAWK: Add an enum for Riven's command types
3900597996 MOHAWK: Implement card transitions for Riven
aa32c5e584 MOHAWK: Pass rects by const reference in Riven's graphics manager
ad7f94f10f MOHAWK: Add a transition speed widget to the settings dialog
1aa4233802 MOHAWK: Rework stack frame updates to work like the original
9153393219 MOHAWK: Allow games to opt out of the default video manager
f977b57123 MOHAWK: Rewrite the Riven movie manager
44943e1285 MOHAWK: Change the delay function not to have an event loop
637a08bcf3 MOHAWK: Don't allow loading while a script is running
61d78e33e1 MOHAWK: Don't call updateScreen when setting the cursor
23c597ab12 MOHAWK: Print variable names in assignments when dumping scripts
42f91b9769 MOHAWK: Simplify the stored movie script opcode
121c0ee08c MOHAWK: Don't update the screen immediatly after drawing
006dcf6a74 MOHAWK: Check the Jungle island external commands against the original
39b0d53bb5 MOHAWK: Fix incorrect loop in sound manager
6b4fe224db MOHAWK: Check the Book making island external commands against the original
2a444d35e8 MOHAWK: Check the Temple island external commands against the original
ee70244fbf MOHAWK: Check the Garden island external commands against the original
ea303ab682 MOHAWK: Switch timers to script commands
0f79e423d8 MOHAWK: Check the Prison island external commands against the original
2f7a04ae95 MOHAWK: Check the Rebel island external commands against the original
8ad53851bc MOHAWK: Implement interrupting scripts for the new script manager
d7b241abdc MOHAWK: Check the Office island external commands against the original
08e642314e MOHAWK: Check the Books external commands against the original
f29197e32c MOHAWK: Don't allow string patterns when looking for resources
dd4c0bcbeb MOHAWK: Set the proper mixer sound types for Riven
672cfbd518 MOHAWK: Set GUI option flags to disable unneeded settings for Riven
b9a72ff7a6 MOHAWK: Preload all the PE cursors on startup
88d594538d MOHAWK: Document unused MLST record fields
64c1a1d2b2 MOHAWK: Switch enabling the debug rectangles to a console var
8c6cd98067 MOHAWK: Fix the inventory being visible when scripts are running
7801d6489b MOHAWK: Reenable the Riven demo specific features
b0ee5772fb MOHAWK: Remove unused / not working functions
723b7d7a4c MOHAWK: Add some missing public interface comments
95951eebf7 MOHAWK: Get rid of refreshCard
b552719a81 MOHAWK: Switch external command arguments to Common::Array
c4f9423137 MOHAWK: Switch SimpleCommand arguments to Common::Array
4bdd58948d MOHAWK: Build Riven by default


Commit: 9b9fa7e0a5f92c1b3b3a1252af5ed82a5911505d
    https://github.com/scummvm/scummvm/commit/9b9fa7e0a5f92c1b3b3a1252af5ed82a5911505d
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Refactor the script manager to read data to Command classes

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index f08eee9..82173c2 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -617,10 +617,9 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 		debugN("==================================\n\n");
 		Common::SeekableReadStream *cardStream = _vm->getResource(MKTAG('C','A','R','D'), (uint16)atoi(argv[3]));
 		cardStream->seek(4);
-		RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream, false);
+		RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream);
 		for (uint32 i = 0; i < scriptList.size(); i++) {
 			scriptList[i]->dumpScript(varNames, xNames, 0);
-			delete scriptList[i];
 		}
 		delete cardStream;
 	} else if (!scumm_stricmp(argv[2], "HSPT")) {
@@ -635,10 +634,9 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 		for (uint16 i = 0; i < hotspotCount; i++) {
 			debugN("Hotspot %d:\n", i);
 			hsptStream->seek(22, SEEK_CUR);	// Skip non-script related stuff
-			RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream, false);
+			RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream);
 			for (uint32 j = 0; j < scriptList.size(); j++) {
 				scriptList[j]->dumpScript(varNames, xNames, 1);
-				delete scriptList[j];
 			}
 		}
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 12b4851..61ceb41 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -697,7 +697,8 @@ void MohawkEngine_Riven::runCardScript(uint16 scriptType) {
 	assert(_cardData.hasData);
 	for (uint16 i = 0; i < _cardData.scripts.size(); i++)
 		if (_cardData.scripts[i]->getScriptType() == scriptType) {
-			_cardData.scripts[i]->runScript();
+			RivenScriptPtr script = _cardData.scripts[i];
+			script->runScript();
 			break;
 		}
 }
@@ -706,7 +707,8 @@ void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
 	assert(hotspot < _hotspotCount);
 	for (uint16 i = 0; i < _hotspots[hotspot].scripts.size(); i++)
 		if (_hotspots[hotspot].scripts[i]->getScriptType() == scriptType) {
-			_hotspots[hotspot].scripts[i]->runScript();
+			RivenScriptPtr script = _hotspots[hotspot].scripts[i];
+			script->runScript();
 			break;
 		}
 }
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 3655452..8e5207f 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -34,60 +34,151 @@
 
 namespace Mohawk {
 
-RivenScript::RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard)
-	: _vm(vm), _stream(stream), _scriptType(scriptType), _parentStack(parentStack), _parentCard(parentCard) {
-	setupOpcodes();
-	_isRunning = _continueRunning = false;
+static void printTabs(byte tabs) {
+	for (byte i = 0; i < tabs; i++)
+		debugN("\t");
 }
 
-RivenScript::~RivenScript() {
-	delete _stream;
-}
-
-uint32 RivenScript::calculateCommandSize(Common::SeekableReadStream* script) {
-	uint16 command = script->readUint16BE();
-	uint32 commandSize = 2;
-	if (command == 8) {
-		if (script->readUint16BE() != 2) // Arg count?
-			warning ("if-then-else unknown value is not 2");
-		script->readUint16BE();								// variable to check against
-		uint16 logicBlockCount = script->readUint16BE();	// number of logic blocks
-		commandSize += 6;									// 2 + variable + logicBlocks
-
-		for (uint16 i = 0; i < logicBlockCount; i++) {
-			script->readUint16BE(); // Block variable
-			uint16 logicBlockLength = script->readUint16BE();
-			commandSize += 4;
-			for (uint16 j = 0; j < logicBlockLength; j++)
-				commandSize += calculateCommandSize(script);
-		}
+RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) {
+	_vm = vm;
+	_storedMovieOpcode.time = 0;
+	_storedMovieOpcode.id = 0;
+}
+
+RivenScriptManager::~RivenScriptManager() {
+	clearStoredMovieOpcode();
+}
+
+RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream, uint16 scriptType) {
+	RivenScriptPtr script = RivenScriptPtr(new RivenScript(_vm, scriptType));
+
+	uint16 commandCount = stream->readUint16BE();
+
+	for (uint16 i = 0; i < commandCount; i++) {
+		RivenCommand *command = readCommand(stream);
+		script->addCommand(command);
+	}
+
+	return script;
+}
+
+RivenCommand *RivenScriptManager::readCommand(Common::ReadStream *stream) {
+	uint16 type = stream->readUint16BE();
+
+	if (type == 8) {
+		return RivenSwitchCommand::createFromStream(_vm, type, stream);
 	} else {
-		uint16 argCount = script->readUint16BE();
-		commandSize += 2;
-		for (uint16 i = 0; i < argCount; i++) {
-			script->readUint16BE();
-			commandSize += 2;
-		}
+		return RivenSimpleCommand::createFromStream(_vm, type, stream);
+	}
+}
+
+RivenScriptList RivenScriptManager::readScripts(Common::ReadStream *stream) {
+	RivenScriptList scriptList;
+
+	uint16 scriptCount = stream->readUint16BE();
+	for (uint16 i = 0; i < scriptCount; i++) {
+		uint16 scriptType = stream->readUint16BE();
+		RivenScriptPtr script = readScript(stream, scriptType);
+		scriptList.push_back(script);
 	}
 
-	return commandSize;
+	return scriptList;
+}
+
+void RivenScriptManager::stopAllScripts() {
+// TODO: Restore
+//	for (uint32 i = 0; i < _currentScripts.size(); i++)
+//		_currentScripts[i]->stopRunning();
+}
+
+void RivenScriptManager::setStoredMovieOpcode(const StoredMovieOpcode &op) {
+	clearStoredMovieOpcode();
+	_storedMovieOpcode.script = op.script;
+	_storedMovieOpcode.id = op.id;
+	_storedMovieOpcode.time = op.time;
+}
+
+void RivenScriptManager::runStoredMovieOpcode() {
+	if (_storedMovieOpcode.script) {
+		_storedMovieOpcode.script->runScript();
+		clearStoredMovieOpcode();
+	}
+}
+
+void RivenScriptManager::clearStoredMovieOpcode() {
+	_storedMovieOpcode.script = RivenScriptPtr();
+	_storedMovieOpcode.time = 0;
+	_storedMovieOpcode.id = 0;
+}
+
+RivenScript::RivenScript(MohawkEngine_Riven *vm, uint16 scriptType) :
+		_vm(vm),
+		_scriptType(scriptType) {
+	_continueRunning = true;
+}
+
+RivenScript::~RivenScript() {
+	for (uint i = 0; i < _commands.size(); i ++) {
+		delete _commands[i];
+	}
+}
+
+void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
+	printTabs(tabs); debugN("Stream Type %d:\n", _scriptType);
+	dumpCommands(varNames, xNames, tabs + 1);
+}
+
+void RivenScript::dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
+	for (uint16 i = 0; i < _commands.size(); i++) {
+		_commands[i]->dump(varNames, xNames, tabs);
+	}
+}
+
+void RivenScript::runScript() {
+	for (uint16 i = 0; i < _commands.size() && _continueRunning; i++) {
+		_commands[i]->execute();
+	}
+}
+
+void RivenScript::addCommand(RivenCommand *command) {
+	_commands.push_back(command);
+}
+
+RivenCommand::RivenCommand(MohawkEngine_Riven *vm) :
+		_vm(vm) {
+
+}
+
+RivenCommand::~RivenCommand() {
+
+}
+
+RivenSimpleCommand::RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments) :
+		RivenCommand(vm),
+		_type(type),
+		_arguments(arguments) {
+	setupOpcodes();
+}
+
+RivenSimpleCommand::~RivenSimpleCommand() {
 }
 
-uint32 RivenScript::calculateScriptSize(Common::SeekableReadStream* script) {
-	uint32 oldPos = script->pos();
-	uint16 commandCount = script->readUint16BE();
-	uint16 scriptSize = 2; // 2 for command count
+RivenSimpleCommand *RivenSimpleCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) {
+	uint16 argCount = stream->readUint16BE();
 
-	for (uint16 i = 0; i < commandCount; i++)
-		scriptSize += calculateCommandSize(script);
+	Common::Array<uint16> arguments;
+	arguments.resize(argCount);
 
-	script->seek(oldPos);
-	return scriptSize;
+	for (uint16 i = 0; i < argCount; i++) {
+		arguments[i] = stream->readUint16BE();
+	}
+
+	return new RivenSimpleCommand(vm, type, arguments);
 }
 
-#define OPCODE(x) { &RivenScript::x, #x }
+#define OPCODE(x) { &RivenSimpleCommand::x, #x }
 
-void RivenScript::setupOpcodes() {
+void RivenSimpleCommand::setupOpcodes() {
 	static const RivenOpcode riven_opcodes[] = {
 		// 0x00 (0 decimal)
 		OPCODE(empty),
@@ -154,139 +245,12 @@ void RivenScript::setupOpcodes() {
 	_opcodes = riven_opcodes;
 }
 
-static void printTabs(byte tabs) {
-	for (byte i = 0; i < tabs; i++)
-		debugN("\t");
-}
-
-void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
-	if (_stream->pos() != 0)
-		_stream->seek(0);
-
-	printTabs(tabs); debugN("Stream Type %d:\n", _scriptType);
-	dumpCommands(varNames, xNames, tabs + 1);
-}
-
-void RivenScript::dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
-	uint16 commandCount = _stream->readUint16BE();
-
-	for (uint16 i = 0; i < commandCount; i++) {
-		uint16 command = _stream->readUint16BE();
-
-		if (command == 8) { // "Switch" Statement
-			if (_stream->readUint16BE() != 2)
-				warning ("if-then-else unknown value is not 2");
-			uint16 var = _stream->readUint16BE();
-			printTabs(tabs); debugN("switch (%s) {\n", varNames[var].c_str());
-			uint16 logicBlockCount = _stream->readUint16BE();
-			for (uint16 j = 0; j < logicBlockCount; j++) {
-				uint16 varCheck = _stream->readUint16BE();
-				printTabs(tabs + 1);
-				if (varCheck == 0xFFFF)
-					debugN("default:\n");
-				else
-					debugN("case %d:\n", varCheck);
-				dumpCommands(varNames, xNames, tabs + 2);
-				printTabs(tabs + 2); debugN("break;\n");
-			}
-			printTabs(tabs); debugN("}\n");
-		} else if (command == 7) { // Use the variable name
-			_stream->readUint16BE(); // Skip the opcode var count
-			printTabs(tabs);
-			uint16 var = _stream->readUint16BE();
-			debugN("%s = %d;\n", varNames[var].c_str(), _stream->readUint16BE());
-		} else if (command == 17) { // Use the external command name
-			_stream->readUint16BE(); // Skip the opcode var count
-			printTabs(tabs);
-			debugN("%s(", xNames[_stream->readUint16BE()].c_str());
-			uint16 varCount = _stream->readUint16BE();
-			for (uint16 j = 0; j < varCount; j++) {
-				debugN("%d", _stream->readUint16BE());
-				if (j != varCount - 1)
-					debugN(", ");
-			}
-			debugN(");\n");
-		} else if (command == 24) { // Use the variable name
-			_stream->readUint16BE(); // Skip the opcode var count
-			printTabs(tabs);
-			uint16 var = _stream->readUint16BE();
-			debugN("%s += %d;\n", varNames[var].c_str(), _stream->readUint16BE());
-		} else {
-			printTabs(tabs);
-			uint16 varCount = _stream->readUint16BE();
-			debugN("%s(", _opcodes[command].desc);
-			for (uint16 j = 0; j < varCount; j++) {
-				debugN("%d", _stream->readUint16BE());
-				if (j != varCount - 1)
-					debugN(", ");
-			}
-			debugN(");\n");
-		}
-	}
-}
-
-void RivenScript::runScript() {
-	_isRunning = _continueRunning = true;
-
-	if (_stream->pos() != 0)
-		_stream->seek(0);
-
-	processCommands(true);
-	_isRunning = false;
-}
-
-void RivenScript::processCommands(bool runCommands) {
-	bool runBlock = true;
-
-	uint16 commandCount = _stream->readUint16BE();
-
-	for (uint16 j = 0; j < commandCount && !_vm->shouldQuit() && _stream->pos() < _stream->size() && _continueRunning; j++) {
-		uint16 command = _stream->readUint16BE();
-
-		if (command == 8) {
-			// Command 8 contains a conditional branch, similar to switch statements
-			if (_stream->readUint16BE() != 2)
-				warning("if-then-else unknown value is not 2");
-
-			uint16 var = _stream->readUint16BE();				// variable to check against
-			uint16 logicBlockCount = _stream->readUint16BE();	// number of logic blocks
-			bool anotherBlockEvaluated = false;
-
-			for (uint16 k = 0; k < logicBlockCount; k++) {
-				uint16 checkValue = _stream->readUint16BE();	// variable for this logic block
-
-				// Run the following block if the block's variable is equal to the variable to check against
-				// Don't run it if the parent block is not executed
-				// And don't run it if another block has already evaluated to true (needed for the default case)
-				runBlock = (_vm->getStackVar(var) == checkValue || checkValue == 0xffff) && runCommands && !anotherBlockEvaluated;
-				processCommands(runBlock);
-
-				if (runBlock)
-					anotherBlockEvaluated = true;
-			}
-		} else {
-			uint16 argCount = _stream->readUint16BE();
-			uint16 *argValues = new uint16[argCount];
-
-			for (uint16 k = 0; k < argCount; k++)
-				argValues[k] = _stream->readUint16BE();
-
-			if (runCommands) {
-				debug (4, "Running opcode %04x, argument count %d", command, argCount);
-				(this->*(_opcodes[command].proc)) (command, argCount, argValues);
-			}
-
-			delete[] argValues;
-		}
-	}
-}
-
 ////////////////////////////////
 // Opcodes
 ////////////////////////////////
 
 // Command 1: draw tBMP resource (tbmp_id, left, top, right, bottom, u0, u1, u2, u3)
-void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
 	if (argc < 5) // Copy the image to the whole screen, ignoring the rest of the parameters
 		_vm->_gfx->copyImageToScreen(argv[0], 0, 0, 608, 392);
 	else          // Copy the image to a certain part of the screen
@@ -297,18 +261,19 @@ void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 2: go to card (card id)
-void RivenScript::switchCard(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::switchCard(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->changeToCard(argv[0]);
 
 	// WORKAROUND: If we changed card on a mouse down event,
 	// we want to ignore the next mouse up event so we don't
 	// change card when lifting the mouse on the next card.
-	if (_scriptType == kMouseDownScript)
-		_vm->ignoreNextMouseUp();
+// TODO: Restore
+//	if (_scriptType == kMouseDownScript)
+//		_vm->ignoreNextMouseUp();
 }
 
 // Command 3: play an SLST from the script
-void RivenScript::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) {
 	int offset = 0, j = 0;
 	uint16 soundCount = argv[offset++];
 
@@ -342,7 +307,7 @@ void RivenScript::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 4: play local tWAV resource (twav_id, volume, block)
-void RivenScript::playSound(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::playSound(uint16 op, uint16 argc, uint16 *argv) {
 	uint16 volume = argv[1];
 	bool playOnDraw = argv[2] == 1;
 
@@ -350,17 +315,17 @@ void RivenScript::playSound(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 7: set variable value (variable, value)
-void RivenScript::setVariable(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::setVariable(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->getStackVar(argv[0]) = argv[1];
 }
 
 // Command 8: conditional branch
-void RivenScript::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) {
 	// dummy function, this opcode does logic checking in processCommands()
 }
 
 // Command 9: enable hotspot (blst_id)
-void RivenScript::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 	for (uint16 i = 0; i < _vm->getHotspotCount(); i++) {
 		if (_vm->_hotspots[i].blstID == argv[0]) {
 			debug(2, "Enabling hotspot with BLST ID %d", argv[0]);
@@ -373,7 +338,7 @@ void RivenScript::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 10: disable hotspot (blst_id)
-void RivenScript::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 	for (uint16 i = 0; i < _vm->getHotspotCount(); i++) {
 		if (_vm->_hotspots[i].blstID == argv[0]) {
 			debug(2, "Disabling hotspot with BLST ID %d", argv[0]);
@@ -386,7 +351,7 @@ void RivenScript::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 12: stop sounds (flags)
-void RivenScript::stopSound(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::stopSound(uint16 op, uint16 argc, uint16 *argv) {
 	// WORKAROUND: The Play Riven/Visit Riven/Start New Game buttons
 	// in the main menu call this function to stop ambient sounds
 	// after the change stack call to Temple Island. However, this
@@ -408,21 +373,21 @@ void RivenScript::stopSound(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 13: set mouse cursor (cursor_id)
-void RivenScript::changeCursor(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::changeCursor(uint16 op, uint16 argc, uint16 *argv) {
 	debug(2, "Change to cursor %d", argv[0]);
 	_vm->_cursor->setCursor(argv[0]);
 	_vm->_system->updateScreen();
 }
 
 // Command 14: pause script execution (delay in ms, u1)
-void RivenScript::delay(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::delay(uint16 op, uint16 argc, uint16 *argv) {
 	debug(2, "Delay %dms", argv[0]);
 	if (argv[0] > 0)
 		_vm->delayAndUpdate(argv[0]);
 }
 
 // Command 17: call external command
-void RivenScript::runExternalCommand(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::runExternalCommand(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_externalScriptHandler->runCommand(argc, argv);
 }
 
@@ -430,7 +395,7 @@ void RivenScript::runExternalCommand(uint16 op, uint16 argc, uint16 *argv) {
 // Note that this opcode has 1 or 5 parameters, depending on argc
 // Parameter 0: transition type
 // Parameters 1-4: transition rectangle
-void RivenScript::transition(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::transition(uint16 op, uint16 argc, uint16 *argv) {
 	if (argc == 1)
 		_vm->_gfx->scheduleTransition(argv[0]);
 	else
@@ -438,31 +403,31 @@ void RivenScript::transition(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 19: reload card
-void RivenScript::refreshCard(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::refreshCard(uint16 op, uint16 argc, uint16 *argv) {
 	debug(2, "Refreshing card");
 	_vm->refreshCard();
 }
 
 // Command 20: disable screen update
-void RivenScript::disableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::disableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
 	debug(2, "Screen update disabled");
 	_vm->_gfx->_updatesEnabled = false;
 }
 
 // Command 21: enable screen update
-void RivenScript::enableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::enableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
 	debug(2, "Screen update enabled");
 	_vm->_gfx->_updatesEnabled = true;
 	_vm->_gfx->updateScreen();
 }
 
 // Command 24: increment variable (variable, value)
-void RivenScript::incrementVariable(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::incrementVariable(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->getStackVar(argv[0]) += argv[1];
 }
 
 // Command 27: go to stack (stack name, code high, code low)
-void RivenScript::changeStack(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
 	Common::String stackName = _vm->getName(StackNames, argv[0]);
 	int8 index = -1;
 
@@ -482,54 +447,54 @@ void RivenScript::changeStack(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 28: disable a movie
-void RivenScript::disableMovie(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::disableMovie(uint16 op, uint16 argc, uint16 *argv) {
 	VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]);
 	if (handle)
 		handle->setEnabled(false);
 }
 
 // Command 29: disable all movies
-void RivenScript::disableAllMovies(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::disableAllMovies(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_video->disableAllMovies();
 }
 
 // Command 31: enable a movie
-void RivenScript::enableMovie(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::enableMovie(uint16 op, uint16 argc, uint16 *argv) {
 	VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]);
 	if (handle)
 		handle->setEnabled(true);
 }
 
 // Command 32: play foreground movie - blocking (movie_id)
-void RivenScript::playMovieBlocking(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::playMovieBlocking(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_cursor->hideCursor();
 	_vm->_video->playMovieBlockingRiven(argv[0]);
 	_vm->_cursor->showCursor();
 }
 
 // Command 33: play background movie - nonblocking (movie_id)
-void RivenScript::playMovie(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::playMovie(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_video->playMovieRiven(argv[0]);
 }
 
 // Command 34: stop a movie
-void RivenScript::stopMovie(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::stopMovie(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_video->stopMovieRiven(argv[0]);
 }
 
 // Command 36: unknown
-void RivenScript::unk_36(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::unk_36(uint16 op, uint16 argc, uint16 *argv) {
 	debug(0, "unk_36: Ignoring");
 }
 
 // Command 37: fade ambient sounds
-void RivenScript::fadeAmbientSounds(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::fadeAmbientSounds(uint16 op, uint16 argc, uint16 *argv) {
 	// Similar to stopSound(), but does fading
 	_vm->_sound->stopAllSLST(true);
 }
 
 // Command 38: Store an opcode for use when playing a movie (movie id, time high, time low, opcode, arguments...)
-void RivenScript::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) {
 	// This opcode is used to delay an opcode's usage based on the elapsed
 	// time of a specified movie. However, every use in the game is for
 	// delaying an activateSLST opcode.
@@ -547,7 +512,7 @@ void RivenScript::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) {
 
 	// Build a script out of 'er
 	Common::SeekableReadStream *scriptStream = new Common::MemoryReadStream(scriptBuf, scriptSize, DisposeAfterUse::YES);
-	RivenScript *script = new RivenScript(_vm, scriptStream, kStoredOpcodeScript, getParentStack(), getParentCard());
+	RivenScriptPtr script = _vm->_scriptMan->readScript(scriptStream, kStoredOpcodeScript);
 
 	uint32 delayTime = (argv[1] << 16) + argv[2];
 
@@ -563,21 +528,23 @@ void RivenScript::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) {
 	} else {
 		// Run immediately if we have no delay
 		script->runScript();
-		delete script;
 	}
+
+	delete scriptStream;
 }
 
 // Command 39: activate PLST record (card picture lists)
-void RivenScript::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_gfx->drawPLST(argv[0]);
 
 	// An update is automatically sent here as long as it's not a load or update script and updates are enabled.
-	if (_scriptType != kCardLoadScript && _scriptType != kCardUpdateScript)
+	// TODO: Fix the graphics manager
+	//if (_scriptType != kCardLoadScript && _scriptType != kCardUpdateScript)
 		_vm->_gfx->updateScreen();
 }
 
 // Command 40: activate SLST record (card ambient sound lists)
-void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 	// WORKAROUND: Disable the SLST that is played during Riven's intro.
 	// Riven X does this too (spoke this over with Jeff)
 	if (_vm->getCurStack() == kStackTspit && _vm->getCurCardRMAP() == 0x6e9a && argv[0] == 2)
@@ -588,13 +555,13 @@ void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 41: activate MLST record and play
-void RivenScript::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_video->activateMLST(argv[0], _vm->getCurCard());
 	_vm->_video->playMovieRiven(argv[0]);
 }
 
 // Command 43: activate BLST record (card hotspot enabling lists)
-void RivenScript::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 	Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, _vm->getCurCard());
 	uint16 recordCount = blst->readUint16BE();
 
@@ -616,7 +583,7 @@ void RivenScript::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 44: activate FLST record (information on which SFXE resource this card should use)
-void RivenScript::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
 	Common::SeekableReadStream* flst = _vm->getResource(ID_FLST, _vm->getCurCard());
 	uint16 recordCount = flst->readUint16BE();
 
@@ -637,7 +604,7 @@ void RivenScript::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 45: do zip mode
-void RivenScript::zipMode(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
 	// Check the ZIPS records to see if we have a match to the hotspot name
 	Common::String hotspotName = _vm->getHotspotName(_vm->getCurHotspot());
 
@@ -649,81 +616,120 @@ void RivenScript::zipMode(uint16 op, uint16 argc, uint16 *argv) {
 }
 
 // Command 46: activate MLST record (movie lists)
-void RivenScript::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
+void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_video->activateMLST(argv[0], _vm->getCurCard());
 }
 
-RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) {
-	_vm = vm;
-	_storedMovieOpcode.script = 0;
-	_storedMovieOpcode.time = 0;
-	_storedMovieOpcode.id = 0;
+void RivenSimpleCommand::dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
+	printTabs(tabs);
+
+	if (_type == 7) { // Use the variable name
+		uint16 var = _arguments[0];
+		debugN("%s = %d;\n", varNames[var].c_str(), _arguments[1]);
+	} else if (_type == 17) { // Use the external command name
+		debugN("%s(", xNames[_arguments[0]].c_str());
+		uint16 varCount = _arguments[1];
+		for (uint16 j = 0; j < varCount; j++) {
+			debugN("%d", _arguments[1 + j]);
+			if (j != varCount - 1)
+				debugN(", ");
+		}
+		debugN(");\n");
+	} else if (_type == 24) { // Use the variable name
+		uint16 var = _arguments[0];
+		debugN("%s += %d;\n", varNames[var].c_str(), _arguments[1]);
+	} else {
+		debugN("%s(", _opcodes[_type].desc);
+		for (uint16 j = 0; j < _arguments.size(); j++) {
+			debugN("%d", _arguments[j]);
+			if (j != _arguments.size() - 1)
+				debugN(", ");
+		}
+		debugN(");\n");
+	}
 }
 
-RivenScriptManager::~RivenScriptManager() {
-	for (uint32 i = 0; i < _currentScripts.size(); i++)
-		delete _currentScripts[i];
+void RivenSimpleCommand::execute() {
+	uint16 *argValues = new uint16[_arguments.size()];
 
-	clearStoredMovieOpcode();
+	for (uint16 k = 0; k < _arguments.size(); k++)
+		argValues[k] = _arguments[k];
+
+	debug (4, "Running opcode %04x, argument count %d", _type, _arguments.size());
+	(this->*(_opcodes[_type].proc)) (_type, _arguments.size(), argValues);
+
+	delete[] argValues;
 }
 
-RivenScriptList RivenScriptManager::readScripts(Common::SeekableReadStream *stream, bool garbageCollect) {
-	if (garbageCollect)
-		unloadUnusedScripts(); // Garbage collect!
+RivenSwitchCommand::RivenSwitchCommand(MohawkEngine_Riven *vm) :
+		RivenCommand(vm),
+		_variableId(0) {
 
-	RivenScriptList scriptList;
+}
 
-	uint16 scriptCount = stream->readUint16BE();
-	for (uint16 i = 0; i < scriptCount; i++) {
-		uint16 scriptType = stream->readUint16BE();
-		uint32 scriptSize = RivenScript::calculateScriptSize(stream);
-		RivenScript *script = new RivenScript(_vm, stream->readStream(scriptSize), scriptType, _vm->getCurStack(), _vm->getCurCard());
-		scriptList.push_back(script);
+RivenSwitchCommand::~RivenSwitchCommand() {
+
+}
+
+RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) {
+	RivenSwitchCommand *command = new RivenSwitchCommand(vm);
 
-		// Only add it to the scripts that we will free later if it is requested.
-		// (ie. we don't want to store scripts from the dumpScript console command)
-		if (garbageCollect)
-			_currentScripts.push_back(script);
+	if (stream->readUint16BE() != 2) {
+		// This value is not used in the original engine
+		warning("if-then-else unknown value is not 2");
 	}
 
-	return scriptList;
-}
+	// variable to check against
+	command->_variableId = stream->readUint16BE();
 
-void RivenScriptManager::stopAllScripts() {
-	for (uint32 i = 0; i < _currentScripts.size(); i++)
-		_currentScripts[i]->stopRunning();
-}
+	// number of logic blocks
+	uint16 logicBlockCount = stream->readUint16BE();
+	command->_branches.resize(logicBlockCount);
 
-void RivenScriptManager::unloadUnusedScripts() {
-	// Free any scripts that aren't part of the current card and aren't running
-	for (uint32 i = 0; i < _currentScripts.size(); i++) {
-		if ((_vm->getCurStack() != _currentScripts[i]->getParentStack() || _vm->getCurCard() != _currentScripts[i]->getParentCard()) && !_currentScripts[i]->isRunning()) {
-			delete _currentScripts[i];
-			_currentScripts.remove_at(i);
-			i--;
-		}
+	for (uint16 i = 0; i < logicBlockCount; i++) {
+		Branch &branch = command->_branches[i];
+
+		// Value for this logic block
+		branch.value = stream->readUint16BE();
+		branch.script = vm->_scriptMan->readScript(stream, kStoredOpcodeScript);
 	}
-}
 
-void RivenScriptManager::setStoredMovieOpcode(const StoredMovieOpcode &op) {
-	clearStoredMovieOpcode();
-	_storedMovieOpcode.script = op.script;
-	_storedMovieOpcode.id = op.id;
-	_storedMovieOpcode.time = op.time;
+	return command;
 }
 
-void RivenScriptManager::runStoredMovieOpcode() {
-	if (_storedMovieOpcode.script) {
-		_storedMovieOpcode.script->runScript();
-		clearStoredMovieOpcode();
+void RivenSwitchCommand::dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
+	printTabs(tabs); debugN("switch (%s) {\n", varNames[_variableId].c_str());
+	for (uint16 j = 0; j < _branches.size(); j++) {
+		printTabs(tabs + 1);
+		if (_branches[j].value == 0xFFFF)
+			debugN("default:\n");
+		else
+			debugN("case %d:\n", _branches[j].value);
+		_branches[j].script->dumpScript(varNames, xNames, tabs + 2);
+		printTabs(tabs + 2); debugN("break;\n");
 	}
+	printTabs(tabs); debugN("}\n");
 }
 
-void RivenScriptManager::clearStoredMovieOpcode() {
-	delete _storedMovieOpcode.script;
-	_storedMovieOpcode.script = 0;
-	_storedMovieOpcode.time = 0;
-	_storedMovieOpcode.id = 0;
+void RivenSwitchCommand::execute() {
+	// Get the switch variable value
+	uint32 value = _vm->getStackVar(_variableId);
+
+	// Look for a case matching the value
+	for (uint i = 0; i < _branches.size(); i++) {
+		if  (_branches[i].value == value) {
+			_branches[i].script->runScript();
+			return;
+		}
+	}
+
+	// Look for the default case if any
+	for (uint i = 0; i < _branches.size(); i++) {
+		if  (_branches[i].value == 0Xffff) {
+			_branches[i].script->runScript();
+			return;
+		}
+	}
 }
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index b669cd5..9163345 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -49,25 +49,93 @@ enum {
 };
 
 class MohawkEngine_Riven;
-class RivenScript;
+class RivenCommand;
 
 class RivenScript {
 public:
-	RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard);
+	RivenScript(MohawkEngine_Riven *vm, uint16 scriptType);
 	~RivenScript();
 
+	void addCommand(RivenCommand *command);
+
 	void runScript();
 	void dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
 	uint16 getScriptType() { return _scriptType; }
-	uint16 getParentStack() { return _parentStack; }
-	uint16 getParentCard() { return _parentCard; }
-	bool isRunning() { return _isRunning; }
 	void stopRunning() { _continueRunning = false; }
 
-	static uint32 calculateScriptSize(Common::SeekableReadStream *script);
+private:
+	MohawkEngine_Riven *_vm;
+
+	Common::Array<RivenCommand *> _commands;
+	uint16 _scriptType;
+	bool _continueRunning;
+
+	void dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
+};
+
+typedef Common::SharedPtr<RivenScript> RivenScriptPtr;
+typedef Common::Array<RivenScriptPtr> RivenScriptList;
+
+class RivenScriptManager {
+public:
+	RivenScriptManager(MohawkEngine_Riven *vm);
+	~RivenScriptManager();
+
+	RivenScriptPtr readScript(Common::ReadStream *stream, uint16 scriptType);
+	RivenScriptList readScripts(Common::ReadStream *stream);
+	void stopAllScripts();
+
+	struct StoredMovieOpcode {
+		RivenScriptPtr script;
+		uint32 time;
+		uint16 id;
+	};
+
+	uint16 getStoredMovieOpcodeID() { return _storedMovieOpcode.id; }
+	uint32 getStoredMovieOpcodeTime() { return _storedMovieOpcode.time; }
+	void setStoredMovieOpcode(const StoredMovieOpcode &op);
+	void runStoredMovieOpcode();
+	void clearStoredMovieOpcode();
 
 private:
-	typedef void (RivenScript::*OpcodeProcRiven)(uint16 op, uint16 argc, uint16 *argv);
+	MohawkEngine_Riven *_vm;
+
+	StoredMovieOpcode _storedMovieOpcode;
+
+	RivenCommand *readCommand(Common::ReadStream *stream);
+};
+
+class RivenCommand {
+public:
+	RivenCommand(MohawkEngine_Riven *vm);
+	virtual ~RivenCommand();
+
+	virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) = 0;
+
+	virtual void execute() = 0;
+
+protected:
+	MohawkEngine_Riven *_vm;
+};
+
+class RivenSimpleCommand : public RivenCommand {
+public:
+	static RivenSimpleCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);
+	virtual ~RivenSimpleCommand();
+
+	// RivenCommand API
+	virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) override;
+	virtual void execute() override;
+
+private:
+	typedef Common::Array<uint16> ArgumentArray;
+
+	RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments);
+
+	int _type;
+	ArgumentArray _arguments;
+
+	typedef void (RivenSimpleCommand::*OpcodeProcRiven)(uint16 op, uint16 argc, uint16 *argv);
 	struct RivenOpcode {
 		OpcodeProcRiven proc;
 		const char *desc;
@@ -75,16 +143,6 @@ private:
 	const RivenOpcode *_opcodes;
 	void setupOpcodes();
 
-	MohawkEngine_Riven *_vm;
-	Common::SeekableReadStream *_stream;
-	uint16 _scriptType, _parentStack, _parentCard;
-	bool _isRunning, _continueRunning;
-
-	void dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
-	void processCommands(bool runCommands);
-
-	static uint32 calculateCommandSize(Common::SeekableReadStream *script);
-
 	DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); }
 
 	//Opcodes
@@ -124,34 +182,25 @@ private:
 	DECLARE_OPCODE(activateMLST);
 };
 
-typedef Common::Array<RivenScript *> RivenScriptList;
-
-class RivenScriptManager {
+class RivenSwitchCommand : public RivenCommand {
 public:
-	RivenScriptManager(MohawkEngine_Riven *vm);
-	~RivenScriptManager();
+	static RivenSwitchCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);
+	virtual ~RivenSwitchCommand();
 
-	RivenScriptList readScripts(Common::SeekableReadStream *stream, bool garbageCollect = true);
-	void stopAllScripts();
-
-	struct StoredMovieOpcode {
-		RivenScript *script;
-		uint32 time;
-		uint16 id;
-	};
-
-	uint16 getStoredMovieOpcodeID() { return _storedMovieOpcode.id; }
-	uint32 getStoredMovieOpcodeTime() { return _storedMovieOpcode.time; }
-	void setStoredMovieOpcode(const StoredMovieOpcode &op);
-	void runStoredMovieOpcode();
-	void clearStoredMovieOpcode();
+	// RivenCommand API
+	virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) override;
+	virtual void execute() override;
 
 private:
-	void unloadUnusedScripts();
-	RivenScriptList _currentScripts;
-	MohawkEngine_Riven *_vm;
+	RivenSwitchCommand(MohawkEngine_Riven *vm);
 
-	StoredMovieOpcode _storedMovieOpcode;
+	struct Branch {
+		uint16 value;
+		RivenScriptPtr script;
+	};
+
+	uint16 _variableId;
+	Common::Array<Branch> _branches;
 };
 
 } // End of namespace Mohawk


Commit: f944b629881765626cedf37702f712bd30c8bdd3
    https://github.com/scummvm/scummvm/commit/f944b629881765626cedf37702f712bd30c8bdd3
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Change Riven's scripts not to have a type

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 82173c2..4d30008 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -619,7 +619,8 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 		cardStream->seek(4);
 		RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream);
 		for (uint32 i = 0; i < scriptList.size(); i++) {
-			scriptList[i]->dumpScript(varNames, xNames, 0);
+			debugN("Stream Type %d:\n", scriptList[i].type);
+			scriptList[i].script->dumpScript(varNames, xNames, 0);
 		}
 		delete cardStream;
 	} else if (!scumm_stricmp(argv[2], "HSPT")) {
@@ -636,7 +637,8 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 			hsptStream->seek(22, SEEK_CUR);	// Skip non-script related stuff
 			RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream);
 			for (uint32 j = 0; j < scriptList.size(); j++) {
-				scriptList[j]->dumpScript(varNames, xNames, 1);
+				debugN("\tStream Type %d:\n", scriptList[i].type);
+				scriptList[j].script->dumpScript(varNames, xNames, 1);
 			}
 		}
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 61ceb41..4d6c6fe 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -696,8 +696,8 @@ uint32 MohawkEngine_Riven::getCurCardRMAP() {
 void MohawkEngine_Riven::runCardScript(uint16 scriptType) {
 	assert(_cardData.hasData);
 	for (uint16 i = 0; i < _cardData.scripts.size(); i++)
-		if (_cardData.scripts[i]->getScriptType() == scriptType) {
-			RivenScriptPtr script = _cardData.scripts[i];
+		if (_cardData.scripts[i].type == scriptType) {
+			RivenScriptPtr script = _cardData.scripts[i].script;
 			script->runScript();
 			break;
 		}
@@ -706,8 +706,8 @@ void MohawkEngine_Riven::runCardScript(uint16 scriptType) {
 void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
 	assert(hotspot < _hotspotCount);
 	for (uint16 i = 0; i < _hotspots[hotspot].scripts.size(); i++)
-		if (_hotspots[hotspot].scripts[i]->getScriptType() == scriptType) {
-			RivenScriptPtr script = _hotspots[hotspot].scripts[i];
+		if (_hotspots[hotspot].scripts[i].type == scriptType) {
+			RivenScriptPtr script = _hotspots[hotspot].scripts[i].script;
 			script->runScript();
 			break;
 		}
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 8e5207f..6e5b811 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -49,8 +49,8 @@ RivenScriptManager::~RivenScriptManager() {
 	clearStoredMovieOpcode();
 }
 
-RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream, uint16 scriptType) {
-	RivenScriptPtr script = RivenScriptPtr(new RivenScript(_vm, scriptType));
+RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream) {
+	RivenScriptPtr script = RivenScriptPtr(new RivenScript(_vm));
 
 	uint16 commandCount = stream->readUint16BE();
 
@@ -77,8 +77,9 @@ RivenScriptList RivenScriptManager::readScripts(Common::ReadStream *stream) {
 
 	uint16 scriptCount = stream->readUint16BE();
 	for (uint16 i = 0; i < scriptCount; i++) {
-		uint16 scriptType = stream->readUint16BE();
-		RivenScriptPtr script = readScript(stream, scriptType);
+		RivenTypedScript script;
+		script.type = stream->readUint16BE();
+		script.script = readScript(stream);
 		scriptList.push_back(script);
 	}
 
@@ -111,9 +112,8 @@ void RivenScriptManager::clearStoredMovieOpcode() {
 	_storedMovieOpcode.id = 0;
 }
 
-RivenScript::RivenScript(MohawkEngine_Riven *vm, uint16 scriptType) :
-		_vm(vm),
-		_scriptType(scriptType) {
+RivenScript::RivenScript(MohawkEngine_Riven *vm) :
+		_vm(vm) {
 	_continueRunning = true;
 }
 
@@ -124,11 +124,6 @@ RivenScript::~RivenScript() {
 }
 
 void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
-	printTabs(tabs); debugN("Stream Type %d:\n", _scriptType);
-	dumpCommands(varNames, xNames, tabs + 1);
-}
-
-void RivenScript::dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
 	for (uint16 i = 0; i < _commands.size(); i++) {
 		_commands[i]->dump(varNames, xNames, tabs);
 	}
@@ -512,7 +507,7 @@ void RivenSimpleCommand::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv)
 
 	// Build a script out of 'er
 	Common::SeekableReadStream *scriptStream = new Common::MemoryReadStream(scriptBuf, scriptSize, DisposeAfterUse::YES);
-	RivenScriptPtr script = _vm->_scriptMan->readScript(scriptStream, kStoredOpcodeScript);
+	RivenScriptPtr script = _vm->_scriptMan->readScript(scriptStream);
 
 	uint32 delayTime = (argv[1] << 16) + argv[2];
 
@@ -691,7 +686,7 @@ RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm,
 
 		// Value for this logic block
 		branch.value = stream->readUint16BE();
-		branch.script = vm->_scriptMan->readScript(stream, kStoredOpcodeScript);
+		branch.script = vm->_scriptMan->readScript(stream);
 	}
 
 	return command;
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 9163345..95b62b6 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -43,9 +43,7 @@ enum {
 	kCardLoadScript = 6,
 	kCardLeaveScript = 7,
 	kCardOpenScript = 9,
-	kCardUpdateScript = 10,
-
-	kStoredOpcodeScript // This is ScummVM-only to denote the script from a storeMovieOpcode() call
+	kCardUpdateScript = 10
 };
 
 class MohawkEngine_Riven;
@@ -53,35 +51,37 @@ class RivenCommand;
 
 class RivenScript {
 public:
-	RivenScript(MohawkEngine_Riven *vm, uint16 scriptType);
+	RivenScript(MohawkEngine_Riven *vm);
 	~RivenScript();
 
 	void addCommand(RivenCommand *command);
 
 	void runScript();
 	void dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
-	uint16 getScriptType() { return _scriptType; }
 	void stopRunning() { _continueRunning = false; }
 
 private:
 	MohawkEngine_Riven *_vm;
 
 	Common::Array<RivenCommand *> _commands;
-	uint16 _scriptType;
 	bool _continueRunning;
-
-	void dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
 };
 
 typedef Common::SharedPtr<RivenScript> RivenScriptPtr;
-typedef Common::Array<RivenScriptPtr> RivenScriptList;
+
+struct RivenTypedScript {
+	uint16 type;
+	RivenScriptPtr script;
+};
+
+typedef Common::Array<RivenTypedScript> RivenScriptList;
 
 class RivenScriptManager {
 public:
 	RivenScriptManager(MohawkEngine_Riven *vm);
 	~RivenScriptManager();
 
-	RivenScriptPtr readScript(Common::ReadStream *stream, uint16 scriptType);
+	RivenScriptPtr readScript(Common::ReadStream *stream);
 	RivenScriptList readScripts(Common::ReadStream *stream);
 	void stopAllScripts();
 


Commit: d625b4dbd66b5bdc8fe52eaf26bd49cb959b0107
    https://github.com/scummvm/scummvm/commit/d625b4dbd66b5bdc8fe52eaf26bd49cb959b0107
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Rename Riven's script types

Changed paths:
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 95b62b6..388cb73 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -34,11 +34,11 @@ namespace Mohawk {
 // Script Types
 enum {
 	kMouseDownScript = 0,
-	kMouseDownScriptAlt = 1,
+	kMouseDragScript = 1,
 	kMouseUpScript = 2,
-	kMouseMovedPressedReleasedScript = 3,
+	kMouseEnterScript = 3,
 	kMouseInsideScript = 4,
-	kMouseLeaveScript = 5, // This is unconfirmed
+	kMouseLeaveScript = 5,
 
 	kCardLoadScript = 6,
 	kCardLeaveScript = 7,


Commit: bc01a276f971f51ec435734155adadecf7cf1c2b
    https://github.com/scummvm/scummvm/commit/bc01a276f971f51ec435734155adadecf7cf1c2b
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add documentation to Riven's script module

Changed paths:
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 6e5b811..eec27a0 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -50,7 +50,7 @@ RivenScriptManager::~RivenScriptManager() {
 }
 
 RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream) {
-	RivenScriptPtr script = RivenScriptPtr(new RivenScript(_vm));
+	RivenScriptPtr script = RivenScriptPtr(new RivenScript());
 
 	uint16 commandCount = stream->readUint16BE();
 
@@ -112,8 +112,7 @@ void RivenScriptManager::clearStoredMovieOpcode() {
 	_storedMovieOpcode.id = 0;
 }
 
-RivenScript::RivenScript(MohawkEngine_Riven *vm) :
-		_vm(vm) {
+RivenScript::RivenScript() {
 	_continueRunning = true;
 }
 
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 388cb73..68b7807 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -49,26 +49,41 @@ enum {
 class MohawkEngine_Riven;
 class RivenCommand;
 
+/**
+ * Scripts in Riven are a list of Commands
+ *
+ * This class should only be used through the RivenScriptPtr
+ * type to ensure the underlying memory is not freed when changing card.
+ */
 class RivenScript {
 public:
-	RivenScript(MohawkEngine_Riven *vm);
+	RivenScript();
 	~RivenScript();
 
+	/** Append a command to the script */
 	void addCommand(RivenCommand *command);
 
+	/** Run the script */
 	void runScript();
+
+	/** Print script details to the standard output */
 	void dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
+
+	/** Stop the script after the current command */
 	void stopRunning() { _continueRunning = false; }
 
 private:
-	MohawkEngine_Riven *_vm;
-
 	Common::Array<RivenCommand *> _commands;
 	bool _continueRunning;
 };
 
 typedef Common::SharedPtr<RivenScript> RivenScriptPtr;
 
+/**
+ * A script and its type
+ *
+ * The type defines when the script should be run
+ */
 struct RivenTypedScript {
 	uint16 type;
 	RivenScriptPtr script;
@@ -76,12 +91,21 @@ struct RivenTypedScript {
 
 typedef Common::Array<RivenTypedScript> RivenScriptList;
 
+/**
+ * Script manager
+ *
+ * Reads scripts from raw data.
+ * Can run scripts immediatly, or store them for future execution.
+ */
 class RivenScriptManager {
 public:
 	RivenScriptManager(MohawkEngine_Riven *vm);
 	~RivenScriptManager();
 
+	/** Read a single script from a stream */
 	RivenScriptPtr readScript(Common::ReadStream *stream);
+
+	/** Read a list of typed scripts from a stream */
 	RivenScriptList readScripts(Common::ReadStream *stream);
 	void stopAllScripts();
 
@@ -99,25 +123,38 @@ public:
 
 private:
 	MohawkEngine_Riven *_vm;
-
 	StoredMovieOpcode _storedMovieOpcode;
 
 	RivenCommand *readCommand(Common::ReadStream *stream);
 };
 
+/**
+ * An abstract command
+ *
+ * Commands are unit operations part of a script
+ */
 class RivenCommand {
 public:
 	RivenCommand(MohawkEngine_Riven *vm);
 	virtual ~RivenCommand();
 
+	/** Print details about the command to standard output */
 	virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) = 0;
 
+	/** Execute the command */
 	virtual void execute() = 0;
 
 protected:
 	MohawkEngine_Riven *_vm;
 };
 
+/**
+ * A simple Command
+ *
+ * Simple commands have a type and a list of arguments.
+ * The operation to be executed when running the command
+ * depends on the type.
+ */
 class RivenSimpleCommand : public RivenCommand {
 public:
 	static RivenSimpleCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);
@@ -129,18 +166,14 @@ public:
 
 private:
 	typedef Common::Array<uint16> ArgumentArray;
-
-	RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments);
-
-	int _type;
-	ArgumentArray _arguments;
-
 	typedef void (RivenSimpleCommand::*OpcodeProcRiven)(uint16 op, uint16 argc, uint16 *argv);
 	struct RivenOpcode {
 		OpcodeProcRiven proc;
 		const char *desc;
 	};
-	const RivenOpcode *_opcodes;
+
+	RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments);
+
 	void setupOpcodes();
 
 	DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); }
@@ -180,8 +213,21 @@ private:
 	DECLARE_OPCODE(activateFLST);
 	DECLARE_OPCODE(zipMode);
 	DECLARE_OPCODE(activateMLST);
+
+	const RivenOpcode *_opcodes;
+
+	int _type;
+	ArgumentArray _arguments;
 };
 
+/**
+ * A switch branch command
+ *
+ * Switch commands have a variable id and a list of branches.
+ * Each branch associates a value to a script.
+ * The branch matching the variable's value is executed,
+ * if not found an optional default branch can be executed.
+ */
 class RivenSwitchCommand : public RivenCommand {
 public:
 	static RivenSwitchCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);


Commit: 3c2ca0887766d54db5690e140d3fb2d9479bdc4d
    https://github.com/scummvm/scummvm/commit/3c2ca0887766d54db5690e140d3fb2d9479bdc4d
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add a script queue to Riven's script manager

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 4d6c6fe..03a4898 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -698,7 +698,7 @@ void MohawkEngine_Riven::runCardScript(uint16 scriptType) {
 	for (uint16 i = 0; i < _cardData.scripts.size(); i++)
 		if (_cardData.scripts[i].type == scriptType) {
 			RivenScriptPtr script = _cardData.scripts[i].script;
-			script->runScript();
+			_scriptMan->runScript(script, false);
 			break;
 		}
 }
@@ -708,7 +708,7 @@ void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
 	for (uint16 i = 0; i < _hotspots[hotspot].scripts.size(); i++)
 		if (_hotspots[hotspot].scripts[i].type == scriptType) {
 			RivenScriptPtr script = _hotspots[hotspot].scripts[i].script;
-			script->runScript();
+			_scriptMan->runScript(script, false);
 			break;
 		}
 }
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index eec27a0..134c383 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -101,7 +101,7 @@ void RivenScriptManager::setStoredMovieOpcode(const StoredMovieOpcode &op) {
 
 void RivenScriptManager::runStoredMovieOpcode() {
 	if (_storedMovieOpcode.script) {
-		_storedMovieOpcode.script->runScript();
+		runScript(_storedMovieOpcode.script, false);
 		clearStoredMovieOpcode();
 	}
 }
@@ -112,6 +112,14 @@ void RivenScriptManager::clearStoredMovieOpcode() {
 	_storedMovieOpcode.id = 0;
 }
 
+void RivenScriptManager::runScript(const RivenScriptPtr &script, bool queue) {
+	if (!queue) {
+		script->run();
+	} else {
+		_queue.push_back(script);
+	}
+}
+
 RivenScript::RivenScript() {
 	_continueRunning = true;
 }
@@ -128,7 +136,7 @@ void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::
 	}
 }
 
-void RivenScript::runScript() {
+void RivenScript::run() {
 	for (uint16 i = 0; i < _commands.size() && _continueRunning; i++) {
 		_commands[i]->execute();
 	}
@@ -521,7 +529,7 @@ void RivenSimpleCommand::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv)
 		_vm->_scriptMan->setStoredMovieOpcode(storedOp);
 	} else {
 		// Run immediately if we have no delay
-		script->runScript();
+		_vm->_scriptMan->runScript(script, false);
 	}
 
 	delete scriptStream;
@@ -712,7 +720,7 @@ void RivenSwitchCommand::execute() {
 	// Look for a case matching the value
 	for (uint i = 0; i < _branches.size(); i++) {
 		if  (_branches[i].value == value) {
-			_branches[i].script->runScript();
+			_vm->_scriptMan->runScript(_branches[i].script, false);
 			return;
 		}
 	}
@@ -720,7 +728,7 @@ void RivenSwitchCommand::execute() {
 	// Look for the default case if any
 	for (uint i = 0; i < _branches.size(); i++) {
 		if  (_branches[i].value == 0Xffff) {
-			_branches[i].script->runScript();
+			_vm->_scriptMan->runScript(_branches[i].script, false);
 			return;
 		}
 	}
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 68b7807..394c046 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -63,8 +63,13 @@ public:
 	/** Append a command to the script */
 	void addCommand(RivenCommand *command);
 
-	/** Run the script */
-	void runScript();
+	/**
+	 * Run the script
+	 *
+	 * Script execution must go through the ScriptManager,
+	 * this method should not be called directly.
+	 */
+	void run();
 
 	/** Print script details to the standard output */
 	void dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
@@ -107,6 +112,10 @@ public:
 
 	/** Read a list of typed scripts from a stream */
 	RivenScriptList readScripts(Common::ReadStream *stream);
+
+	/** Run a script */
+	void runScript(const RivenScriptPtr &script, bool queue);
+
 	void stopAllScripts();
 
 	struct StoredMovieOpcode {
@@ -123,6 +132,8 @@ public:
 
 private:
 	MohawkEngine_Riven *_vm;
+
+	Common::Array<RivenScriptPtr> _queue;
 	StoredMovieOpcode _storedMovieOpcode;
 
 	RivenCommand *readCommand(Common::ReadStream *stream);
@@ -178,7 +189,7 @@ private:
 
 	DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); }
 
-	//Opcodes
+	// Opcodes
 	DECLARE_OPCODE(drawBitmap);
 	DECLARE_OPCODE(switchCard);
 	DECLARE_OPCODE(playScriptSLST);


Commit: 0aaa3760c25e37800f0fda6ef4771c2347d72f7c
    https://github.com/scummvm/scummvm/commit/0aaa3760c25e37800f0fda6ef4771c2347d72f7c
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move Riven's Card to a separate object

Changed paths:
  A engines/mohawk/riven_card.cpp
  A engines/mohawk/riven_card.h
    engines/mohawk/module.mk
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 3fc118d..8a186b1 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -53,6 +53,7 @@ endif
 ifdef ENABLE_RIVEN
 MODULE_OBJS += \
 	riven.o \
+	riven_card.o \
 	riven_external.o \
 	riven_graphics.o \
 	riven_saveload.o \
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 03a4898..9403627 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -31,6 +31,7 @@
 #include "mohawk/installer_archive.h"
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/riven_external.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_saveload.h"
@@ -51,7 +52,6 @@ Common::Rect *g_demoExitRect;
 
 MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
 	_showHotspots = false;
-	_cardData.hasData = false;
 	_gameOver = false;
 	_activatedSLST = false;
 	_ignoreNextMouseUp = false;
@@ -67,6 +67,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_saveLoad = nullptr;
 	_optionsDialog = nullptr;
 	_curCard = 0;
+	_card = nullptr;
 	_hotspotCount = 0;
 	_curHotspot = -1;
 	removeTimer();
@@ -94,6 +95,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 }
 
 MohawkEngine_Riven::~MohawkEngine_Riven() {
+	delete _card;
 	delete _sound;
 	delete _gfx;
 	delete _console;
@@ -392,10 +394,12 @@ void MohawkEngine_Riven::changeToCard(uint16 dest) {
 			}
 	}
 
-	if (_cardData.hasData)
-		runCardScript(kCardLeaveScript);
+	if (_card)
+		_card->runScript(kCardLeaveScript);
+
+	delete _card;
+	_card = new RivenCard(this, dest);
 
-	loadCard(_curCard);
 	refreshCard(); // Handles hotspots and scripts
 }
 
@@ -412,9 +416,10 @@ void MohawkEngine_Riven::refreshCard() {
 	_gfx->drawPLST(1);
 	_activatedSLST = false;
 
-	runCardScript(kCardLoadScript);
+	_card->runScript(kCardLoadScript);
 	_gfx->updateScreen();
-	runCardScript(kCardOpenScript);
+	_card->runScript(kCardOpenScript);
+	_card->open();
 
 	// Activate the first sound list if none have been activated
 	if (!_activatedSLST)
@@ -431,30 +436,6 @@ void MohawkEngine_Riven::refreshCard() {
 	installCardTimer();
 }
 
-void MohawkEngine_Riven::loadCard(uint16 id) {
-	// NOTE: The card scripts are cleared by the RivenScriptManager automatically.
-
-	Common::SeekableReadStream* inStream = getResource(ID_CARD, id);
-
-	_cardData.name = inStream->readSint16BE();
-	_cardData.zipModePlace = inStream->readUint16BE();
-	_cardData.scripts = _scriptMan->readScripts(inStream);
-	_cardData.hasData = true;
-
-	delete inStream;
-
-	if (_cardData.zipModePlace) {
-		Common::String cardName = getName(CardNames, _cardData.name);
-		if (cardName.empty())
-			return;
-		ZipMode zip;
-		zip.name = cardName;
-		zip.id = id;
-		if (!(Common::find(_zipModeData.begin(), _zipModeData.end(), zip) != _zipModeData.end()))
-			_zipModeData.push_back(zip);
-	}
-}
-
 void MohawkEngine_Riven::loadHotspots(uint16 id) {
 	// Clear old hotspots
 	delete[] _hotspots;
@@ -693,16 +674,6 @@ uint32 MohawkEngine_Riven::getCurCardRMAP() {
 	return rmapCode;
 }
 
-void MohawkEngine_Riven::runCardScript(uint16 scriptType) {
-	assert(_cardData.hasData);
-	for (uint16 i = 0; i < _cardData.scripts.size(); i++)
-		if (_cardData.scripts[i].type == scriptType) {
-			RivenScriptPtr script = _cardData.scripts[i].script;
-			_scriptMan->runScript(script, false);
-			break;
-		}
-}
-
 void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
 	assert(hotspot < _hotspotCount);
 	for (uint16 i = 0; i < _hotspots[hotspot].scripts.size(); i++)
@@ -1022,6 +993,21 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 	sunners = 1;
 }
 
+void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
+	Common::String cardName = getName(CardNames, cardNameId);
+	if (cardName.empty())
+		return;
+	ZipMode zip;
+	zip.name = cardName;
+	zip.id = cardId;
+	if (!(Common::find(_zipModeData.begin(), _zipModeData.end(), zip) != _zipModeData.end()))
+		_zipModeData.push_back(zip);
+}
+
+void MohawkEngine_Riven::runUpdateScreenScript() {
+	_card->runScript(kCardUpdateScript);
+}
+
 bool ZipMode::operator== (const ZipMode &z) const {
 	return z.name == name && z.id == id;
 }
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index ce819ac..11a6d07 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -41,6 +41,7 @@ class RivenExternal;
 class RivenConsole;
 class RivenSaveLoad;
 class RivenOptionsDialog;
+class RivenCard;
 class RivenSoundManager;
 
 // Riven Stack Types
@@ -99,13 +100,6 @@ struct RivenHotspot {
 	bool enabled;
 };
 
-struct Card {
-	int16 name;
-	uint16 zipModePlace;
-	bool hasData;
-	RivenScriptList scripts;
-};
-
 struct ZipMode {
 	Common::String name;
 	uint16 id;
@@ -128,7 +122,7 @@ public:
 	Common::RandomSource *_rnd;
 	RivenScriptManager *_scriptMan;
 
-	Card _cardData;
+	RivenCard *_card;
 
 	GUI::Debugger *getDebugger();
 
@@ -152,7 +146,6 @@ private:
 	// Stack/Card-related functions and variables
 	uint16 _curCard;
 	uint16 _curStack;
-	void loadCard(uint16);
 	void handleEvents();
 
 	// Hotspot related functions and variables
@@ -182,8 +175,7 @@ public:
 	void refreshCard();
 	Common::String getName(uint16 nameResource, uint16 nameID);
 	Common::String getStackName(uint16 stack) const;
-	void runCardScript(uint16 scriptType);
-	void runUpdateScreenScript() { runCardScript(kCardUpdateScript); }
+	void runUpdateScreenScript();
 	uint16 getCurCard() const { return _curCard; }
 	uint16 getCurStack() const { return _curStack; }
 	uint16 matchRMAPToCard(uint32);
@@ -198,6 +190,7 @@ public:
 	int32 getCurHotspot() const { return _curHotspot; }
 	Common::String getHotspotName(uint16 hotspot);
 	void updateCurrentHotspot();
+	void addZipVisitedCard(uint16 cardId, uint16 cardNameId);
 
 	// Variables
 	RivenVariableMap _vars;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
new file mode 100644
index 0000000..f45fde1
--- /dev/null
+++ b/engines/mohawk/riven_card.cpp
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_card.h"
+
+#include "mohawk/resource.h"
+#include "mohawk/riven.h"
+
+namespace Mohawk {
+
+RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
+	_vm(vm),
+	_id(id) {
+	loadCardResource(id);
+}
+
+RivenCard::~RivenCard() {
+
+}
+
+void RivenCard::loadCardResource(uint16 id) {
+	Common::SeekableReadStream *inStream = _vm->getResource(ID_CARD, id);
+
+	_name = inStream->readSint16BE();
+	_zipModePlace = inStream->readUint16BE();
+	_scripts = _vm->_scriptMan->readScripts(inStream);
+
+	delete inStream;
+}
+
+void RivenCard::open() {
+	initializeZipMode();
+}
+
+void RivenCard::initializeZipMode() {
+	if (_zipModePlace) {
+		_vm->addZipVisitedCard(_id, _name);
+	}
+}
+
+void RivenCard::runScript(uint16 scriptType) {
+	for (uint16 i = 0; i < _scripts.size(); i++)
+		if (_scripts[i].type == scriptType) {
+			RivenScriptPtr script = _scripts[i].script;
+			_vm->_scriptMan->runScript(script, false);
+			break;
+		}
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
new file mode 100644
index 0000000..94bbfe6
--- /dev/null
+++ b/engines/mohawk/riven_card.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_CARD_H
+#define RIVEN_CARD_H
+
+#include "mohawk/riven_scripts.h"
+
+#include "common/system.h"
+
+namespace Mohawk {
+
+class RivenCard {
+public:
+	RivenCard(MohawkEngine_Riven *vm, uint16 id);
+	~RivenCard();
+
+	void open();
+	void initializeZipMode();
+	void runScript(uint16 scriptType);
+
+private:
+	void loadCardResource(uint16 id);
+
+	MohawkEngine_Riven *_vm;
+
+	uint16 _id;
+	int16 _name;
+	uint16 _zipModePlace;
+	RivenScriptList _scripts;
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 394c046..f562619 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -29,6 +29,10 @@
 
 #define DECLARE_OPCODE(x) void x(uint16 op, uint16 argc, uint16 *argv)
 
+namespace Common {
+class ReadStream;
+}
+
 namespace Mohawk {
 
 // Script Types


Commit: abe6889bbe640c15e0fdf454d3867eb22869db5c
    https://github.com/scummvm/scummvm/commit/abe6889bbe640c15e0fdf454d3867eb22869db5c
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Remove the current card id from the Riven engine class

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_saveload.cpp
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 4d30008..87a0cd4 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -41,6 +41,7 @@
 
 #ifdef ENABLE_RIVEN
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/riven_external.h"
 #include "mohawk/riven_sound.h"
 #endif
@@ -410,7 +411,7 @@ bool RivenConsole::Cmd_ChangeCard(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_CurCard(int argc, const char **argv) {
-	debugPrintf("Current Card: %d\n", _vm->getCurCard());
+	debugPrintf("Current Card: %d\n", _vm->getCurCard()->getId());
 
 	return true;
 }
@@ -457,7 +458,7 @@ bool RivenConsole::Cmd_PlaySLST(int argc, const char **argv) {
 	_vm->_sound->stopSound();
 	_vm->_sound->stopAllSLST();
 
-	uint16 card = (argc == 3) ? (uint16)atoi(argv[2]) : _vm->getCurCard();
+	uint16 card = (argc == 3) ? (uint16)atoi(argv[2]) : _vm->getCurCard()->getId();
 
 	_vm->_sound->playSLST((uint16)atoi(argv[1]), card);
 	return false;
@@ -511,7 +512,7 @@ bool RivenConsole::Cmd_ChangeStack(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
-	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard(), _vm->getHotspotCount());
+	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), _vm->getHotspotCount());
 
 	for (uint16 i = 0; i < _vm->getHotspotCount(); i++) {
 		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, _vm->_hotspots[i].index, _vm->_hotspots[i].blstID);
@@ -671,7 +672,7 @@ bool RivenConsole::Cmd_ListZipCards(int argc, const char **argv) {
 
 bool RivenConsole::Cmd_GetRMAP(int argc, const char **argv) {
 	uint32 rmapCode = _vm->getCurCardRMAP();
-	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getCurStack()).c_str(), _vm->getCurCard(), rmapCode);
+	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getCurStack()).c_str(), _vm->getCurCard()->getId(), rmapCode);
 	return true;
 }
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 9403627..cf194a5 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -66,7 +66,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_console = nullptr;
 	_saveLoad = nullptr;
 	_optionsDialog = nullptr;
-	_curCard = 0;
 	_card = nullptr;
 	_hotspotCount = 0;
 	_curHotspot = -1;
@@ -379,8 +378,7 @@ static const RivenSpecialChange rivenSpecialChange[] = {
 };
 
 void MohawkEngine_Riven::changeToCard(uint16 dest) {
-	_curCard = dest;
-	debug (1, "Changing to card %d", _curCard);
+	debug (1, "Changing to card %d", dest);
 
 	// Clear the graphics cache (images typically aren't used
 	// on different cards).
@@ -388,9 +386,9 @@ void MohawkEngine_Riven::changeToCard(uint16 dest) {
 
 	if (!(getFeatures() & GF_DEMO)) {
 		for (byte i = 0; i < 13; i++)
-			if (_curStack == rivenSpecialChange[i].startStack && _curCard == matchRMAPToCard(rivenSpecialChange[i].startCardRMAP)) {
+			if (_curStack == rivenSpecialChange[i].startStack && dest == matchRMAPToCard(rivenSpecialChange[i].startCardRMAP)) {
 				changeToStack(rivenSpecialChange[i].targetStack);
-				_curCard = matchRMAPToCard(rivenSpecialChange[i].targetCardRMAP);
+				dest = matchRMAPToCard(rivenSpecialChange[i].targetCardRMAP);
 			}
 	}
 
@@ -407,7 +405,7 @@ void MohawkEngine_Riven::refreshCard() {
 	// Clear any timer still floating around
 	removeTimer();
 
-	loadHotspots(_curCard);
+	loadHotspots(_card->getId());
 
 	_gfx->_updatesEnabled = true;
 	_gfx->clearWaterEffects();
@@ -423,7 +421,7 @@ void MohawkEngine_Riven::refreshCard() {
 
 	// Activate the first sound list if none have been activated
 	if (!_activatedSLST)
-		_sound->playSLST(1, _curCard);
+		_sound->playSLST(1, _card->getId());
 
 	if (_showHotspots)
 		for (uint16 i = 0; i < _hotspotCount; i++)
@@ -462,7 +460,7 @@ void MohawkEngine_Riven::loadHotspots(uint16 id) {
 		// Known weird hotspots:
 		// - tspit 371 (DVD: 377), hotspot 4
 		if (left >= right || top >= bottom) {
-			warning("%s %d hotspot %d is invalid: (%d, %d, %d, %d)", getStackName(_curStack).c_str(), _curCard, i, left, top, right, bottom);
+			warning("%s %d hotspot %d is invalid: (%d, %d, %d, %d)", getStackName(_curStack).c_str(), id, i, left, top, right, bottom);
 			left = top = right = bottom = 0;
 			_hotspots[i].enabled = 0;
 		}
@@ -554,10 +552,10 @@ void MohawkEngine_Riven::checkInventoryClick() {
 	// In the demo, check if we've clicked the exit button
 	if (getFeatures() & GF_DEMO) {
 		if (g_demoExitRect->contains(mousePos)) {
-			if (_curStack == kStackAspit && _curCard == 1) {
+			if (_curStack == kStackAspit && _card->getId() == 1) {
 				// From the main menu, go to the "quit" screen
 				changeToCard(12);
-			} else if (_curStack == kStackAspit && _curCard == 12) {
+			} else if (_curStack == kStackAspit && _card->getId() == 12) {
 				// From the "quit" screen, just quit
 				_gameOver = true;
 			} else {
@@ -576,7 +574,7 @@ void MohawkEngine_Riven::checkInventoryClick() {
 
 	// Set the return stack/card id's.
 	_vars["returnstackid"] = _curStack;
-	_vars["returncardid"] = _curCard;
+	_vars["returncardid"] = _card->getId();
 
 	// See RivenGraphics::showInventory() for an explanation
 	// of the variables' meanings.
@@ -668,7 +666,7 @@ uint16 MohawkEngine_Riven::matchRMAPToCard(uint32 rmapCode) {
 
 uint32 MohawkEngine_Riven::getCurCardRMAP() {
 	Common::SeekableReadStream *rmapStream = getResource(ID_RMAP, 1);
-	rmapStream->seek(_curCard * 4);
+	rmapStream->seek(_card->getId() * 4);
 	uint32 rmapCode = rmapStream->readUint32BE();
 	delete rmapStream;
 	return rmapCode;
@@ -785,7 +783,7 @@ static void catherineIdleTimer(MohawkEngine_Riven *vm) {
 		cathState = 1;
 
 	// Play the movie, blocking
-	vm->_video->activateMLST(movie, vm->getCurCard());
+	vm->_video->activateMLST(movie, vm->getCurCard()->getId());
 	vm->_cursor->hideCursor();
 	vm->_video->playMovieBlockingRiven(movie);
 	vm->_cursor->showCursor();
@@ -919,7 +917,7 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) {
 			// Unlike the other cards' scripts which automatically
 			// activate the MLST, we have to set it manually here.
 			uint16 mlstID = vm->_rnd->getRandomNumberRng(3, 8);
-			vm->_video->activateMLST(mlstID, vm->getCurCard());
+			vm->_video->activateMLST(mlstID, vm->getCurCard()->getId());
 			VideoHandle handle = vm->_video->playMovieRiven(mlstID);
 
 			timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 11a6d07..3000786 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -144,7 +144,6 @@ private:
 	InstallerArchive _installerArchive;
 
 	// Stack/Card-related functions and variables
-	uint16 _curCard;
 	uint16 _curStack;
 	void handleEvents();
 
@@ -176,7 +175,7 @@ public:
 	Common::String getName(uint16 nameResource, uint16 nameID);
 	Common::String getStackName(uint16 stack) const;
 	void runUpdateScreenScript();
-	uint16 getCurCard() const { return _curCard; }
+	RivenCard *getCurCard() const { return _card; }
 	uint16 getCurStack() const { return _curStack; }
 	uint16 matchRMAPToCard(uint32);
 	uint32 getCurCardRMAP();
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index f45fde1..90a6888 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -66,4 +66,8 @@ void RivenCard::runScript(uint16 scriptType) {
 		}
 }
 
+uint16 RivenCard::getId() const {
+	return _id;
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 94bbfe6..a615e70 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -38,6 +38,8 @@ public:
 	void initializeZipMode();
 	void runScript(uint16 scriptType);
 
+	uint16 getId() const;
+
 private:
 	void loadCardResource(uint16 id);
 
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index fb98145..38fe8b2 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -22,6 +22,7 @@
 
 #include "mohawk/cursors.h"
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/riven_external.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_sound.h"
@@ -770,11 +771,11 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
 
 void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
 	if (_vm->_vars["bheat"] != 0)
-		_vm->_sound->playSLST(1, _vm->getCurCard());
+		_vm->_sound->playSLST(1, _vm->getCurCard()->getId());
 	else if (_vm->_vars["bcratergg"] != 0)
-		_vm->_sound->playSLST(2, _vm->getCurCard());
+		_vm->_sound->playSLST(2, _vm->getCurCard()->getId());
 	else
-		_vm->_sound->playSLST(3, _vm->getCurCard());
+		_vm->_sound->playSLST(3, _vm->getCurCard()->getId());
 }
 
 void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
@@ -789,60 +790,60 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
 		// Water is filling/draining from the boiler
 		if (water == 0) {
 			if (platform == 1)
-				_vm->_video->activateMLST(12, _vm->getCurCard());
+				_vm->_video->activateMLST(12, _vm->getCurCard()->getId());
 			else
-				_vm->_video->activateMLST(10, _vm->getCurCard());
+				_vm->_video->activateMLST(10, _vm->getCurCard()->getId());
 		} else if (heat == 1) {
 			if (platform == 1)
-				_vm->_video->activateMLST(22, _vm->getCurCard());
+				_vm->_video->activateMLST(22, _vm->getCurCard()->getId());
 			else
-				_vm->_video->activateMLST(19, _vm->getCurCard());
+				_vm->_video->activateMLST(19, _vm->getCurCard()->getId());
 		} else {
 			if (platform == 1)
-				_vm->_video->activateMLST(16, _vm->getCurCard());
+				_vm->_video->activateMLST(16, _vm->getCurCard()->getId());
 			else
-				_vm->_video->activateMLST(13, _vm->getCurCard());
+				_vm->_video->activateMLST(13, _vm->getCurCard()->getId());
 		}
 	} else if (argv[0] == 2 && water != 0) {
 		if (heat == 1) {
 			// Turning on the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(23, _vm->getCurCard());
+				_vm->_video->activateMLST(23, _vm->getCurCard()->getId());
 			else
-				_vm->_video->activateMLST(20, _vm->getCurCard());
+				_vm->_video->activateMLST(20, _vm->getCurCard()->getId());
 		} else {
 			// Turning off the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(18, _vm->getCurCard());
+				_vm->_video->activateMLST(18, _vm->getCurCard()->getId());
 			else
-				_vm->_video->activateMLST(15, _vm->getCurCard());
+				_vm->_video->activateMLST(15, _vm->getCurCard()->getId());
 		}
 	} else if (argv[0] == 3) {
 		if (platform == 1) {
 			// Lowering the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(24, _vm->getCurCard());
+					_vm->_video->activateMLST(24, _vm->getCurCard()->getId());
 				else
-					_vm->_video->activateMLST(17, _vm->getCurCard());
+					_vm->_video->activateMLST(17, _vm->getCurCard()->getId());
 			} else
-				_vm->_video->activateMLST(11, _vm->getCurCard());
+				_vm->_video->activateMLST(11, _vm->getCurCard()->getId());
 		} else {
 			// Raising the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(21, _vm->getCurCard());
+					_vm->_video->activateMLST(21, _vm->getCurCard()->getId());
 				else
-					_vm->_video->activateMLST(14, _vm->getCurCard());
+					_vm->_video->activateMLST(14, _vm->getCurCard()->getId());
 			} else
-				_vm->_video->activateMLST(9, _vm->getCurCard());
+				_vm->_video->activateMLST(9, _vm->getCurCard()->getId());
 		}
 	}
 
 	if (argc > 1)
-		_vm->_sound->playSLST(argv[1], _vm->getCurCard());
+		_vm->_sound->playSLST(argv[1], _vm->getCurCard()->getId());
 	else if (argv[0] == 2)
-		_vm->_sound->playSLST(1, _vm->getCurCard());
+		_vm->_sound->playSLST(1, _vm->getCurCard()->getId());
 
 	_vm->_cursor->setCursor(kRivenHideCursor);
 	_vm->_video->playMovieBlockingRiven(11);
@@ -851,10 +852,10 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
 void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
 	if (_vm->_vars["bheat"] != 0) {
 		if (_vm->_vars["bblrgrt"] == 0) {
-			_vm->_video->activateMLST(8, _vm->getCurCard());
+			_vm->_video->activateMLST(8, _vm->getCurCard()->getId());
 			_vm->_video->playMovieRiven(8);
 		} else {
-			_vm->_video->activateMLST(7, _vm->getCurCard());
+			_vm->_video->activateMLST(7, _vm->getCurCard()->getId());
 			_vm->_video->playMovieRiven(7);
 		}
 	} else {
@@ -972,11 +973,11 @@ void RivenExternal::xbfreeytram(uint16 argc, uint16 *argv) {
 	}
 
 	// Activate the MLST and play the video
-	_vm->_video->activateMLST(mlstId, _vm->getCurCard());
+	_vm->_video->activateMLST(mlstId, _vm->getCurCard()->getId());
 	_vm->_video->playMovieBlockingRiven(11);
 
 	// Now play the second movie
-	_vm->_video->activateMLST(mlstId + 5, _vm->getCurCard());
+	_vm->_video->activateMLST(mlstId + 5, _vm->getCurCard()->getId());
 	_vm->_video->playMovieBlockingRiven(12);
 }
 
@@ -1381,19 +1382,19 @@ void RivenExternal::xgplaywhark(uint16 argc, uint16 *argv) {
 	// Activate the correct video based on the amount of times we've been visited
 	switch (wharkVisits) {
 	case 1:
-		_vm->_video->activateMLST(3, _vm->getCurCard());
+		_vm->_video->activateMLST(3, _vm->getCurCard()->getId());
 		break;
 	case 2:
 		// One of two random videos
-		_vm->_video->activateMLST(4 + _vm->_rnd->getRandomBit(), _vm->getCurCard());
+		_vm->_video->activateMLST(4 + _vm->_rnd->getRandomBit(), _vm->getCurCard()->getId());
 		break;
 	case 3:
 		// One of two random videos
-		_vm->_video->activateMLST(6 + _vm->_rnd->getRandomBit(), _vm->getCurCard());
+		_vm->_video->activateMLST(6 + _vm->_rnd->getRandomBit(), _vm->getCurCard()->getId());
 		break;
 	case 4:
 		// Red alert! Shields online! Brace yourself for impact!
-		_vm->_video->activateMLST(8, _vm->getCurCard());
+		_vm->_video->activateMLST(8, _vm->getCurCard()->getId());
 		break;
 	}
 
@@ -1468,7 +1469,7 @@ static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
 		cathState = 3;
 
 	// Begin playing the new movie
-	vm->_video->activateMLST(movie, vm->getCurCard());
+	vm->_video->activateMLST(movie, vm->getCurCard()->getId());
 	VideoHandle videoHandle = vm->_video->playMovieRiven(30);
 
 	// Reset the timer
@@ -1509,7 +1510,7 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
 
 	// Begin playing a movie immediately if Catherine is already in the viewer
 	if (cathMovie == 8 || (cathMovie >= 13 && cathMovie <= 16)) {
-		_vm->_video->activateMLST(cathMovie, _vm->getCurCard());
+		_vm->_video->activateMLST(cathMovie, _vm->getCurCard()->getId());
 		VideoHandle videoHandle = _vm->_video->playMovieRiven(30);
 
 		timeUntilNextMovie = videoHandle->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
@@ -2111,7 +2112,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 					_vm->_gfx->drawPLST(3);                             // Black out the screen
 					_vm->_gfx->updateScreen();                          // Update the screen
 					_vm->_sound->playSound(0);                          // Play the link sound
-					_vm->_video->activateMLST(7, _vm->getCurCard());    // Activate Gehn Link Video
+					_vm->_video->activateMLST(7, _vm->getCurCard()->getId());    // Activate Gehn Link Video
 					_vm->_video->playMovieBlockingRiven(1);             // Play Gehn Link Video
 					_vm->_vars["agehn"] = 4;                            // Set Gehn to the trapped state
 					_vm->_vars["atrapbook"] = 1;                        // We've got the trap book again
@@ -2252,7 +2253,7 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
 	}
 
 	// Now play the video for the watch
-	_vm->_video->activateMLST(1, _vm->getCurCard());
+	_vm->_video->activateMLST(1, _vm->getCurCard()->getId());
 	_vm->_video->playMovieBlockingRiven(1);
 
 	// And, finally, refresh
@@ -2336,7 +2337,7 @@ void RivenExternal::xrhideinventory(uint16 argc, uint16 *argv) {
 static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) {
 	// Randomize a video out in the middle of Tay
 	uint16 movie = vm->_rnd->getRandomNumberRng(2, 13);
-	vm->_video->activateMLST(movie, vm->getCurCard());
+	vm->_video->activateMLST(movie, vm->getCurCard()->getId());
 	VideoHandle handle = vm->_video->playMovieRiven(movie);
 
 	// Ensure the next video starts after this one ends
@@ -2404,25 +2405,25 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
 			if (_vm->_vars["pcage"] == 2) {
 				// The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
 				// And now we fall back to Earth... all the way...
-				_vm->_video->activateMLST(8, _vm->getCurCard());
+				_vm->_video->activateMLST(8, _vm->getCurCard()->getId());
 				runEndGame(8, 5000);
 			} else if (_vm->_vars["agehn"] == 4) {
 				// The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
 				// Nice going! Catherine and the islanders are all dead now! Just go back to your home...
-				_vm->_video->activateMLST(9, _vm->getCurCard());
+				_vm->_video->activateMLST(9, _vm->getCurCard()->getId());
 				runEndGame(9, 5000);
 			} else if (_vm->_vars["atrapbook"] == 1) {
 				// The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
 				// And then you get shot by Cho. Nice going! Catherine and the islanders are dead
 				// and you have just set Gehn free from Riven, not to mention you're dead.
-				_vm->_video->activateMLST(10, _vm->getCurCard());
+				_vm->_video->activateMLST(10, _vm->getCurCard()->getId());
 				runEndGame(10, 5000);
 			} else {
 				// The impossible ending: You don't have Catherine's journal and yet you were somehow
 				// able to open the hatch on the telescope. The game provides an ending for those who
 				// cheat, load a saved game with the combo, or just guess the telescope combo. Atrus
 				// doesn't come and you just fall into the fissure.
-				_vm->_video->activateMLST(11, _vm->getCurCard());
+				_vm->_video->activateMLST(11, _vm->getCurCard()->getId());
 				runEndGame(11, 5000);
 			}
 		} else {
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index b583bc9..983c31e 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -22,6 +22,7 @@
 
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_sound.h"
 
@@ -82,7 +83,7 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
 }
 
 void RivenGraphics::drawPLST(uint16 x) {
-	Common::SeekableReadStream* plst = _vm->getResource(ID_PLST, _vm->getCurCard());
+	Common::SeekableReadStream* plst = _vm->getResource(ID_PLST, _vm->getCurCard()->getId());
 	uint16 recordCount = plst->readUint16BE();
 
 	for (uint16 i = 0; i < recordCount; i++) {
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 755f877..766ad30 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -22,6 +22,7 @@
 
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/riven_saveload.h"
 
 #include "common/system.h"
@@ -403,7 +404,7 @@ Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &desc
 
 	// Convert class variables to variable numbers
 	_vm->_vars["currentstackid"] = _vm->getCurStack();
-	_vm->_vars["currentcardid"] = _vm->getCurCard();
+	_vm->_vars["currentcardid"] = _vm->getCurCard()->getId();
 
 	Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
 	if (!saveFile)
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 134c383..2f26081 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -22,6 +22,7 @@
 
 #include "mohawk/cursors.h"
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/riven_external.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_scripts.h"
@@ -552,19 +553,19 @@ void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 	if (_vm->getCurStack() == kStackTspit && _vm->getCurCardRMAP() == 0x6e9a && argv[0] == 2)
 		return;
 
-	_vm->_sound->playSLST(argv[0], _vm->getCurCard());
+	_vm->_sound->playSLST(argv[0], _vm->getCurCard()->getId());
 	_vm->_activatedSLST = true;
 }
 
 // Command 41: activate MLST record and play
 void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(argv[0], _vm->getCurCard());
+	_vm->_video->activateMLST(argv[0], _vm->getCurCard()->getId());
 	_vm->_video->playMovieRiven(argv[0]);
 }
 
 // Command 43: activate BLST record (card hotspot enabling lists)
 void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
-	Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, _vm->getCurCard());
+	Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, _vm->getCurCard()->getId());
 	uint16 recordCount = blst->readUint16BE();
 
 	for (uint16 i = 0; i < recordCount; i++) {
@@ -586,7 +587,7 @@ void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 44: activate FLST record (information on which SFXE resource this card should use)
 void RivenSimpleCommand::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
-	Common::SeekableReadStream* flst = _vm->getResource(ID_FLST, _vm->getCurCard());
+	Common::SeekableReadStream* flst = _vm->getResource(ID_FLST, _vm->getCurCard()->getId());
 	uint16 recordCount = flst->readUint16BE();
 
 	for (uint16 i = 0; i < recordCount; i++) {
@@ -619,7 +620,7 @@ void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 46: activate MLST record (movie lists)
 void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(argv[0], _vm->getCurCard());
+	_vm->_video->activateMLST(argv[0], _vm->getCurCard()->getId());
 }
 
 void RivenSimpleCommand::dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {


Commit: 1bb5424dddca2cf150fa1a09f4845e193b581e3e
    https://github.com/scummvm/scummvm/commit/1bb5424dddca2cf150fa1a09f4845e193b581e3e
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move PLST handling to the RivenCard class

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index cf194a5..3ea5d16 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -53,6 +53,7 @@ Common::Rect *g_demoExitRect;
 MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
 	_showHotspots = false;
 	_gameOver = false;
+	_activatedPLST = false;
 	_activatedSLST = false;
 	_ignoreNextMouseUp = false;
 	_extrasFile = nullptr;
@@ -409,20 +410,10 @@ void MohawkEngine_Riven::refreshCard() {
 
 	_gfx->_updatesEnabled = true;
 	_gfx->clearWaterEffects();
-	_gfx->_activatedPLSTs.clear();
 	_video->stopVideos();
-	_gfx->drawPLST(1);
-	_activatedSLST = false;
 
-	_card->runScript(kCardLoadScript);
-	_gfx->updateScreen();
-	_card->runScript(kCardOpenScript);
 	_card->open();
 
-	// Activate the first sound list if none have been activated
-	if (!_activatedSLST)
-		_sound->playSLST(1, _card->getId());
-
 	if (_showHotspots)
 		for (uint16 i = 0; i < _hotspotCount; i++)
 			_gfx->drawRect(_hotspots[i].rect, _hotspots[i].enabled);
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 3000786..a61c4b0 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -199,6 +199,7 @@ public:
 	void setGameOver() { _gameOver = true; }
 	void ignoreNextMouseUp() { _ignoreNextMouseUp = true; }
 	Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id);
+	bool _activatedPLST;
 	bool _activatedSLST;
 	void runLoadDialog();
 	void delayAndUpdate(uint32 ms);
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 90a6888..54b588c 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -22,6 +22,9 @@
 
 #include "mohawk/riven_card.h"
 
+#include "mohawk/riven_graphics.h"
+#include "mohawk/sound.h"
+
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
 
@@ -31,6 +34,7 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	_vm(vm),
 	_id(id) {
 	loadCardResource(id);
+	loadCardPictureList(id);
 }
 
 RivenCard::~RivenCard() {
@@ -48,7 +52,16 @@ void RivenCard::loadCardResource(uint16 id) {
 }
 
 void RivenCard::open() {
+	_vm->_activatedPLST = false;
+	_vm->_activatedSLST = false;
+
+	runScript(kCardLoadScript);
+	defaultLoadScript();
+
 	initializeZipMode();
+	_vm->_gfx->updateScreen();
+
+	runScript(kCardOpenScript);
 }
 
 void RivenCard::initializeZipMode() {
@@ -70,4 +83,49 @@ uint16 RivenCard::getId() const {
 	return _id;
 }
 
+void RivenCard::defaultLoadScript() {
+	// Activate the first picture list if none have been activated
+	if (!_vm->_activatedPLST)
+		drawPicture(1);
+
+	// Activate the first sound list if none have been activated
+	if (!_vm->_activatedSLST)
+		_vm->_sound->playSLST(1, _id);
+}
+
+void RivenCard::loadCardPictureList(uint16 id) {
+	Common::SeekableReadStream* plst = _vm->getResource(ID_PLST, id);
+	uint16 recordCount = plst->readUint16BE();
+	_pictureList.resize(recordCount);
+
+	for (uint16 i = 0; i < recordCount; i++) {
+		Picture &picture = _pictureList[i];
+		picture.index = plst->readUint16BE();
+		picture.id = plst->readUint16BE();
+		picture.rect.left = plst->readUint16BE();
+		picture.rect.top = plst->readUint16BE();
+		picture.rect.right = plst->readUint16BE();
+		picture.rect.bottom = plst->readUint16BE();
+	}
+
+	delete plst;
+}
+
+void RivenCard::drawPicture(uint16 index, bool queue) {
+	if (index > 0 && index <= _pictureList.size()) {
+		RivenScriptPtr script = _vm->_scriptMan->createScriptFromData(1, 39, 1, index);
+		_vm->_scriptMan->runScript(script, queue);
+	}
+}
+
+RivenCard::Picture RivenCard::getPicture(uint16 index) const {
+	for (uint16 i = 0; i < _pictureList.size(); i++) {
+		if (_pictureList[i].index == index) {
+			return _pictureList[i];
+		}
+	}
+
+	error("Could not find picture %d in card %d", index, _id);
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index a615e70..86e8724 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -25,30 +25,65 @@
 
 #include "mohawk/riven_scripts.h"
 
+#include "common/rect.h"
 #include "common/system.h"
 
 namespace Mohawk {
 
+/**
+ * A game view
+ *
+ * The names Card and Stack are legacy from the HyperCard engine used in
+ * the original mac version of Myst.
+ *
+ * Cards contain hotspots, scripts, and resource lists.
+ */
 class RivenCard {
 public:
 	RivenCard(MohawkEngine_Riven *vm, uint16 id);
 	~RivenCard();
 
+	/**
+	 * An image that can be drawn in this card
+	 */
+	struct Picture {
+		uint16 index;
+		uint16 id;
+		Common::Rect rect;
+	};
+
+	/** Initialization routine used to draw a card for the first time or to refresh it */
 	void open();
-	void initializeZipMode();
+
+	/** Run one of the card's scripts */
 	void runScript(uint16 scriptType);
 
+	/** Get the id of the card in the stack */
 	uint16 getId() const;
 
+	/** Get the card's picture with the specified index */
+	Picture getPicture(uint16 index) const;
+
+	/** Draw one of the card's pictures synchronously or asynchronously */
+	void drawPicture(uint16 index, bool queue = false);
+
 private:
 	void loadCardResource(uint16 id);
+	void loadCardPictureList(uint16 id);
+
+	void initializeZipMode();
+	void defaultLoadScript();
 
 	MohawkEngine_Riven *_vm;
 
+	// General card data
 	uint16 _id;
 	int16 _name;
 	uint16 _zipModePlace;
 	RivenScriptList _scripts;
+
+	// Resource lists
+	Common::Array<Picture> _pictureList;
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 38fe8b2..1027052 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -460,7 +460,7 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
 	}
 
 	// Draw the image of the page
-	_vm->_gfx->drawPLST(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xaatrusbookback(uint16 argc, uint16 *argv) {
@@ -525,13 +525,13 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
 	}
 
 	// Draw the image of the page
-	_vm->_gfx->drawPLST(page);
+	_vm->getCurCard()->drawPicture(page);
 
 	// Draw the white page edges
 	if (page > 1 && page < 5)
-		_vm->_gfx->drawPLST(50);
+		_vm->getCurCard()->drawPicture(50);
 	else if (page > 5)
-		_vm->_gfx->drawPLST(51);
+		_vm->getCurCard()->drawPicture(51);
 
 	if (page == 28) {
 		// Draw the telescope combination
@@ -708,7 +708,7 @@ void RivenExternal::xblabopenbook(uint16 argc, uint16 *argv) {
 	uint32 page = _vm->_vars["blabpage"];
 
 	// Draw the image of the page based on the blabbook variable
-	_vm->_gfx->drawPLST(page);
+	_vm->getCurCard()->drawPicture(page);
 
 	if (page == 14) {
 		// Draw the dome combination
@@ -949,7 +949,7 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
 	// Set the bait if we put it on the plate
 	if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
-		_vm->_gfx->drawPLST(4);
+		_vm->getCurCard()->drawPicture(4);
 		_vm->_gfx->updateScreen();
 		_vm->_hotspots[3].enabled = false; // Disable bait hotspot
 		_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot
@@ -983,7 +983,7 @@ void RivenExternal::xbfreeytram(uint16 argc, uint16 *argv) {
 
 void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	// Remove the pellet from the plate and put it in your hand
-	_vm->_gfx->drawPLST(3);
+	_vm->getCurCard()->drawPicture(3);
 	_vm->_cursor->setCursor(kRivenPelletCursor);
 	_vm->_gfx->updateScreen();
 
@@ -1010,7 +1010,7 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	// Set the bait if we put it on the plate, remove otherwise
 	if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
-		_vm->_gfx->drawPLST(4);
+		_vm->getCurCard()->drawPicture(4);
 		_vm->_gfx->updateScreen();
 		_vm->_hotspots[3].enabled = false; // Disable bait hotspot
 		_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot
@@ -1426,21 +1426,21 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	curPos = newPos % 6; // Clip it to 0-5
 
 	// And update the screen with the new image
-	_vm->_gfx->drawPLST(curPos + 2);
+	_vm->getCurCard()->drawPicture(curPos + 2);
 	_vm->_gfx->updateScreen();
 }
 
 void RivenExternal::xglview_villageon(uint16 argc, uint16 *argv) {
 	// Turn on the left viewer to 'village mode'
 	_vm->_vars["glview"] = 2;
-	_vm->_gfx->drawPLST(_vm->_vars["glviewpos"] + 2);
+	_vm->getCurCard()->drawPicture(_vm->_vars["glviewpos"] + 2);
 	_vm->_gfx->updateScreen();
 }
 
 void RivenExternal::xglview_villageoff(uint16 argc, uint16 *argv) {
 	// Turn off the left viewer when in 'village mode' (why is this external?)
 	_vm->_vars["glview"] = 0;
-	_vm->_gfx->drawPLST(1);
+	_vm->getCurCard()->drawPicture(1);
 	_vm->_gfx->updateScreen();
 }
 
@@ -1517,7 +1517,7 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
 	} else {
 		// Otherwise, just redraw the imager
 		timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000;
-		_vm->_gfx->drawPLST(8);
+		_vm->getCurCard()->drawPicture(8);
 		_vm->_gfx->updateScreen();
 	}
 
@@ -1541,7 +1541,7 @@ void RivenExternal::xglview_prisonoff(uint16 argc, uint16 *argv) {
 	_vm->_cursor->showCursor();
 
 	// Redraw the viewer
-	_vm->_gfx->drawPLST(1);
+	_vm->getCurCard()->drawPicture(1);
 	_vm->_gfx->updateScreen();
 }
 
@@ -1620,19 +1620,19 @@ void RivenExternal::xjtunnel103_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 0))
-		_vm->_gfx->drawPLST(2);
+		_vm->getCurCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 1))
-		_vm->_gfx->drawPLST(3);
+		_vm->getCurCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 2))
-		_vm->_gfx->drawPLST(4);
+		_vm->getCurCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 3))
-		_vm->_gfx->drawPLST(5);
+		_vm->getCurCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 22))
-		_vm->_gfx->drawPLST(6);
+		_vm->getCurCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 23))
-		_vm->_gfx->drawPLST(7);
+		_vm->getCurCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 24))
-		_vm->_gfx->drawPLST(8);
+		_vm->getCurCard()->drawPicture(8);
 }
 
 void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
@@ -1641,21 +1641,21 @@ void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 9))
-		_vm->_gfx->drawPLST(2);
+		_vm->getCurCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 10))
-		_vm->_gfx->drawPLST(3);
+		_vm->getCurCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 11))
-		_vm->_gfx->drawPLST(4);
+		_vm->getCurCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 12))
-		_vm->_gfx->drawPLST(5);
+		_vm->getCurCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 13))
-		_vm->_gfx->drawPLST(6);
+		_vm->getCurCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 14))
-		_vm->_gfx->drawPLST(7);
+		_vm->getCurCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 15))
-		_vm->_gfx->drawPLST(8);
+		_vm->getCurCard()->drawPicture(8);
 	if (iconsDepressed & (1 << 16))
-		_vm->_gfx->drawPLST(9);
+		_vm->getCurCard()->drawPicture(9);
 }
 
 void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
@@ -1664,19 +1664,19 @@ void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 3))
-		_vm->_gfx->drawPLST(2);
+		_vm->getCurCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 4))
-		_vm->_gfx->drawPLST(3);
+		_vm->getCurCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 5))
-		_vm->_gfx->drawPLST(4);
+		_vm->getCurCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 6))
-		_vm->_gfx->drawPLST(5);
+		_vm->getCurCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 7))
-		_vm->_gfx->drawPLST(6);
+		_vm->getCurCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 8))
-		_vm->_gfx->drawPLST(7);
+		_vm->getCurCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 9))
-		_vm->_gfx->drawPLST(8);
+		_vm->getCurCard()->drawPicture(8);
 }
 
 void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
@@ -1685,21 +1685,21 @@ void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 16))
-		_vm->_gfx->drawPLST(2);
+		_vm->getCurCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 17))
-		_vm->_gfx->drawPLST(3);
+		_vm->getCurCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 18))
-		_vm->_gfx->drawPLST(4);
+		_vm->getCurCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 19))
-		_vm->_gfx->drawPLST(5);
+		_vm->getCurCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 20))
-		_vm->_gfx->drawPLST(6);
+		_vm->getCurCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 21))
-		_vm->_gfx->drawPLST(7);
+		_vm->getCurCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 22))
-		_vm->_gfx->drawPLST(8);
+		_vm->getCurCard()->drawPicture(8);
 	if (iconsDepressed & (1 << 23))
-		_vm->_gfx->drawPLST(9);
+		_vm->getCurCard()->drawPicture(9);
 }
 
 void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
@@ -1947,8 +1947,8 @@ void RivenExternal::xjschool280_resetright(uint16 argc, uint16 *argv) {
 void RivenExternal::redrawWharkNumberPuzzle(uint16 overlay, uint16 number) {
 	// Update the screen for the whark number puzzle
 	// We don't update the whole screen here because we don't want to overwrite the video data
-	_vm->_gfx->drawPLST(overlay);
-	_vm->_gfx->drawPLST(number + 1);
+	_vm->getCurCard()->drawPicture(overlay);
+	_vm->getCurCard()->drawPicture(number + 1);
 	_vm->_gfx->updateScreen(Common::Rect(80, 212, 477, 392));
 	_vm->_system->updateScreen();
 }
@@ -2109,7 +2109,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 					_vm->_scriptMan->stopAllScripts();                  // Stop all running scripts (so we don't remain in the cage)
 					_vm->_video->stopVideos();                          // Stop all videos
 					_vm->_cursor->setCursor(kRivenHideCursor);          // Hide the cursor
-					_vm->_gfx->drawPLST(3);                             // Black out the screen
+					_vm->getCurCard()->drawPicture(3);                             // Black out the screen
 					_vm->_gfx->updateScreen();                          // Update the screen
 					_vm->_sound->playSound(0);                          // Play the link sound
 					_vm->_video->activateMLST(7, _vm->getCurCard()->getId());    // Activate Gehn Link Video
@@ -2182,7 +2182,7 @@ void RivenExternal::xobedroom5_closedrawer(uint16 argc, uint16 *argv) {
 }
 
 void RivenExternal::xogehnopenbook(uint16 argc, uint16 *argv) {
-	_vm->_gfx->drawPLST(_vm->_vars["ogehnpage"]);
+	_vm->getCurCard()->drawPicture(_vm->_vars["ogehnpage"]);
 }
 
 void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
@@ -2685,7 +2685,7 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
 	assert(marble != 0);
 
 	// Redraw the background
-	_vm->_gfx->drawPLST(1);
+	_vm->getCurCard()->drawPicture(1);
 	_vm->_gfx->updateScreen();
 
 	// Loop until the player lets go (or quits)
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 983c31e..34da11f 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -82,42 +82,12 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
 	_dirtyScreen = true;
 }
 
-void RivenGraphics::drawPLST(uint16 x) {
-	Common::SeekableReadStream* plst = _vm->getResource(ID_PLST, _vm->getCurCard()->getId());
-	uint16 recordCount = plst->readUint16BE();
-
-	for (uint16 i = 0; i < recordCount; i++) {
-		uint16 index = plst->readUint16BE();
-		uint16 id = plst->readUint16BE();
-		uint16 left = plst->readUint16BE();
-		uint16 top = plst->readUint16BE();
-		uint16 right = plst->readUint16BE();
-		uint16 bottom = plst->readUint16BE();
-
-		// We are also checking here to make sure we haven't drawn the image yet on screen.
-		// This fixes problems with drawing PLST 1 twice and some other images twice. PLST
-		// 1 is sometimes not called by the scripts, so some cards don't appear if we don't
-		// draw PLST 1 each time. This "hack" is here to catch any PLST attempting to draw
-		// twice. There should never be a problem with doing it this way.
-		if (index == x && !(Common::find(_activatedPLSTs.begin(), _activatedPLSTs.end(), x) != _activatedPLSTs.end())) {
-			debug(0, "Drawing image %d", id);
-			copyImageToScreen(id, left, top, right, bottom);
-			_activatedPLSTs.push_back(x);
-			break;
-		}
-	}
-
-	delete plst;
-}
-
 void RivenGraphics::updateScreen(Common::Rect updateRect) {
 	if (_updatesEnabled) {
 		_vm->runUpdateScreenScript();
 		_vm->_sound->triggerDrawSound();
 
 		if (_dirtyScreen) {
-			_activatedPLSTs.clear();
-
 			// Copy to screen if there's no transition. Otherwise transition. ;)
 			if (_scheduledTransition < 0)
 				_vm->_system->copyRectToScreen(_mainScreen->getBasePtr(updateRect.left, updateRect.top), _mainScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 577e5e6..a90c288 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -34,11 +34,9 @@ public:
 	RivenGraphics(MohawkEngine_Riven *vm);
 	~RivenGraphics();
 
-	void copyImageToScreen(uint16, uint32, uint32, uint32, uint32);
+	void copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom);
 	void updateScreen(Common::Rect updateRect = Common::Rect(0, 0, 608, 392));
 	bool _updatesEnabled;
-	Common::Array<uint16> _activatedPLSTs;
-	void drawPLST(uint16 x);
 	void drawRect(Common::Rect rect, bool active);
 	void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect);
 	void drawExtrasImage(uint16 id, Common::Rect dstRect);
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 2f26081..68a2152 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -121,6 +121,38 @@ void RivenScriptManager::runScript(const RivenScriptPtr &script, bool queue) {
 	}
 }
 
+RivenScriptPtr RivenScriptManager::createScriptFromData(uint16 commandCount, ...) {
+	va_list args;
+	va_start(args, commandCount);
+
+	// Build a script from the variadic arguments
+	Common::MemoryWriteStreamDynamic writeStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
+	writeStream.writeUint16BE(commandCount);
+
+	for (uint i = 0; i < commandCount; i++) {
+		uint16 command = va_arg(args, int);
+		writeStream.writeUint16BE(command);
+
+		if (command == 8) {
+			// The switch command has a different format that is not implemented
+			error("Cannot create a Switch command from data");
+		}
+
+		uint16 argumentCount = va_arg(args, int);
+		writeStream.writeUint16BE(argumentCount);
+
+		for (uint j = 0; j < commandCount; j++) {
+			uint16 argument = va_arg(args, int);
+			writeStream.writeUint16BE(argument);
+		}
+	}
+
+	va_end(args);
+
+	Common::MemoryReadStream readStream = Common::MemoryReadStream(writeStream.getData(), writeStream.size());
+	return readScript(&readStream);
+}
+
 RivenScript::RivenScript() {
 	_continueRunning = true;
 }
@@ -538,7 +570,11 @@ void RivenSimpleCommand::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv)
 
 // Command 39: activate PLST record (card picture lists)
 void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_gfx->drawPLST(argv[0]);
+	_vm->_activatedPLST = true;
+
+	RivenCard::Picture picture = _vm->getCurCard()->getPicture(argv[0]);
+
+	_vm->_gfx->copyImageToScreen(picture.id, picture.rect.left, picture.rect.top, picture.rect.right, picture.rect.bottom);
 
 	// An update is automatically sent here as long as it's not a load or update script and updates are enabled.
 	// TODO: Fix the graphics manager
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index f562619..fd47a10 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -52,6 +52,9 @@ enum {
 
 class MohawkEngine_Riven;
 class RivenCommand;
+class RivenScript;
+
+typedef Common::SharedPtr<RivenScript> RivenScriptPtr;
 
 /**
  * Scripts in Riven are a list of Commands
@@ -86,8 +89,6 @@ private:
 	bool _continueRunning;
 };
 
-typedef Common::SharedPtr<RivenScript> RivenScriptPtr;
-
 /**
  * A script and its type
  *
@@ -114,6 +115,9 @@ public:
 	/** Read a single script from a stream */
 	RivenScriptPtr readScript(Common::ReadStream *stream);
 
+	/** Create a script from the caller provided arguments containing raw data */
+	RivenScriptPtr createScriptFromData(uint16 commandCount, ...);
+
 	/** Read a list of typed scripts from a stream */
 	RivenScriptList readScripts(Common::ReadStream *stream);
 


Commit: 2fbe284a311f2eef62126115ddd6d2bb483b3c4b
    https://github.com/scummvm/scummvm/commit/2fbe284a311f2eef62126115ddd6d2bb483b3c4b
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Chane Riven's graphics manager to automatically handle screen updates

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 3ea5d16..b7ff1c3 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -408,7 +408,6 @@ void MohawkEngine_Riven::refreshCard() {
 
 	loadHotspots(_card->getId());
 
-	_gfx->_updatesEnabled = true;
 	_gfx->clearWaterEffects();
 	_video->stopVideos();
 
@@ -993,10 +992,6 @@ void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
 		_zipModeData.push_back(zip);
 }
 
-void MohawkEngine_Riven::runUpdateScreenScript() {
-	_card->runScript(kCardUpdateScript);
-}
-
 bool ZipMode::operator== (const ZipMode &z) const {
 	return z.name == name && z.id == id;
 }
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index a61c4b0..5566161 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -174,7 +174,6 @@ public:
 	void refreshCard();
 	Common::String getName(uint16 nameResource, uint16 nameID);
 	Common::String getStackName(uint16 stack) const;
-	void runUpdateScreenScript();
 	RivenCard *getCurCard() const { return _card; }
 	uint16 getCurStack() const { return _curStack; }
 	uint16 matchRMAPToCard(uint32);
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 54b588c..9bb5a2e 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -23,7 +23,7 @@
 #include "mohawk/riven_card.h"
 
 #include "mohawk/riven_graphics.h"
-#include "mohawk/sound.h"
+#include "mohawk/riven_sound.h"
 
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
@@ -55,11 +55,12 @@ void RivenCard::open() {
 	_vm->_activatedPLST = false;
 	_vm->_activatedSLST = false;
 
+	_vm->_gfx->beginScreenUpdate();
 	runScript(kCardLoadScript);
 	defaultLoadScript();
 
 	initializeZipMode();
-	_vm->_gfx->updateScreen();
+	_vm->_gfx->applyScreenUpdate(true);
 
 	runScript(kCardOpenScript);
 }
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 1027052..72c944f 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -486,7 +486,7 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
@@ -506,7 +506,7 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
@@ -572,7 +572,7 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(3);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
@@ -589,7 +589,7 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(2);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {
@@ -749,7 +749,7 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
@@ -766,7 +766,7 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
@@ -950,7 +950,6 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
 	if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_gfx->updateScreen();
 		_vm->_hotspots[3].enabled = false; // Disable bait hotspot
 		_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot
 	}
@@ -983,9 +982,8 @@ void RivenExternal::xbfreeytram(uint16 argc, uint16 *argv) {
 
 void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	// Remove the pellet from the plate and put it in your hand
-	_vm->getCurCard()->drawPicture(3);
 	_vm->_cursor->setCursor(kRivenPelletCursor);
-	_vm->_gfx->updateScreen();
+	_vm->getCurCard()->drawPicture(3);
 
 	// Loop until the player lets go (or quits)
 	Common::Event event;
@@ -1011,7 +1009,6 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_gfx->updateScreen();
 		_vm->_hotspots[3].enabled = false; // Disable bait hotspot
 		_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot
 	} else {
@@ -1427,21 +1424,18 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 
 	// And update the screen with the new image
 	_vm->getCurCard()->drawPicture(curPos + 2);
-	_vm->_gfx->updateScreen();
 }
 
 void RivenExternal::xglview_villageon(uint16 argc, uint16 *argv) {
 	// Turn on the left viewer to 'village mode'
 	_vm->_vars["glview"] = 2;
 	_vm->getCurCard()->drawPicture(_vm->_vars["glviewpos"] + 2);
-	_vm->_gfx->updateScreen();
 }
 
 void RivenExternal::xglview_villageoff(uint16 argc, uint16 *argv) {
 	// Turn off the left viewer when in 'village mode' (why is this external?)
 	_vm->_vars["glview"] = 0;
 	_vm->getCurCard()->drawPicture(1);
-	_vm->_gfx->updateScreen();
 }
 
 static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
@@ -1518,7 +1512,6 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
 		// Otherwise, just redraw the imager
 		timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000;
 		_vm->getCurCard()->drawPicture(8);
-		_vm->_gfx->updateScreen();
 	}
 
 	// Create the timer for the next video
@@ -1542,7 +1535,6 @@ void RivenExternal::xglview_prisonoff(uint16 argc, uint16 *argv) {
 
 	// Redraw the viewer
 	_vm->getCurCard()->drawPicture(1);
-	_vm->_gfx->updateScreen();
 }
 
 // ------------------------------------------------------------------------------------
@@ -2109,8 +2101,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 					_vm->_scriptMan->stopAllScripts();                  // Stop all running scripts (so we don't remain in the cage)
 					_vm->_video->stopVideos();                          // Stop all videos
 					_vm->_cursor->setCursor(kRivenHideCursor);          // Hide the cursor
-					_vm->getCurCard()->drawPicture(3);                             // Black out the screen
-					_vm->_gfx->updateScreen();                          // Update the screen
+					_vm->getCurCard()->drawPicture(3);                  // Black out the screen
 					_vm->_sound->playSound(0);                          // Play the link sound
 					_vm->_video->activateMLST(7, _vm->getCurCard()->getId());    // Activate Gehn Link Video
 					_vm->_video->playMovieBlockingRiven(1);             // Play Gehn Link Video
@@ -2199,7 +2190,7 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
@@ -2216,7 +2207,7 @@ void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->_gfx->updateScreen();
+	_vm->_card->drawPicture(page);
 }
 
 uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) {
@@ -2662,10 +2653,6 @@ void RivenExternal::drawMarbles() {
 void RivenExternal::xdrawmarbles(uint16 argc, uint16 *argv) {
 	// Draw marbles in the closeup
 	drawMarbles();
-
-	// We have to re-enable the updates here
-	// Would be really nice if the scripts did this for us, but alas...
-	_vm->_gfx->_updatesEnabled = true;
 }
 
 void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
@@ -2686,7 +2673,6 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
 
 	// Redraw the background
 	_vm->getCurCard()->drawPicture(1);
-	_vm->_gfx->updateScreen();
 
 	// Loop until the player lets go (or quits)
 	Common::Event event;
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 34da11f..e0cfa36 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -46,7 +46,8 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 	_mainScreen = new Graphics::Surface();
 	_mainScreen->create(608, 392, _pixelFormat);
 
-	_updatesEnabled = true;
+	_screenUpdateNesting = 0;
+	_screenUpdateRunning = false;
 	_scheduledTransition = -1;	// no transition
 	_dirtyScreen = false;
 	_inventoryDrawn = false;
@@ -72,6 +73,8 @@ MohawkSurface *RivenGraphics::decodeImage(uint16 id) {
 void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom) {
 	Graphics::Surface *surface = findImage(image)->getSurface();
 
+	beginScreenUpdate();
+
 	// Clip the width to fit on the screen. Fixes some images.
 	if (left + surface->w > 608)
 		surface->w = 608 - left;
@@ -80,24 +83,20 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
 		memcpy(_mainScreen->getBasePtr(left, i + top), surface->getBasePtr(0, i), surface->w * surface->format.bytesPerPixel);
 
 	_dirtyScreen = true;
+	applyScreenUpdate();
 }
 
 void RivenGraphics::updateScreen(Common::Rect updateRect) {
-	if (_updatesEnabled) {
-		_vm->runUpdateScreenScript();
-		_vm->_sound->triggerDrawSound();
-
-		if (_dirtyScreen) {
-			// Copy to screen if there's no transition. Otherwise transition. ;)
-			if (_scheduledTransition < 0)
-				_vm->_system->copyRectToScreen(_mainScreen->getBasePtr(updateRect.left, updateRect.top), _mainScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
-			else
-				runScheduledTransition();
+	if (_dirtyScreen) {
+		// Copy to screen if there's no transition. Otherwise transition. ;)
+		if (_scheduledTransition < 0)
+			_vm->_system->copyRectToScreen(_mainScreen->getBasePtr(updateRect.left, updateRect.top), _mainScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
+		else
+			runScheduledTransition();
 
-			// Finally, update the screen.
-			_vm->_system->updateScreen();
-			_dirtyScreen = false;
-		}
+		// Finally, update the screen.
+		_vm->_system->updateScreen();
+		_dirtyScreen = false;
 	}
 }
 
@@ -417,4 +416,28 @@ void RivenGraphics::updateCredits() {
 	}
 }
 
+void RivenGraphics::beginScreenUpdate() {
+	_screenUpdateNesting++;
+}
+
+void RivenGraphics::applyScreenUpdate(bool force) {
+	if (force) {
+		_screenUpdateNesting = 0;
+	} else {
+		_screenUpdateNesting--;
+	}
+
+	// The screen is only updated when the outermost screen update ends
+	if (_screenUpdateNesting <= 0 && !_screenUpdateRunning) {
+		_screenUpdateRunning = true;
+
+		_vm->getCurCard()->runScript(kCardUpdateScript);
+		_vm->_sound->triggerDrawSound();
+		updateScreen();
+
+		_screenUpdateNesting = 0;
+		_screenUpdateRunning = false;
+	}
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index a90c288..614f9ab 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -34,9 +34,12 @@ public:
 	RivenGraphics(MohawkEngine_Riven *vm);
 	~RivenGraphics();
 
+	// Screen updates
+	void beginScreenUpdate();
+	void applyScreenUpdate(bool force = false);
+
 	void copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom);
 	void updateScreen(Common::Rect updateRect = Common::Rect(0, 0, 608, 392));
-	bool _updatesEnabled;
 	void drawRect(Common::Rect rect, bool active);
 	void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect);
 	void drawExtrasImage(uint16 id, Common::Rect dstRect);
@@ -68,6 +71,8 @@ protected:
 private:
 	MohawkEngine_Riven *_vm;
 	MohawkBitmap *_bitmapDecoder;
+	int _screenUpdateNesting;
+	bool _screenUpdateRunning;
 
 	// Water Effects
 	struct SFXERecord {
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 68a2152..068f002 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -241,8 +241,8 @@ void RivenSimpleCommand::setupOpcodes() {
 		OPCODE(transition),
 		OPCODE(refreshCard),
 		// 0x14 (20 decimal)
-		OPCODE(disableScreenUpdate),
-		OPCODE(enableScreenUpdate),
+		OPCODE(beginScreenUpdate),
+		OPCODE(applyScreenUpdate),
 		OPCODE(empty),						// Empty
 		OPCODE(empty),						// Empty
 		// 0x18 (24 decimal)
@@ -290,9 +290,6 @@ void RivenSimpleCommand::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
 		_vm->_gfx->copyImageToScreen(argv[0], 0, 0, 608, 392);
 	else          // Copy the image to a certain part of the screen
 		_vm->_gfx->copyImageToScreen(argv[0], argv[1], argv[2], argv[3], argv[4]);
-
-	// Now, update the screen
-	_vm->_gfx->updateScreen();
 }
 
 // Command 2: go to card (card id)
@@ -443,17 +440,16 @@ void RivenSimpleCommand::refreshCard(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->refreshCard();
 }
 
-// Command 20: disable screen update
-void RivenSimpleCommand::disableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
+// Command 20: begin screen update
+void RivenSimpleCommand::beginScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
 	debug(2, "Screen update disabled");
-	_vm->_gfx->_updatesEnabled = false;
+	_vm->_gfx->beginScreenUpdate();
 }
 
-// Command 21: enable screen update
-void RivenSimpleCommand::enableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
+// Command 21: apply screen update
+void RivenSimpleCommand::applyScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
 	debug(2, "Screen update enabled");
-	_vm->_gfx->_updatesEnabled = true;
-	_vm->_gfx->updateScreen();
+	_vm->_gfx->applyScreenUpdate();
 }
 
 // Command 24: increment variable (variable, value)
@@ -573,13 +569,7 @@ void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_activatedPLST = true;
 
 	RivenCard::Picture picture = _vm->getCurCard()->getPicture(argv[0]);
-
 	_vm->_gfx->copyImageToScreen(picture.id, picture.rect.left, picture.rect.top, picture.rect.right, picture.rect.bottom);
-
-	// An update is automatically sent here as long as it's not a load or update script and updates are enabled.
-	// TODO: Fix the graphics manager
-	//if (_scriptType != kCardLoadScript && _scriptType != kCardUpdateScript)
-		_vm->_gfx->updateScreen();
 }
 
 // Command 40: activate SLST record (card ambient sound lists)
@@ -589,8 +579,8 @@ void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 	if (_vm->getCurStack() == kStackTspit && _vm->getCurCardRMAP() == 0x6e9a && argv[0] == 2)
 		return;
 
-	_vm->_sound->playSLST(argv[0], _vm->getCurCard()->getId());
 	_vm->_activatedSLST = true;
+	_vm->_sound->playSLST(argv[0], _vm->getCurCard()->getId());
 }
 
 // Command 41: activate MLST record and play
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index fd47a10..b05b99e 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -212,8 +212,8 @@ private:
 	DECLARE_OPCODE(runExternalCommand);
 	DECLARE_OPCODE(transition);
 	DECLARE_OPCODE(refreshCard);
-	DECLARE_OPCODE(disableScreenUpdate);
-	DECLARE_OPCODE(enableScreenUpdate);
+	DECLARE_OPCODE(beginScreenUpdate);
+	DECLARE_OPCODE(applyScreenUpdate);
 	DECLARE_OPCODE(incrementVariable);
 	DECLARE_OPCODE(changeStack);
 	DECLARE_OPCODE(disableMovie);


Commit: 1b062d1e39988388468bb13af97276d5674bbcbe
    https://github.com/scummvm/scummvm/commit/1b062d1e39988388468bb13af97276d5674bbcbe
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the sound lists to RivenCard

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_sound.cpp
    engines/mohawk/riven_sound.h


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 87a0cd4..9b4d056 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -450,7 +450,7 @@ bool RivenConsole::Cmd_PlaySound(int argc, const char **argv) {
 
 bool RivenConsole::Cmd_PlaySLST(int argc, const char **argv) {
 	if (argc < 2) {
-		debugPrintf("Usage: playSLST <slst index> <card, default = current>\n");
+		debugPrintf("Usage: playSLST <slst index>\n");
 
 		return true;
 	}
@@ -458,9 +458,7 @@ bool RivenConsole::Cmd_PlaySLST(int argc, const char **argv) {
 	_vm->_sound->stopSound();
 	_vm->_sound->stopAllSLST();
 
-	uint16 card = (argc == 3) ? (uint16)atoi(argv[2]) : _vm->getCurCard()->getId();
-
-	_vm->_sound->playSLST((uint16)atoi(argv[1]), card);
+	_vm->getCurCard()->playSound((uint16)atoi(argv[1]));
 	return false;
 }
 
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 9bb5a2e..34045fa 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -23,7 +23,6 @@
 #include "mohawk/riven_card.h"
 
 #include "mohawk/riven_graphics.h"
-#include "mohawk/riven_sound.h"
 
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
@@ -35,6 +34,7 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	_id(id) {
 	loadCardResource(id);
 	loadCardPictureList(id);
+	loadCardSoundList(id);
 }
 
 RivenCard::~RivenCard() {
@@ -91,7 +91,7 @@ void RivenCard::defaultLoadScript() {
 
 	// Activate the first sound list if none have been activated
 	if (!_vm->_activatedSLST)
-		_vm->_sound->playSLST(1, _id);
+		playSound(1);
 }
 
 void RivenCard::loadCardPictureList(uint16 id) {
@@ -129,4 +129,71 @@ RivenCard::Picture RivenCard::getPicture(uint16 index) const {
 	error("Could not find picture %d in card %d", index, _id);
 }
 
+void RivenCard::loadCardSoundList(uint16 id) {
+	Common::SeekableReadStream *slstStream = _vm->getResource(ID_SLST, id);
+
+	uint16 recordCount = slstStream->readUint16BE();
+	_soundList.resize(recordCount);
+
+	for (uint16 i = 0; i < recordCount; i++) {
+		SLSTRecord &slstRecord = _soundList[i];
+		slstRecord.index = slstStream->readUint16BE();
+
+		uint16 soundCount = slstStream->readUint16BE();
+
+		slstRecord.soundIds.resize(soundCount);
+		for (uint16 j = 0; j < soundCount; j++)
+			slstRecord.soundIds[j] = slstStream->readUint16BE();
+
+		slstRecord.fadeFlags = slstStream->readUint16BE();
+		slstRecord.loop = slstStream->readUint16BE();
+		slstRecord.globalVolume = slstStream->readUint16BE();
+		slstRecord.u0 = slstStream->readUint16BE();			// Unknown
+
+		if (slstRecord.u0 > 1)
+			warning("slstRecord.u0: %d non-boolean", slstRecord.u0);
+
+		slstRecord.suspend = slstStream->readUint16BE();
+
+		if (slstRecord.suspend != 0)
+			warning("slstRecord.suspend: %d non-zero", slstRecord.suspend);
+
+		slstRecord.volumes.resize(soundCount);
+		slstRecord.balances.resize(soundCount);
+		slstRecord.u2.resize(soundCount);
+
+		for (uint16 j = 0; j < soundCount; j++)
+			slstRecord.volumes[j] = slstStream->readUint16BE();
+
+		for (uint16 j = 0; j < soundCount; j++)
+			slstRecord.balances[j] = slstStream->readSint16BE();	// negative = left, 0 = center, positive = right
+
+		for (uint16 j = 0; j < soundCount; j++) {
+			slstRecord.u2[j] = slstStream->readUint16BE();		// Unknown
+
+			if (slstRecord.u2[j] != 255 && slstRecord.u2[j] != 256)
+				warning("slstRecord.u2[%d]: %d not 255 or 256", j, slstRecord.u2[j]);
+		}
+	}
+
+	delete slstStream;
+}
+
+void RivenCard::playSound(uint16 index, bool queue) {
+	if (index > 0 && index <= _soundList.size()) {
+		RivenScriptPtr script = _vm->_scriptMan->createScriptFromData(1, 40, 1, index);
+		_vm->_scriptMan->runScript(script, queue);
+	}
+}
+
+SLSTRecord RivenCard::getSound(uint16 index) const {
+	for (uint16 i = 0; i < _soundList.size(); i++) {
+		if (_soundList[i].index == index) {
+			return _soundList[i];
+		}
+	}
+
+	error("Could not find sound %d in card %d", index, _id);
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 86e8724..b83f330 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -24,6 +24,7 @@
 #define RIVEN_CARD_H
 
 #include "mohawk/riven_scripts.h"
+#include "mohawk/riven_sound.h"
 
 #include "common/rect.h"
 #include "common/system.h"
@@ -67,9 +68,16 @@ public:
 	/** Draw one of the card's pictures synchronously or asynchronously */
 	void drawPicture(uint16 index, bool queue = false);
 
+	/** Play the card's ambient sounds with the specified index */
+	void playSound(uint16 index, bool queue = false);
+
+	/** Get the card's sound description with the specified index */
+	SLSTRecord getSound(uint16 index) const;
+
 private:
 	void loadCardResource(uint16 id);
 	void loadCardPictureList(uint16 id);
+	void loadCardSoundList(uint16 id);
 
 	void initializeZipMode();
 	void defaultLoadScript();
@@ -84,6 +92,7 @@ private:
 
 	// Resource lists
 	Common::Array<Picture> _pictureList;
+	Common::Array<SLSTRecord> _soundList;
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 72c944f..a7161df 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -771,11 +771,11 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
 
 void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
 	if (_vm->_vars["bheat"] != 0)
-		_vm->_sound->playSLST(1, _vm->getCurCard()->getId());
+		_vm->getCurCard()->playSound(1);
 	else if (_vm->_vars["bcratergg"] != 0)
-		_vm->_sound->playSLST(2, _vm->getCurCard()->getId());
+		_vm->getCurCard()->playSound(2);
 	else
-		_vm->_sound->playSLST(3, _vm->getCurCard()->getId());
+		_vm->getCurCard()->playSound(3);
 }
 
 void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
@@ -841,9 +841,9 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
 	}
 
 	if (argc > 1)
-		_vm->_sound->playSLST(argv[1], _vm->getCurCard()->getId());
+		_vm->getCurCard()->playSound(argv[1]);
 	else if (argv[0] == 2)
-		_vm->_sound->playSLST(1, _vm->getCurCard()->getId());
+		_vm->getCurCard()->playSound(1);
 
 	_vm->_cursor->setCursor(kRivenHideCursor);
 	_vm->_video->playMovieBlockingRiven(11);
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 068f002..bdb30bd 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -580,7 +580,8 @@ void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 		return;
 
 	_vm->_activatedSLST = true;
-	_vm->_sound->playSLST(argv[0], _vm->getCurCard()->getId());
+	SLSTRecord picture = _vm->getCurCard()->getSound(argv[0]);
+	_vm->_sound->playSLST(picture);
 }
 
 // Command 41: activate MLST record and play
diff --git a/engines/mohawk/riven_sound.cpp b/engines/mohawk/riven_sound.cpp
index 10a23a0..569bbb4 100644
--- a/engines/mohawk/riven_sound.cpp
+++ b/engines/mohawk/riven_sound.cpp
@@ -68,64 +68,6 @@ void RivenSoundManager::playSound(uint16 id, uint16 volume, bool playOnDraw) {
 	}
 }
 
-void RivenSoundManager::playSLST(uint16 index, uint16 card) {
-	Common::SeekableReadStream *slstStream = _vm->getResource(ID_SLST, card);
-
-	uint16 recordCount = slstStream->readUint16BE();
-
-	for (uint16 i = 0; i < recordCount; i++) {
-		SLSTRecord slstRecord;
-		slstRecord.index = slstStream->readUint16BE();
-
-		uint16 soundCount = slstStream->readUint16BE();
-		slstRecord.soundIds.resize(soundCount);
-
-		for (uint16 j = 0; j < soundCount; j++)
-			slstRecord.soundIds[j] = slstStream->readUint16BE();
-
-		slstRecord.fadeFlags = slstStream->readUint16BE();
-		slstRecord.loop = slstStream->readUint16BE();
-		slstRecord.globalVolume = slstStream->readUint16BE();
-		slstRecord.u0 = slstStream->readUint16BE();			// Unknown
-
-		if (slstRecord.u0 > 1)
-			warning("slstRecord.u0: %d non-boolean", slstRecord.u0);
-
-		slstRecord.suspend = slstStream->readUint16BE();
-
-		if (slstRecord.suspend != 0)
-			warning("slstRecord.u1: %d non-zero", slstRecord.suspend);
-
-		slstRecord.volumes.resize(soundCount);
-		slstRecord.balances.resize(soundCount);
-		slstRecord.u2.resize(soundCount);
-
-		for (uint16 j = 0; j < soundCount; j++)
-			slstRecord.volumes[j] = slstStream->readUint16BE();
-
-		for (uint16 j = 0; j < soundCount; j++)
-			slstRecord.balances[j] = slstStream->readSint16BE();	// negative = left, 0 = center, positive = right
-
-		for (uint16 j = 0; j < soundCount; j++) {
-			slstRecord.u2[j] = slstStream->readUint16BE();		// Unknown
-
-			if (slstRecord.u2[j] != 255 && slstRecord.u2[j] != 256)
-				warning("slstRecord.u2[%d]: %d not 255 or 256", j, slstRecord.u2[j]);
-		}
-
-		if (slstRecord.index == index) {
-			playSLST(slstRecord);
-			delete slstStream;
-			return;
-		}
-	}
-
-	delete slstStream;
-
-	// If we have no matching entries, we do nothing and just let
-	// the previous ambient sounds continue.
-}
-
 void RivenSoundManager::playSLST(const SLSTRecord &slstRecord) {
 	if (slstRecord.soundIds.empty()) {
 		return;
diff --git a/engines/mohawk/riven_sound.h b/engines/mohawk/riven_sound.h
index c79ccc3..d4e7872 100644
--- a/engines/mohawk/riven_sound.h
+++ b/engines/mohawk/riven_sound.h
@@ -86,9 +86,6 @@ public:
 	/** Start playing an ambient sound list */
 	void playSLST(const SLSTRecord &slstRecord);
 
-	/** Start playing an ambient sound list from a resource */
-	void playSLST(uint16 index, uint16 card);
-
 	/** Stop playing the current ambient sounds */
 	void stopAllSLST(bool fade = false);
 


Commit: 23bbf05c9162e8126df21794b08eb953d65d057e
    https://github.com/scummvm/scummvm/commit/23bbf05c9162e8126df21794b08eb953d65d057e
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Start converting RivenHotspot into a class

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 9b4d056..966929b 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -510,17 +510,17 @@ bool RivenConsole::Cmd_ChangeStack(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
-	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), _vm->getHotspotCount());
+	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), _vm->_hotspots.size());
 
-	for (uint16 i = 0; i < _vm->getHotspotCount(); i++) {
-		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, _vm->_hotspots[i].index, _vm->_hotspots[i].blstID);
+	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
+		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, _vm->_hotspots[i]->index, _vm->_hotspots[i]->blstID);
 
-		if (_vm->_hotspots[i].enabled)
+		if (_vm->_hotspots[i]->enabled)
 			debugPrintf("enabled");
 		else
 			debugPrintf("disabled");
 
-		debugPrintf(") - (%d, %d, %d, %d)\n", _vm->_hotspots[i].rect.left, _vm->_hotspots[i].rect.top, _vm->_hotspots[i].rect.right, _vm->_hotspots[i].rect.bottom);
+		debugPrintf(") - (%d, %d, %d, %d)\n", _vm->_hotspots[i]->rect.left, _vm->_hotspots[i]->rect.top, _vm->_hotspots[i]->rect.right, _vm->_hotspots[i]->rect.bottom);
 		debugPrintf("    Name = %s\n", _vm->getHotspotName(i).c_str());
 	}
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index b7ff1c3..586c5f5 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -58,7 +58,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_ignoreNextMouseUp = false;
 	_extrasFile = nullptr;
 	_curStack = kStackUnknown;
-	_hotspots = nullptr;
 	_gfx = nullptr;
 	_sound = nullptr;
 	_externalScriptHandler = nullptr;
@@ -68,7 +67,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_saveLoad = nullptr;
 	_optionsDialog = nullptr;
 	_card = nullptr;
-	_hotspotCount = 0;
 	_curHotspot = -1;
 	removeTimer();
 
@@ -105,7 +103,9 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
 	delete _scriptMan;
 	delete _optionsDialog;
 	delete _rnd;
-	delete[] _hotspots;
+	for (uint i = 0; i < _hotspots.size(); i++) {
+		delete _hotspots[i];
+	}
 	delete g_atrusJournalRect1;
 	delete g_atrusJournalRect2;
 	delete g_cathJournalRect2;
@@ -257,8 +257,8 @@ void MohawkEngine_Riven::handleEvents() {
 			case Common::KEYCODE_F4:
 				_showHotspots = !_showHotspots;
 				if (_showHotspots) {
-					for (uint16 i = 0; i < _hotspotCount; i++)
-						_gfx->drawRect(_hotspots[i].rect, _hotspots[i].enabled);
+					for (uint16 i = 0; i < _hotspots.size(); i++)
+						_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->enabled);
 					needsUpdate = true;
 				} else
 					refreshCard();
@@ -414,8 +414,8 @@ void MohawkEngine_Riven::refreshCard() {
 	_card->open();
 
 	if (_showHotspots)
-		for (uint16 i = 0; i < _hotspotCount; i++)
-			_gfx->drawRect(_hotspots[i].rect, _hotspots[i].enabled);
+		for (uint16 i = 0; i < _hotspots.size(); i++)
+			_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->enabled);
 
 	// Now we need to redraw the cursor if necessary and handle mouse over scripts
 	updateCurrentHotspot();
@@ -425,46 +425,17 @@ void MohawkEngine_Riven::refreshCard() {
 }
 
 void MohawkEngine_Riven::loadHotspots(uint16 id) {
-	// Clear old hotspots
-	delete[] _hotspots;
-
-	// NOTE: The hotspot scripts are cleared by the RivenScriptManager automatically.
+	for (uint i = 0; i < _hotspots.size(); i++) {
+		delete _hotspots[i];
+	}
 
 	Common::SeekableReadStream *inStream = getResource(ID_HSPT, id);
 
-	_hotspotCount = inStream->readUint16BE();
-	_hotspots = new RivenHotspot[_hotspotCount];
-
-	for (uint16 i = 0; i < _hotspotCount; i++) {
-		_hotspots[i].enabled = true;
-
-		_hotspots[i].blstID = inStream->readUint16BE();
-		_hotspots[i].name_resource = inStream->readSint16BE();
-
-		int16 left = inStream->readSint16BE();
-		int16 top = inStream->readSint16BE();
-		int16 right = inStream->readSint16BE();
-		int16 bottom = inStream->readSint16BE();
+	uint16 hotspotCount = inStream->readUint16BE();
+	_hotspots.resize(hotspotCount);
 
-		// Riven has some invalid rects, disable them here
-		// Known weird hotspots:
-		// - tspit 371 (DVD: 377), hotspot 4
-		if (left >= right || top >= bottom) {
-			warning("%s %d hotspot %d is invalid: (%d, %d, %d, %d)", getStackName(_curStack).c_str(), id, i, left, top, right, bottom);
-			left = top = right = bottom = 0;
-			_hotspots[i].enabled = 0;
-		}
-
-		_hotspots[i].rect = Common::Rect(left, top, right, bottom);
-
-		_hotspots[i].u0 = inStream->readUint16BE();
-		_hotspots[i].mouse_cursor = inStream->readUint16BE();
-		_hotspots[i].index = inStream->readUint16BE();
-		_hotspots[i].u1 = inStream->readSint16BE();
-		_hotspots[i].zipModeHotspot = inStream->readUint16BE();
-
-		// Read in the scripts now
-		_hotspots[i].scripts = _scriptMan->readScripts(inStream);
+	for (uint16 i = 0; i < hotspotCount; i++) {
+		_hotspots[i] = new RivenHotspot(this, inStream);
 	}
 
 	delete inStream;
@@ -474,11 +445,11 @@ void MohawkEngine_Riven::loadHotspots(uint16 id) {
 void MohawkEngine_Riven::updateZipMode() {
 	// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
 
-	for (uint32 i = 0; i < _hotspotCount; i++) {
-		if (_hotspots[i].zipModeHotspot) {
+	for (uint32 i = 0; i < _hotspots.size(); i++) {
+		if (_hotspots[i]->zipModeHotspot) {
 			if (_vars["azip"] != 0) {
 				// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
-				Common::String hotspotName = getName(HotspotNames, _hotspots[i].name_resource);
+				Common::String hotspotName = getName(HotspotNames, _hotspots[i]->name_resource);
 
 				bool foundMatch = false;
 
@@ -489,9 +460,9 @@ void MohawkEngine_Riven::updateZipMode() {
 							break;
 						}
 
-				_hotspots[i].enabled = foundMatch;
+				_hotspots[i]->enabled = foundMatch;
 			} else // Disable the hotspot if zip mode is disabled
-				_hotspots[i].enabled = false;
+				_hotspots[i]->enabled = false;
 		}
 	}
 }
@@ -499,8 +470,8 @@ void MohawkEngine_Riven::updateZipMode() {
 void MohawkEngine_Riven::checkHotspotChange() {
 	uint16 hotspotIndex = 0;
 	bool foundHotspot = false;
-	for (uint16 i = 0; i < _hotspotCount; i++)
-		if (_hotspots[i].enabled && _hotspots[i].rect.contains(_eventMan->getMousePos())) {
+	for (uint16 i = 0; i < _hotspots.size(); i++)
+		if (_hotspots[i]->enabled && _hotspots[i]->rect.contains(_eventMan->getMousePos())) {
 			foundHotspot = true;
 			hotspotIndex = i;
 		}
@@ -508,7 +479,7 @@ void MohawkEngine_Riven::checkHotspotChange() {
 	if (foundHotspot) {
 		if (_curHotspot != hotspotIndex) {
 			_curHotspot = hotspotIndex;
-			_cursor->setCursor(_hotspots[_curHotspot].mouse_cursor);
+			_cursor->setCursor(_hotspots[_curHotspot]->mouse_cursor);
 			_system->updateScreen();
 		}
 	} else {
@@ -524,12 +495,12 @@ void MohawkEngine_Riven::updateCurrentHotspot() {
 }
 
 Common::String MohawkEngine_Riven::getHotspotName(uint16 hotspot) {
-	assert(hotspot < _hotspotCount);
+	assert(hotspot < _hotspots.size());
 
-	if (_hotspots[hotspot].name_resource < 0)
+	if (_hotspots[hotspot]->name_resource < 0)
 		return Common::String();
 
-	return getName(HotspotNames, _hotspots[hotspot].name_resource);
+	return getName(HotspotNames, _hotspots[hotspot]->name_resource);
 }
 
 void MohawkEngine_Riven::checkInventoryClick() {
@@ -663,13 +634,7 @@ uint32 MohawkEngine_Riven::getCurCardRMAP() {
 }
 
 void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
-	assert(hotspot < _hotspotCount);
-	for (uint16 i = 0; i < _hotspots[hotspot].scripts.size(); i++)
-		if (_hotspots[hotspot].scripts[i].type == scriptType) {
-			RivenScriptPtr script = _hotspots[hotspot].scripts[i].script;
-			_scriptMan->runScript(script, false);
-			break;
-		}
+	_hotspots[hotspot]->runScript(scriptType);
 }
 
 void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 5566161..2fa8c65 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -42,6 +42,7 @@ class RivenConsole;
 class RivenSaveLoad;
 class RivenOptionsDialog;
 class RivenCard;
+class RivenHotspot;
 class RivenSoundManager;
 
 // Riven Stack Types
@@ -86,20 +87,6 @@ extern Common::Rect *g_cathJournalRect3;
 extern Common::Rect *g_trapBookRect3;
 extern Common::Rect *g_demoExitRect;
 
-struct RivenHotspot {
-	uint16 blstID;
-	int16 name_resource;
-	Common::Rect rect;
-	uint16 u0;
-	uint16 mouse_cursor;
-	uint16 index;
-	int16 u1;
-	int16 zipModeHotspot;
-	RivenScriptList scripts;
-
-	bool enabled;
-};
-
 struct ZipMode {
 	Common::String name;
 	uint16 id;
@@ -148,7 +135,6 @@ private:
 	void handleEvents();
 
 	// Hotspot related functions and variables
-	uint16 _hotspotCount;
 	void loadHotspots(uint16);
 	void checkInventoryClick();
 	bool _showHotspots;
@@ -180,10 +166,9 @@ public:
 	uint32 getCurCardRMAP();
 
 	// Hotspot functions/variables
-	RivenHotspot *_hotspots;
+	Common::Array<RivenHotspot *> _hotspots;
 	int32 _curHotspot;
 	Common::Array<ZipMode> _zipModeData;
-	uint16 getHotspotCount() const { return _hotspotCount; }
 	void runHotspotScript(uint16 hotspot, uint16 scriptType);
 	int32 getCurHotspot() const { return _curHotspot; }
 	Common::String getHotspotName(uint16 hotspot);
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 34045fa..9f4635f 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -196,4 +196,50 @@ SLSTRecord RivenCard::getSound(uint16 index) const {
 	error("Could not find sound %d in card %d", index, _id);
 }
 
+RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
+		_vm(vm) {
+	loadFromStream(stream);
+}
+
+void RivenHotspot::loadFromStream(Common::ReadStream *stream) {
+	enabled = true;
+
+	blstID = stream->readUint16BE();
+	name_resource = stream->readSint16BE();
+
+	int16 left = stream->readSint16BE();
+	int16 top = stream->readSint16BE();
+	int16 right = stream->readSint16BE();
+	int16 bottom = stream->readSint16BE();
+
+	// Riven has some invalid rects, disable them here
+	// Known weird hotspots:
+	// - tspit 371 (DVD: 377), hotspot 4
+	if (left >= right || top >= bottom) {
+		warning("Invalid hotspot: (%d, %d, %d, %d)", left, top, right, bottom);
+		left = top = right = bottom = 0;
+		enabled = 0;
+	}
+
+	rect = Common::Rect(left, top, right, bottom);
+
+	_u0 = stream->readUint16BE();
+	mouse_cursor = stream->readUint16BE();
+	index = stream->readUint16BE();
+	_u1 = stream->readSint16BE();
+	zipModeHotspot = stream->readUint16BE();
+
+	// Read in the scripts now
+	_scripts = _vm->_scriptMan->readScripts(stream);
+}
+
+void RivenHotspot::runScript(uint16 scriptType) {
+	for (uint16 i = 0; i < _scripts.size(); i++)
+		if (_scripts[i].type == scriptType) {
+			RivenScriptPtr script = _scripts[i].script;
+			_vm->_scriptMan->runScript(script, false);
+			break;
+		}
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index b83f330..b4e4197 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -95,6 +95,38 @@ private:
 	Common::Array<SLSTRecord> _soundList;
 };
 
+/**
+ * A Card Hotspot
+ *
+ * Hotspots are named rectangular areas of the view.
+ * Hotspots can be interactive through their scripts.
+ */
+class RivenHotspot {
+public:
+	RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream);
+
+	/** Run one of the hotspot's scripts */
+	void runScript(uint16 scriptType);
+
+	uint16 blstID;
+	int16 name_resource;
+	uint16 index;
+	Common::Rect rect;
+	uint16 mouse_cursor;
+	int16 zipModeHotspot;
+
+	bool enabled; // TODO: Remove
+
+private:
+	void loadFromStream(Common::ReadStream *stream);
+
+	MohawkEngine_Riven *_vm;
+
+	uint16 _u0;
+	int16 _u1;
+	RivenScriptList _scripts;
+};
+
 } // End of namespace Mohawk
 
 #endif
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index a7161df..b0aa782 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -316,19 +316,19 @@ void RivenExternal::checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDome
 	// Let's see if we're all matched up...
 	if (_vm->_vars["adomecombo"] == _sliderState) {
 		// Set the button hotspot to the open dome hotspot
-		_vm->_hotspots[resetSlidersHotspot].enabled = false;
-		_vm->_hotspots[openDomeHotspot].enabled = true;
+		_vm->_hotspots[resetSlidersHotspot]->enabled = false;
+		_vm->_hotspots[openDomeHotspot]->enabled = true;
 	} else {
 		// Set the button hotspot to the reset sliders hotspot
-		_vm->_hotspots[resetSlidersHotspot].enabled = true;
-		_vm->_hotspots[openDomeHotspot].enabled = false;
+		_vm->_hotspots[resetSlidersHotspot]->enabled = true;
+		_vm->_hotspots[openDomeHotspot]->enabled = false;
 	}
 }
 
 void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
 	// Set the cursor based on _sliderState and what hotspot we're over
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		if (_vm->_hotspots[i + startHotspot].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+		if (_vm->_hotspots[i + startHotspot]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 			if (_sliderState & (1 << (24 - i)))
 				_vm->_cursor->setCursor(kRivenOpenHandCursor);
 			else
@@ -343,7 +343,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 	int16 foundSlider = -1;
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		if (_vm->_hotspots[i + startHotspot].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+		if (_vm->_hotspots[i + startHotspot]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 			// If the slider is not at this hotspot, we can't do anything else
 			if (!(_sliderState & (1 << (24 - i))))
 				return;
@@ -367,7 +367,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 		while (_vm->_system->getEventManager()->pollEvent(event)) {
 			switch (event.type) {
 			case Common::EVENT_MOUSEMOVE:
-				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot + 1].rect.contains(event.mouse)) {
+				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot + 1]->rect.contains(event.mouse)) {
 					// We've moved the slider right one space
 					_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
 					foundSlider++;
@@ -376,7 +376,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 					// Now play a click sound and redraw
 					_vm->_sound->playSound(soundId);
 					drawDomeSliders(startHotspot);
-				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot - 1].rect.contains(event.mouse)) {
+				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot - 1]->rect.contains(event.mouse)) {
 					// We've moved the slider left one space
 					_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
 					foundSlider--;
@@ -414,10 +414,10 @@ void RivenExternal::drawDomeSliders(uint16 startHotspot) {
 	uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		Common::Rect srcRect = _vm->_hotspots[startHotspot + i].rect;
+		Common::Rect srcRect = _vm->_hotspots[startHotspot + i]->rect;
 		srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
 
-		Common::Rect dstRect = _vm->_hotspots[startHotspot + i].rect;
+		Common::Rect dstRect = _vm->_hotspots[startHotspot + i]->rect;
 
 		if (_sliderState & (1 << (24 - i)))
 			_vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect);
@@ -450,13 +450,13 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
 
 	// Set hotspots depending on the page
 	if (page == 1) {
-		_vm->_hotspots[1].enabled = false;
-		_vm->_hotspots[2].enabled = false;
-		_vm->_hotspots[3].enabled = true;
+		_vm->_hotspots[1]->enabled = false;
+		_vm->_hotspots[2]->enabled = false;
+		_vm->_hotspots[3]->enabled = true;
 	} else {
-		_vm->_hotspots[1].enabled = true;
-		_vm->_hotspots[2].enabled = true;
-		_vm->_hotspots[3].enabled = false;
+		_vm->_hotspots[1]->enabled = true;
+		_vm->_hotspots[2]->enabled = true;
+		_vm->_hotspots[3]->enabled = false;
 	}
 
 	// Draw the image of the page
@@ -515,13 +515,13 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
 
 	// Set hotspots depending on the page
 	if (page == 1) {
-		_vm->_hotspots[1].enabled = false;
-		_vm->_hotspots[2].enabled = false;
-		_vm->_hotspots[3].enabled = true;
+		_vm->_hotspots[1]->enabled = false;
+		_vm->_hotspots[2]->enabled = false;
+		_vm->_hotspots[3]->enabled = true;
 	} else {
-		_vm->_hotspots[1].enabled = true;
-		_vm->_hotspots[2].enabled = true;
-		_vm->_hotspots[3].enabled = false;
+		_vm->_hotspots[1]->enabled = true;
+		_vm->_hotspots[2]->enabled = true;
+		_vm->_hotspots[3]->enabled = false;
 	}
 
 	// Draw the image of the page
@@ -947,11 +947,11 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();
 
 	// Set the bait if we put it on the plate
-	if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+	if (_vm->_hotspots[9]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_hotspots[3].enabled = false; // Disable bait hotspot
-		_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot
+		_vm->_hotspots[3]->enabled = false; // Disable bait hotspot
+		_vm->_hotspots[9]->enabled = true; // Enable baitplate hotspot
 	}
 }
 
@@ -1006,15 +1006,15 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();
 
 	// Set the bait if we put it on the plate, remove otherwise
-	if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+	if (_vm->_hotspots[9]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_hotspots[3].enabled = false; // Disable bait hotspot
-		_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot
+		_vm->_hotspots[3]->enabled = false; // Disable bait hotspot
+		_vm->_hotspots[9]->enabled = true; // Enable baitplate hotspot
 	} else {
 		_vm->_vars["bbait"] = 0;
-		_vm->_hotspots[3].enabled = true; // Enable bait hotspot
-		_vm->_hotspots[9].enabled = false; // Disable baitplate hotspot
+		_vm->_hotspots[3]->enabled = true; // Enable bait hotspot
+		_vm->_hotspots[9]->enabled = false; // Disable baitplate hotspot
 	}
 }
 
@@ -1194,8 +1194,8 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
 
 	// Get our mouse position and adjust it to the beginning of the hotspot
 	Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
-	mousePos.x -= _vm->_hotspots[3].rect.left;
-	mousePos.y -= _vm->_hotspots[3].rect.top;
+	mousePos.x -= _vm->_hotspots[3]->rect.left;
+	mousePos.y -= _vm->_hotspots[3]->rect.top;
 
 	// And now adjust it to which box we hit
 	mousePos.x /= 10;
@@ -1995,8 +1995,8 @@ void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) {
 	}
 
 	// Enable the correct hotspots for the movement now
-	_vm->_hotspots[2].enabled = !_vm->_hotspots[2].enabled;
-	_vm->_hotspots[3].enabled = !_vm->_hotspots[3].enabled;
+	_vm->_hotspots[2]->enabled = !_vm->_hotspots[2]->enabled;
+	_vm->_hotspots[3]->enabled = !_vm->_hotspots[3]->enabled;
 
 	// Update the cursor
 	_vm->updateCurrentHotspot();
@@ -2048,7 +2048,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 	// Track down our hotspot
 	// Of course, they're not in any sane order...
 	static const uint16 hotspotMap[] = { 1, 3, 2, 0 };
-	Common::Rect hotspotRect = _vm->_hotspots[hotspotMap[argv[3] - 1]].rect;
+	Common::Rect hotspotRect = _vm->_hotspots[hotspotMap[argv[3] - 1]]->rect;
 
 	debug(0, "xbookclick:");
 	debug(0, "\tVideo Code = %d", argv[0]);
@@ -2157,9 +2157,9 @@ void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {
 	_vm->_video->playMovieBlockingRiven(1);
 
 	// Set the hotspots into their correct states
-	_vm->_hotspots[2].enabled = false;
-	_vm->_hotspots[5].enabled = false;
-	_vm->_hotspots[6].enabled = true;
+	_vm->_hotspots[2]->enabled = false;
+	_vm->_hotspots[5]->enabled = false;
+	_vm->_hotspots[6]->enabled = true;
 
 	// We now need to draw PLST 1 and refresh, but PLST 1 is
 	// drawn when refreshing anyway, so don't worry about that.
@@ -2484,7 +2484,7 @@ void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {
 
 	// If we have hit the correct 5 buttons in a row, activate the hotspot to open up the
 	// telescope cover.
-	_vm->_hotspots[9].enabled = (correctDigits == 5);
+	_vm->_hotspots[9]->enabled = (correctDigits == 5);
 }
 
 // Atrus' Journal and Trap Book are added to inventory
@@ -2617,9 +2617,9 @@ void RivenExternal::setMarbleHotspots() {
 		uint32 &marblePos = _vm->_vars[s_marbleNames[i]];
 
 		if (marblePos == 0) // In the receptacle
-			_vm->_hotspots[i + 3].rect = _marbleBaseHotspots[i];
+			_vm->_hotspots[i + 3]->rect = _marbleBaseHotspots[i];
 		else                 // On the grid
-			_vm->_hotspots[i + 3].rect = generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos));
+			_vm->_hotspots[i + 3]->rect = generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos));
 	}
 }
 
@@ -2627,7 +2627,7 @@ void RivenExternal::xt7800_setup(uint16 argc, uint16 *argv) {
 	// First, let's store the base receptacle hotspots for the marbles
 	if (_marbleBaseHotspots.empty())
 		for (uint16 i = 0; i < kMarbleCount; i++)
-			_marbleBaseHotspots.push_back(_vm->_hotspots[i + 3].rect);
+			_marbleBaseHotspots.push_back(_vm->_hotspots[i + 3]->rect);
 
 	// Move the marble hotspots based on their position variables
 	setMarbleHotspots();
@@ -2640,7 +2640,7 @@ void RivenExternal::drawMarbles() {
 		if (_vm->_vars["themarble"] - 1 == i)
 			continue;
 
-		Common::Rect rect = _vm->_hotspots[i + 3].rect;
+		Common::Rect rect = _vm->_hotspots[i + 3]->rect;
 		// Trim the rect down a bit
 		rect.left += 3;
 		rect.top += 3;
@@ -2663,7 +2663,7 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
 	marble = 0;
 
 	for (uint32 i = 0; i < kMarbleCount; i++)
-		if (_vm->_hotspots[i + 3].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+		if (_vm->_hotspots[i + 3]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 			marble = i + 1;
 			break;
 		}
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index bdb30bd..35b7195 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -358,10 +358,10 @@ void RivenSimpleCommand::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 9: enable hotspot (blst_id)
 void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
-	for (uint16 i = 0; i < _vm->getHotspotCount(); i++) {
-		if (_vm->_hotspots[i].blstID == argv[0]) {
+	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
+		if (_vm->_hotspots[i]->blstID == argv[0]) {
 			debug(2, "Enabling hotspot with BLST ID %d", argv[0]);
-			_vm->_hotspots[i].enabled = true;
+			_vm->_hotspots[i]->enabled = true;
 		}
 	}
 
@@ -371,10 +371,10 @@ void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 10: disable hotspot (blst_id)
 void RivenSimpleCommand::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
-	for (uint16 i = 0; i < _vm->getHotspotCount(); i++) {
-		if (_vm->_hotspots[i].blstID == argv[0]) {
+	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
+		if (_vm->_hotspots[i]->blstID == argv[0]) {
 			debug(2, "Disabling hotspot with BLST ID %d", argv[0]);
-			_vm->_hotspots[i].enabled = false;
+			_vm->_hotspots[i]->enabled = false;
 		}
 	}
 
@@ -601,9 +601,9 @@ void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 		uint16 hotspotID = blst->readUint16BE();
 
 		if (argv[0] == index)
-			for (uint16 j = 0; j < _vm->getHotspotCount(); j++)
-				if (_vm->_hotspots[j].blstID == hotspotID)
-					_vm->_hotspots[j].enabled = (enabled == 1);
+			for (uint16 j = 0; j < _vm->_hotspots.size(); j++)
+				if (_vm->_hotspots[j]->blstID == hotspotID)
+					_vm->_hotspots[j]->enabled = (enabled == 1);
 	}
 
 	delete blst;


Commit: 6b988670e89b01c554495058f9992bc7b8e25a2d
    https://github.com/scummvm/scummvm/commit/6b988670e89b01c554495058f9992bc7b8e25a2d
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Turn the active hotspot into a pointer

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_external.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 966929b..95a2064 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -513,15 +513,16 @@ bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
 	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), _vm->_hotspots.size());
 
 	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
-		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, _vm->_hotspots[i]->index, _vm->_hotspots[i]->blstID);
+		RivenHotspot *hotspot = _vm->_hotspots[i];
+		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, hotspot->index, hotspot->blstID);
 
-		if (_vm->_hotspots[i]->enabled)
+		if (hotspot->enabled)
 			debugPrintf("enabled");
 		else
 			debugPrintf("disabled");
 
-		debugPrintf(") - (%d, %d, %d, %d)\n", _vm->_hotspots[i]->rect.left, _vm->_hotspots[i]->rect.top, _vm->_hotspots[i]->rect.right, _vm->_hotspots[i]->rect.bottom);
-		debugPrintf("    Name = %s\n", _vm->getHotspotName(i).c_str());
+		debugPrintf(") - (%d, %d, %d, %d)\n", hotspot->rect.left, hotspot->rect.top, hotspot->rect.right, hotspot->rect.bottom);
+		debugPrintf("    Name = %s\n", _vm->getHotspotName(hotspot).c_str());
 	}
 
 	return true;
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 586c5f5..50140f5 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -67,7 +67,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_saveLoad = nullptr;
 	_optionsDialog = nullptr;
 	_card = nullptr;
-	_curHotspot = -1;
+	_curHotspot = nullptr;
 	removeTimer();
 
 	// NOTE: We can never really support CD swapping. All of the music files
@@ -227,17 +227,17 @@ void MohawkEngine_Riven::handleEvents() {
 			needsUpdate = true;
 			break;
 		case Common::EVENT_LBUTTONDOWN:
-			if (_curHotspot >= 0) {
+			if (_curHotspot) {
 				checkSunnerAlertClick();
-				runHotspotScript(_curHotspot, kMouseDownScript);
+				_curHotspot->runScript(kMouseDownScript);
 			}
 			break;
 		case Common::EVENT_LBUTTONUP:
 			// See RivenScript::switchCard() for more information on why we sometimes
 			// disable the next up event.
 			if (!_ignoreNextMouseUp) {
-				if (_curHotspot >= 0)
-					runHotspotScript(_curHotspot, kMouseUpScript);
+				if (_curHotspot)
+					_curHotspot->runScript(kMouseUpScript);
 				else
 					checkInventoryClick();
 			}
@@ -294,8 +294,8 @@ void MohawkEngine_Riven::handleEvents() {
 		}
 	}
 
-	if (_curHotspot >= 0)
-		runHotspotScript(_curHotspot, kMouseInsideScript);
+	if (_curHotspot)
+		_curHotspot->runScript(kMouseInsideScript);
 
 	// Update the screen if we need to
 	if (needsUpdate)
@@ -468,39 +468,35 @@ void MohawkEngine_Riven::updateZipMode() {
 }
 
 void MohawkEngine_Riven::checkHotspotChange() {
-	uint16 hotspotIndex = 0;
-	bool foundHotspot = false;
+	RivenHotspot *hotspot = nullptr;
 	for (uint16 i = 0; i < _hotspots.size(); i++)
 		if (_hotspots[i]->enabled && _hotspots[i]->rect.contains(_eventMan->getMousePos())) {
-			foundHotspot = true;
-			hotspotIndex = i;
+			hotspot = _hotspots[i];
 		}
 
-	if (foundHotspot) {
-		if (_curHotspot != hotspotIndex) {
-			_curHotspot = hotspotIndex;
-			_cursor->setCursor(_hotspots[_curHotspot]->mouse_cursor);
+	if (hotspot) {
+		if (_curHotspot != hotspot) {
+			_curHotspot = hotspot;
+			_cursor->setCursor(hotspot->mouse_cursor);
 			_system->updateScreen();
 		}
 	} else {
-		_curHotspot = -1;
+		_curHotspot = nullptr;
 		_cursor->setCursor(kRivenMainCursor);
 		_system->updateScreen();
 	}
 }
 
 void MohawkEngine_Riven::updateCurrentHotspot() {
-	_curHotspot = -1;
+	_curHotspot = nullptr;
 	checkHotspotChange();
 }
 
-Common::String MohawkEngine_Riven::getHotspotName(uint16 hotspot) {
-	assert(hotspot < _hotspots.size());
-
-	if (_hotspots[hotspot]->name_resource < 0)
+Common::String MohawkEngine_Riven::getHotspotName(const RivenHotspot *hotspot) {
+	if (hotspot->name_resource < 0)
 		return Common::String();
 
-	return getName(HotspotNames, _hotspots[hotspot]->name_resource);
+	return getName(HotspotNames, hotspot->name_resource);
 }
 
 void MohawkEngine_Riven::checkInventoryClick() {
@@ -633,10 +629,6 @@ uint32 MohawkEngine_Riven::getCurCardRMAP() {
 	return rmapCode;
 }
 
-void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
-	_hotspots[hotspot]->runScript(scriptType);
-}
-
 void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
 	uint32 startTime = _system->getMillis();
 
@@ -935,7 +927,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 		return;
 
 	// Only set the sunners variable on the forward hotspot
-	if ((rmapCode == 0x79bd && _curHotspot != 1) || (rmapCode == 0x7beb && _curHotspot != 2))
+	if ((rmapCode == 0x79bd && _curHotspot->index != 2) || (rmapCode == 0x7beb && _curHotspot->index != 3))
 		return;
 
 	// If the alert video is no longer playing, we have nothing left to do
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 2fa8c65..e4f0803 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -167,11 +167,10 @@ public:
 
 	// Hotspot functions/variables
 	Common::Array<RivenHotspot *> _hotspots;
-	int32 _curHotspot;
+	RivenHotspot *_curHotspot;
 	Common::Array<ZipMode> _zipModeData;
-	void runHotspotScript(uint16 hotspot, uint16 scriptType);
-	int32 getCurHotspot() const { return _curHotspot; }
-	Common::String getHotspotName(uint16 hotspot);
+	RivenHotspot *getCurHotspot() const { return _curHotspot; }
+	Common::String getHotspotName(const RivenHotspot *hotspot);
 	void updateCurrentHotspot();
 	void addZipVisitedCard(uint16 cardId, uint16 cardNameId);
 
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index b0aa782..86c58ff 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -1342,7 +1342,7 @@ void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) {
 	// Calculate how much we're moving
 	static const uint16 hotspotPositions[] = { 2, 1, 5, 4, 3 };
 	uint32 &curPos = _vm->_vars["grviewpos"];
-	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot - 1];
+	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->index - 2];
 
 	// Now play the movie
 	VideoHandle handle = _vm->_video->playMovieRiven(1);
@@ -1411,7 +1411,7 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	// Calculate how much we're moving
 	static const uint16 hotspotPositions[] = { 1, 5, 4, 2, 0, 0, 3 };
 	uint32 &curPos = _vm->_vars["glviewpos"];
-	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot - 1];
+	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->index - 2];
 
 	// Now play the movie
 	VideoHandle handle = _vm->_video->playMovieRiven(1);


Commit: 17f1903833491d1e80cb2091a0a0bd7dfe4280de
    https://github.com/scummvm/scummvm/commit/17f1903833491d1e80cb2091a0a0bd7dfe4280de
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Remove the RivenHotspot enabled field

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 95a2064..06c058e 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -516,7 +516,7 @@ bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
 		RivenHotspot *hotspot = _vm->_hotspots[i];
 		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, hotspot->index, hotspot->blstID);
 
-		if (hotspot->enabled)
+		if (hotspot->isEnabled())
 			debugPrintf("enabled");
 		else
 			debugPrintf("disabled");
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 50140f5..417d271 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -258,7 +258,7 @@ void MohawkEngine_Riven::handleEvents() {
 				_showHotspots = !_showHotspots;
 				if (_showHotspots) {
 					for (uint16 i = 0; i < _hotspots.size(); i++)
-						_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->enabled);
+						_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->isEnabled());
 					needsUpdate = true;
 				} else
 					refreshCard();
@@ -415,7 +415,7 @@ void MohawkEngine_Riven::refreshCard() {
 
 	if (_showHotspots)
 		for (uint16 i = 0; i < _hotspots.size(); i++)
-			_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->enabled);
+			_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->isEnabled());
 
 	// Now we need to redraw the cursor if necessary and handle mouse over scripts
 	updateCurrentHotspot();
@@ -446,7 +446,7 @@ void MohawkEngine_Riven::updateZipMode() {
 	// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
 
 	for (uint32 i = 0; i < _hotspots.size(); i++) {
-		if (_hotspots[i]->zipModeHotspot) {
+		if (_hotspots[i]->isZip()) {
 			if (_vars["azip"] != 0) {
 				// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
 				Common::String hotspotName = getName(HotspotNames, _hotspots[i]->name_resource);
@@ -460,9 +460,9 @@ void MohawkEngine_Riven::updateZipMode() {
 							break;
 						}
 
-				_hotspots[i]->enabled = foundMatch;
+				_hotspots[i]->enable(foundMatch);
 			} else // Disable the hotspot if zip mode is disabled
-				_hotspots[i]->enabled = false;
+				_hotspots[i]->enable(false);
 		}
 	}
 }
@@ -470,7 +470,7 @@ void MohawkEngine_Riven::updateZipMode() {
 void MohawkEngine_Riven::checkHotspotChange() {
 	RivenHotspot *hotspot = nullptr;
 	for (uint16 i = 0; i < _hotspots.size(); i++)
-		if (_hotspots[i]->enabled && _hotspots[i]->rect.contains(_eventMan->getMousePos())) {
+		if (_hotspots[i]->isEnabled() && _hotspots[i]->rect.contains(_eventMan->getMousePos())) {
 			hotspot = _hotspots[i];
 		}
 
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 9f4635f..32eb63c 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -202,7 +202,7 @@ RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 }
 
 void RivenHotspot::loadFromStream(Common::ReadStream *stream) {
-	enabled = true;
+	_flags = kFlagEnabled;
 
 	blstID = stream->readUint16BE();
 	name_resource = stream->readSint16BE();
@@ -218,7 +218,7 @@ void RivenHotspot::loadFromStream(Common::ReadStream *stream) {
 	if (left >= right || top >= bottom) {
 		warning("Invalid hotspot: (%d, %d, %d, %d)", left, top, right, bottom);
 		left = top = right = bottom = 0;
-		enabled = 0;
+		enable(false);
 	}
 
 	rect = Common::Rect(left, top, right, bottom);
@@ -227,7 +227,7 @@ void RivenHotspot::loadFromStream(Common::ReadStream *stream) {
 	mouse_cursor = stream->readUint16BE();
 	index = stream->readUint16BE();
 	_u1 = stream->readSint16BE();
-	zipModeHotspot = stream->readUint16BE();
+	_flags |= stream->readUint16BE();
 
 	// Read in the scripts now
 	_scripts = _vm->_scriptMan->readScripts(stream);
@@ -242,4 +242,20 @@ void RivenHotspot::runScript(uint16 scriptType) {
 		}
 }
 
+bool RivenHotspot::isEnabled() const {
+	return (_flags & kFlagEnabled) != 0;
+}
+
+void RivenHotspot::enable(bool e) {
+	if (e) {
+		_flags |= kFlagEnabled;
+	} else {
+		_flags &= ~kFlagEnabled;
+	}
+}
+
+bool RivenHotspot::isZip() const {
+	return (_flags & kFlagZip) != 0;
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index b4e4197..935ac05 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -108,22 +108,34 @@ public:
 	/** Run one of the hotspot's scripts */
 	void runScript(uint16 scriptType);
 
+	/** Enable or disable the hotspot */
+	void enable(bool e);
+
+	/** Can the hotspot be interacted with? */
+	bool isEnabled() const;
+
+	/** Is the hotspot's purpose to zip to another card */
+	bool isZip() const;
+
 	uint16 blstID;
 	int16 name_resource;
 	uint16 index;
 	Common::Rect rect;
 	uint16 mouse_cursor;
-	int16 zipModeHotspot;
-
-	bool enabled; // TODO: Remove
 
 private:
+	enum {
+		kFlagZip = 1,
+		kFlagEnabled = 2
+	};
+
 	void loadFromStream(Common::ReadStream *stream);
 
 	MohawkEngine_Riven *_vm;
 
 	uint16 _u0;
 	int16 _u1;
+	uint16 _flags;
 	RivenScriptList _scripts;
 };
 
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 86c58ff..0bc3a86 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -316,12 +316,12 @@ void RivenExternal::checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDome
 	// Let's see if we're all matched up...
 	if (_vm->_vars["adomecombo"] == _sliderState) {
 		// Set the button hotspot to the open dome hotspot
-		_vm->_hotspots[resetSlidersHotspot]->enabled = false;
-		_vm->_hotspots[openDomeHotspot]->enabled = true;
+		_vm->_hotspots[resetSlidersHotspot]->enable(false);
+		_vm->_hotspots[openDomeHotspot]->enable(true);
 	} else {
 		// Set the button hotspot to the reset sliders hotspot
-		_vm->_hotspots[resetSlidersHotspot]->enabled = true;
-		_vm->_hotspots[openDomeHotspot]->enabled = false;
+		_vm->_hotspots[resetSlidersHotspot]->enable(true);
+		_vm->_hotspots[openDomeHotspot]->enable(false);
 	}
 }
 
@@ -450,13 +450,13 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
 
 	// Set hotspots depending on the page
 	if (page == 1) {
-		_vm->_hotspots[1]->enabled = false;
-		_vm->_hotspots[2]->enabled = false;
-		_vm->_hotspots[3]->enabled = true;
+		_vm->_hotspots[1]->enable(false);
+		_vm->_hotspots[2]->enable(false);
+		_vm->_hotspots[3]->enable(true);
 	} else {
-		_vm->_hotspots[1]->enabled = true;
-		_vm->_hotspots[2]->enabled = true;
-		_vm->_hotspots[3]->enabled = false;
+		_vm->_hotspots[1]->enable(true);
+		_vm->_hotspots[2]->enable(true);
+		_vm->_hotspots[3]->enable(false);
 	}
 
 	// Draw the image of the page
@@ -515,13 +515,13 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
 
 	// Set hotspots depending on the page
 	if (page == 1) {
-		_vm->_hotspots[1]->enabled = false;
-		_vm->_hotspots[2]->enabled = false;
-		_vm->_hotspots[3]->enabled = true;
+		_vm->_hotspots[1]->enable(false);
+		_vm->_hotspots[2]->enable(false);
+		_vm->_hotspots[3]->enable(true);
 	} else {
-		_vm->_hotspots[1]->enabled = true;
-		_vm->_hotspots[2]->enabled = true;
-		_vm->_hotspots[3]->enabled = false;
+		_vm->_hotspots[1]->enable(true);
+		_vm->_hotspots[2]->enable(true);
+		_vm->_hotspots[3]->enable(false);
 	}
 
 	// Draw the image of the page
@@ -950,8 +950,8 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
 	if (_vm->_hotspots[9]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_hotspots[3]->enabled = false; // Disable bait hotspot
-		_vm->_hotspots[9]->enabled = true; // Enable baitplate hotspot
+		_vm->_hotspots[3]->enable(false); // Disable bait hotspot
+		_vm->_hotspots[9]->enable(true); // Enable baitplate hotspot
 	}
 }
 
@@ -1009,12 +1009,12 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	if (_vm->_hotspots[9]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_hotspots[3]->enabled = false; // Disable bait hotspot
-		_vm->_hotspots[9]->enabled = true; // Enable baitplate hotspot
+		_vm->_hotspots[3]->enable(false); // Disable bait hotspot
+		_vm->_hotspots[9]->enable(true); // Enable baitplate hotspot
 	} else {
 		_vm->_vars["bbait"] = 0;
-		_vm->_hotspots[3]->enabled = true; // Enable bait hotspot
-		_vm->_hotspots[9]->enabled = false; // Disable baitplate hotspot
+		_vm->_hotspots[3]->enable(true); // Enable bait hotspot
+		_vm->_hotspots[9]->enable(false); // Disable baitplate hotspot
 	}
 }
 
@@ -1995,8 +1995,8 @@ void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) {
 	}
 
 	// Enable the correct hotspots for the movement now
-	_vm->_hotspots[2]->enabled = !_vm->_hotspots[2]->enabled;
-	_vm->_hotspots[3]->enabled = !_vm->_hotspots[3]->enabled;
+	_vm->_hotspots[2]->enable(!_vm->_hotspots[2]->isEnabled());
+	_vm->_hotspots[3]->enable(!_vm->_hotspots[3]->isEnabled());
 
 	// Update the cursor
 	_vm->updateCurrentHotspot();
@@ -2157,9 +2157,9 @@ void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {
 	_vm->_video->playMovieBlockingRiven(1);
 
 	// Set the hotspots into their correct states
-	_vm->_hotspots[2]->enabled = false;
-	_vm->_hotspots[5]->enabled = false;
-	_vm->_hotspots[6]->enabled = true;
+	_vm->_hotspots[2]->enable(false);
+	_vm->_hotspots[5]->enable(false);
+	_vm->_hotspots[6]->enable(true);
 
 	// We now need to draw PLST 1 and refresh, but PLST 1 is
 	// drawn when refreshing anyway, so don't worry about that.
@@ -2484,7 +2484,7 @@ void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {
 
 	// If we have hit the correct 5 buttons in a row, activate the hotspot to open up the
 	// telescope cover.
-	_vm->_hotspots[9]->enabled = (correctDigits == 5);
+	_vm->_hotspots[9]->enable(correctDigits == 5);
 }
 
 // Atrus' Journal and Trap Book are added to inventory
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 35b7195..6e26fc6 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -361,7 +361,7 @@ void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
 		if (_vm->_hotspots[i]->blstID == argv[0]) {
 			debug(2, "Enabling hotspot with BLST ID %d", argv[0]);
-			_vm->_hotspots[i]->enabled = true;
+			_vm->_hotspots[i]->enable(true);
 		}
 	}
 
@@ -374,7 +374,7 @@ void RivenSimpleCommand::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
 		if (_vm->_hotspots[i]->blstID == argv[0]) {
 			debug(2, "Disabling hotspot with BLST ID %d", argv[0]);
-			_vm->_hotspots[i]->enabled = false;
+			_vm->_hotspots[i]->enable(false);
 		}
 	}
 
@@ -603,7 +603,7 @@ void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 		if (argv[0] == index)
 			for (uint16 j = 0; j < _vm->_hotspots.size(); j++)
 				if (_vm->_hotspots[j]->blstID == hotspotID)
-					_vm->_hotspots[j]->enabled = (enabled == 1);
+					_vm->_hotspots[j]->enable(enabled == 1);
 	}
 
 	delete blst;


Commit: 67d9a3c71bb2c201fc4c43f159025fc6ea4517e7
    https://github.com/scummvm/scummvm/commit/67d9a3c71bb2c201fc4c43f159025fc6ea4517e7
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Make the RivenHotspot fields private

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 06c058e..4be7693 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -514,15 +514,16 @@ bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
 
 	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
 		RivenHotspot *hotspot = _vm->_hotspots[i];
-		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, hotspot->index, hotspot->blstID);
+		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, hotspot->getIndex(), hotspot->getBlstId());
 
 		if (hotspot->isEnabled())
 			debugPrintf("enabled");
 		else
 			debugPrintf("disabled");
 
-		debugPrintf(") - (%d, %d, %d, %d)\n", hotspot->rect.left, hotspot->rect.top, hotspot->rect.right, hotspot->rect.bottom);
-		debugPrintf("    Name = %s\n", _vm->getHotspotName(hotspot).c_str());
+		Common::Rect rect = hotspot->getRect();
+		debugPrintf(") - (%d, %d, %d, %d)\n", rect.left, rect.top, rect.right, rect.bottom);
+		debugPrintf("    Name = %s\n", hotspot->getName().c_str());
 	}
 
 	return true;
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 417d271..d09ecfd 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -258,7 +258,7 @@ void MohawkEngine_Riven::handleEvents() {
 				_showHotspots = !_showHotspots;
 				if (_showHotspots) {
 					for (uint16 i = 0; i < _hotspots.size(); i++)
-						_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->isEnabled());
+						_gfx->drawRect(_hotspots[i]->getRect(), _hotspots[i]->isEnabled());
 					needsUpdate = true;
 				} else
 					refreshCard();
@@ -415,7 +415,7 @@ void MohawkEngine_Riven::refreshCard() {
 
 	if (_showHotspots)
 		for (uint16 i = 0; i < _hotspots.size(); i++)
-			_gfx->drawRect(_hotspots[i]->rect, _hotspots[i]->isEnabled());
+			_gfx->drawRect(_hotspots[i]->getRect(), _hotspots[i]->isEnabled());
 
 	// Now we need to redraw the cursor if necessary and handle mouse over scripts
 	updateCurrentHotspot();
@@ -449,7 +449,7 @@ void MohawkEngine_Riven::updateZipMode() {
 		if (_hotspots[i]->isZip()) {
 			if (_vars["azip"] != 0) {
 				// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
-				Common::String hotspotName = getName(HotspotNames, _hotspots[i]->name_resource);
+				Common::String hotspotName = _hotspots[i]->getName();
 
 				bool foundMatch = false;
 
@@ -470,14 +470,14 @@ void MohawkEngine_Riven::updateZipMode() {
 void MohawkEngine_Riven::checkHotspotChange() {
 	RivenHotspot *hotspot = nullptr;
 	for (uint16 i = 0; i < _hotspots.size(); i++)
-		if (_hotspots[i]->isEnabled() && _hotspots[i]->rect.contains(_eventMan->getMousePos())) {
+		if (_hotspots[i]->isEnabled() && _hotspots[i]->containsPoint(_eventMan->getMousePos())) {
 			hotspot = _hotspots[i];
 		}
 
 	if (hotspot) {
 		if (_curHotspot != hotspot) {
 			_curHotspot = hotspot;
-			_cursor->setCursor(hotspot->mouse_cursor);
+			_cursor->setCursor(hotspot->getMouseCursor());
 			_system->updateScreen();
 		}
 	} else {
@@ -492,13 +492,6 @@ void MohawkEngine_Riven::updateCurrentHotspot() {
 	checkHotspotChange();
 }
 
-Common::String MohawkEngine_Riven::getHotspotName(const RivenHotspot *hotspot) {
-	if (hotspot->name_resource < 0)
-		return Common::String();
-
-	return getName(HotspotNames, hotspot->name_resource);
-}
-
 void MohawkEngine_Riven::checkInventoryClick() {
 	Common::Point mousePos = _eventMan->getMousePos();
 
@@ -927,7 +920,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 		return;
 
 	// Only set the sunners variable on the forward hotspot
-	if ((rmapCode == 0x79bd && _curHotspot->index != 2) || (rmapCode == 0x7beb && _curHotspot->index != 3))
+	if ((rmapCode == 0x79bd && _curHotspot->getIndex() != 2) || (rmapCode == 0x7beb && _curHotspot->getIndex() != 3))
 		return;
 
 	// If the alert video is no longer playing, we have nothing left to do
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index e4f0803..b2e3bf0 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -170,7 +170,6 @@ public:
 	RivenHotspot *_curHotspot;
 	Common::Array<ZipMode> _zipModeData;
 	RivenHotspot *getCurHotspot() const { return _curHotspot; }
-	Common::String getHotspotName(const RivenHotspot *hotspot);
 	void updateCurrentHotspot();
 	void addZipVisitedCard(uint16 cardId, uint16 cardNameId);
 
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 32eb63c..4193eea 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -204,8 +204,8 @@ RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 void RivenHotspot::loadFromStream(Common::ReadStream *stream) {
 	_flags = kFlagEnabled;
 
-	blstID = stream->readUint16BE();
-	name_resource = stream->readSint16BE();
+	_blstID = stream->readUint16BE();
+	_nameResource = stream->readSint16BE();
 
 	int16 left = stream->readSint16BE();
 	int16 top = stream->readSint16BE();
@@ -221,11 +221,11 @@ void RivenHotspot::loadFromStream(Common::ReadStream *stream) {
 		enable(false);
 	}
 
-	rect = Common::Rect(left, top, right, bottom);
+	_rect = Common::Rect(left, top, right, bottom);
 
 	_u0 = stream->readUint16BE();
-	mouse_cursor = stream->readUint16BE();
-	index = stream->readUint16BE();
+	_mouseCursor = stream->readUint16BE();
+	_index = stream->readUint16BE();
 	_u1 = stream->readSint16BE();
 	_flags |= stream->readUint16BE();
 
@@ -258,4 +258,35 @@ bool RivenHotspot::isZip() const {
 	return (_flags & kFlagZip) != 0;
 }
 
+Common::Rect RivenHotspot::getRect() const {
+	return _rect;
+}
+
+bool RivenHotspot::containsPoint(const Common::Point &point) const {
+	return _rect.contains(point);
+}
+
+uint16 RivenHotspot::getMouseCursor() const {
+	return _mouseCursor;
+}
+
+Common::String RivenHotspot::getName() const {
+	if (_nameResource < 0)
+		return Common::String();
+
+	return _vm->getName(HotspotNames, _nameResource);
+}
+
+uint16 RivenHotspot::getIndex() const {
+	return _index;
+}
+
+uint16 RivenHotspot::getBlstId() const {
+	return _blstID;
+}
+
+void RivenHotspot::setRect(const Common::Rect &rect) {
+	_rect = rect;
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 935ac05..e7745fc 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -117,11 +117,26 @@ public:
 	/** Is the hotspot's purpose to zip to another card */
 	bool isZip() const;
 
-	uint16 blstID;
-	int16 name_resource;
-	uint16 index;
-	Common::Rect rect;
-	uint16 mouse_cursor;
+	/** Get the hotspot'a rect in Card coordinates */
+	Common::Rect getRect() const;
+
+	/** Does the hotspot contain the specified point? */
+	bool containsPoint(const Common::Point &point) const;
+
+	/** Override the hotspot's default rect */
+	void setRect(const Common::Rect &rect);
+
+	/** Get the default mouse cursor id to be used when hovering the hostpot */
+	uint16 getMouseCursor() const;
+
+	/** Get the hotspot's name from the current stack's name list */
+	Common::String getName() const;
+
+	/** Get the hotspot's index in the view */
+	uint16 getIndex() const;
+
+	/** Get the hotspot's enable list id */
+	uint16 getBlstId() const;
 
 private:
 	enum {
@@ -133,7 +148,12 @@ private:
 
 	MohawkEngine_Riven *_vm;
 
+	uint16 _blstID;
+	int16 _nameResource;
+	Common::Rect _rect;
 	uint16 _u0;
+	uint16 _mouseCursor;
+	uint16 _index;
 	int16 _u1;
 	uint16 _flags;
 	RivenScriptList _scripts;
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 0bc3a86..8c07118 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -328,7 +328,7 @@ void RivenExternal::checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDome
 void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
 	// Set the cursor based on _sliderState and what hotspot we're over
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		if (_vm->_hotspots[i + startHotspot]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+		if (_vm->_hotspots[i + startHotspot]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			if (_sliderState & (1 << (24 - i)))
 				_vm->_cursor->setCursor(kRivenOpenHandCursor);
 			else
@@ -343,7 +343,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 	int16 foundSlider = -1;
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		if (_vm->_hotspots[i + startHotspot]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+		if (_vm->_hotspots[i + startHotspot]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			// If the slider is not at this hotspot, we can't do anything else
 			if (!(_sliderState & (1 << (24 - i))))
 				return;
@@ -367,7 +367,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 		while (_vm->_system->getEventManager()->pollEvent(event)) {
 			switch (event.type) {
 			case Common::EVENT_MOUSEMOVE:
-				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot + 1]->rect.contains(event.mouse)) {
+				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot + 1]->containsPoint(event.mouse)) {
 					// We've moved the slider right one space
 					_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
 					foundSlider++;
@@ -376,7 +376,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 					// Now play a click sound and redraw
 					_vm->_sound->playSound(soundId);
 					drawDomeSliders(startHotspot);
-				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot - 1]->rect.contains(event.mouse)) {
+				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot - 1]->containsPoint(event.mouse)) {
 					// We've moved the slider left one space
 					_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
 					foundSlider--;
@@ -414,10 +414,10 @@ void RivenExternal::drawDomeSliders(uint16 startHotspot) {
 	uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		Common::Rect srcRect = _vm->_hotspots[startHotspot + i]->rect;
+		Common::Rect srcRect = _vm->_hotspots[startHotspot + i]->getRect();
 		srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
 
-		Common::Rect dstRect = _vm->_hotspots[startHotspot + i]->rect;
+		Common::Rect dstRect = _vm->_hotspots[startHotspot + i]->getRect();
 
 		if (_sliderState & (1 << (24 - i)))
 			_vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect);
@@ -947,7 +947,7 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();
 
 	// Set the bait if we put it on the plate
-	if (_vm->_hotspots[9]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+	if (_vm->_hotspots[9]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
 		_vm->_hotspots[3]->enable(false); // Disable bait hotspot
@@ -1006,7 +1006,7 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();
 
 	// Set the bait if we put it on the plate, remove otherwise
-	if (_vm->_hotspots[9]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+	if (_vm->_hotspots[9]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
 		_vm->_hotspots[3]->enable(false); // Disable bait hotspot
@@ -1194,8 +1194,8 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
 
 	// Get our mouse position and adjust it to the beginning of the hotspot
 	Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
-	mousePos.x -= _vm->_hotspots[3]->rect.left;
-	mousePos.y -= _vm->_hotspots[3]->rect.top;
+	mousePos.x -= _vm->_hotspots[3]->getRect().left;
+	mousePos.y -= _vm->_hotspots[3]->getRect().top;
 
 	// And now adjust it to which box we hit
 	mousePos.x /= 10;
@@ -1342,7 +1342,7 @@ void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) {
 	// Calculate how much we're moving
 	static const uint16 hotspotPositions[] = { 2, 1, 5, 4, 3 };
 	uint32 &curPos = _vm->_vars["grviewpos"];
-	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->index - 2];
+	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->getIndex() - 2];
 
 	// Now play the movie
 	VideoHandle handle = _vm->_video->playMovieRiven(1);
@@ -1411,7 +1411,7 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	// Calculate how much we're moving
 	static const uint16 hotspotPositions[] = { 1, 5, 4, 2, 0, 0, 3 };
 	uint32 &curPos = _vm->_vars["glviewpos"];
-	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->index - 2];
+	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->getIndex() - 2];
 
 	// Now play the movie
 	VideoHandle handle = _vm->_video->playMovieRiven(1);
@@ -2048,7 +2048,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 	// Track down our hotspot
 	// Of course, they're not in any sane order...
 	static const uint16 hotspotMap[] = { 1, 3, 2, 0 };
-	Common::Rect hotspotRect = _vm->_hotspots[hotspotMap[argv[3] - 1]]->rect;
+	Common::Rect hotspotRect = _vm->_hotspots[hotspotMap[argv[3] - 1]]->getRect();
 
 	debug(0, "xbookclick:");
 	debug(0, "\tVideo Code = %d", argv[0]);
@@ -2617,9 +2617,9 @@ void RivenExternal::setMarbleHotspots() {
 		uint32 &marblePos = _vm->_vars[s_marbleNames[i]];
 
 		if (marblePos == 0) // In the receptacle
-			_vm->_hotspots[i + 3]->rect = _marbleBaseHotspots[i];
+			_vm->_hotspots[i + 3]->setRect(_marbleBaseHotspots[i]);
 		else                 // On the grid
-			_vm->_hotspots[i + 3]->rect = generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos));
+			_vm->_hotspots[i + 3]->setRect(generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)));
 	}
 }
 
@@ -2627,7 +2627,7 @@ void RivenExternal::xt7800_setup(uint16 argc, uint16 *argv) {
 	// First, let's store the base receptacle hotspots for the marbles
 	if (_marbleBaseHotspots.empty())
 		for (uint16 i = 0; i < kMarbleCount; i++)
-			_marbleBaseHotspots.push_back(_vm->_hotspots[i + 3]->rect);
+			_marbleBaseHotspots.push_back(_vm->_hotspots[i + 3]->getRect());
 
 	// Move the marble hotspots based on their position variables
 	setMarbleHotspots();
@@ -2640,7 +2640,7 @@ void RivenExternal::drawMarbles() {
 		if (_vm->_vars["themarble"] - 1 == i)
 			continue;
 
-		Common::Rect rect = _vm->_hotspots[i + 3]->rect;
+		Common::Rect rect = _vm->_hotspots[i + 3]->getRect();
 		// Trim the rect down a bit
 		rect.left += 3;
 		rect.top += 3;
@@ -2663,7 +2663,7 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
 	marble = 0;
 
 	for (uint32 i = 0; i < kMarbleCount; i++)
-		if (_vm->_hotspots[i + 3]->rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+		if (_vm->_hotspots[i + 3]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			marble = i + 1;
 			break;
 		}
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 6e26fc6..c299f86 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -359,7 +359,7 @@ void RivenSimpleCommand::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) {
 // Command 9: enable hotspot (blst_id)
 void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
-		if (_vm->_hotspots[i]->blstID == argv[0]) {
+		if (_vm->_hotspots[i]->getBlstId() == argv[0]) {
 			debug(2, "Enabling hotspot with BLST ID %d", argv[0]);
 			_vm->_hotspots[i]->enable(true);
 		}
@@ -372,7 +372,7 @@ void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 // Command 10: disable hotspot (blst_id)
 void RivenSimpleCommand::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
-		if (_vm->_hotspots[i]->blstID == argv[0]) {
+		if (_vm->_hotspots[i]->getBlstId() == argv[0]) {
 			debug(2, "Disabling hotspot with BLST ID %d", argv[0]);
 			_vm->_hotspots[i]->enable(false);
 		}
@@ -602,7 +602,7 @@ void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 
 		if (argv[0] == index)
 			for (uint16 j = 0; j < _vm->_hotspots.size(); j++)
-				if (_vm->_hotspots[j]->blstID == hotspotID)
+				if (_vm->_hotspots[j]->getBlstId() == hotspotID)
 					_vm->_hotspots[j]->enable(enabled == 1);
 	}
 
@@ -635,8 +635,10 @@ void RivenSimpleCommand::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 45: do zip mode
 void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
+	assert(_vm->getCurHotspot());
+
 	// Check the ZIPS records to see if we have a match to the hotspot name
-	Common::String hotspotName = _vm->getHotspotName(_vm->getCurHotspot());
+	Common::String hotspotName = _vm->getCurHotspot()->getName();
 
 	for (uint16 i = 0; i < _vm->_zipModeData.size(); i++)
 		if (_vm->_zipModeData[i].name == hotspotName) {


Commit: 099b3b3d8ff4729b5556f07f0e4476555ce7659c
    https://github.com/scummvm/scummvm/commit/099b3b3d8ff4729b5556f07f0e4476555ce7659c
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the hotspot list to RivenCard

Also replace all hardcoded accesses to the hotspot array with hotspot queries.

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_external.h
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 4be7693..41dd535 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -510,10 +510,12 @@ bool RivenConsole::Cmd_ChangeStack(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
-	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), _vm->_hotspots.size());
+	Common::Array<RivenHotspot *> hotspots = _vm->_card->getHotspots();
 
-	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
-		RivenHotspot *hotspot = _vm->_hotspots[i];
+	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), hotspots.size());
+
+	for (uint16 i = 0; i < hotspots.size(); i++) {
+		RivenHotspot *hotspot = hotspots[i];
 		debugPrintf("Hotspot %d, index %d, BLST ID %d (", i, hotspot->getIndex(), hotspot->getBlstId());
 
 		if (hotspot->isEnabled())
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index d09ecfd..8e30080 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -103,9 +103,6 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
 	delete _scriptMan;
 	delete _optionsDialog;
 	delete _rnd;
-	for (uint i = 0; i < _hotspots.size(); i++) {
-		delete _hotspots[i];
-	}
 	delete g_atrusJournalRect1;
 	delete g_atrusJournalRect2;
 	delete g_cathJournalRect2;
@@ -257,8 +254,7 @@ void MohawkEngine_Riven::handleEvents() {
 			case Common::KEYCODE_F4:
 				_showHotspots = !_showHotspots;
 				if (_showHotspots) {
-					for (uint16 i = 0; i < _hotspots.size(); i++)
-						_gfx->drawRect(_hotspots[i]->getRect(), _hotspots[i]->isEnabled());
+					_card->drawHotspotRects();
 					needsUpdate = true;
 				} else
 					refreshCard();
@@ -267,7 +263,7 @@ void MohawkEngine_Riven::handleEvents() {
 				runDialog(*_optionsDialog);
 				if (_optionsDialog->getLoadSlot() >= 0)
 					loadGameState(_optionsDialog->getLoadSlot());
-				updateZipMode();
+				_card->initializeZipMode();
 				break;
 			case Common::KEYCODE_r:
 				// Return to the main menu in the demo on ctrl+r
@@ -406,16 +402,13 @@ void MohawkEngine_Riven::refreshCard() {
 	// Clear any timer still floating around
 	removeTimer();
 
-	loadHotspots(_card->getId());
-
 	_gfx->clearWaterEffects();
 	_video->stopVideos();
 
 	_card->open();
 
 	if (_showHotspots)
-		for (uint16 i = 0; i < _hotspots.size(); i++)
-			_gfx->drawRect(_hotspots[i]->getRect(), _hotspots[i]->isEnabled());
+		_card->drawHotspotRects();
 
 	// Now we need to redraw the cursor if necessary and handle mouse over scripts
 	updateCurrentHotspot();
@@ -424,55 +417,9 @@ void MohawkEngine_Riven::refreshCard() {
 	installCardTimer();
 }
 
-void MohawkEngine_Riven::loadHotspots(uint16 id) {
-	for (uint i = 0; i < _hotspots.size(); i++) {
-		delete _hotspots[i];
-	}
-
-	Common::SeekableReadStream *inStream = getResource(ID_HSPT, id);
-
-	uint16 hotspotCount = inStream->readUint16BE();
-	_hotspots.resize(hotspotCount);
-
-	for (uint16 i = 0; i < hotspotCount; i++) {
-		_hotspots[i] = new RivenHotspot(this, inStream);
-	}
-
-	delete inStream;
-	updateZipMode();
-}
-
-void MohawkEngine_Riven::updateZipMode() {
-	// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
-
-	for (uint32 i = 0; i < _hotspots.size(); i++) {
-		if (_hotspots[i]->isZip()) {
-			if (_vars["azip"] != 0) {
-				// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
-				Common::String hotspotName = _hotspots[i]->getName();
-
-				bool foundMatch = false;
-
-				if (!hotspotName.empty())
-					for (uint16 j = 0; j < _zipModeData.size(); j++)
-						if (_zipModeData[j].name == hotspotName) {
-							foundMatch = true;
-							break;
-						}
-
-				_hotspots[i]->enable(foundMatch);
-			} else // Disable the hotspot if zip mode is disabled
-				_hotspots[i]->enable(false);
-		}
-	}
-}
-
 void MohawkEngine_Riven::checkHotspotChange() {
-	RivenHotspot *hotspot = nullptr;
-	for (uint16 i = 0; i < _hotspots.size(); i++)
-		if (_hotspots[i]->isEnabled() && _hotspots[i]->containsPoint(_eventMan->getMousePos())) {
-			hotspot = _hotspots[i];
-		}
+	Common::Point mousePos = _eventMan->getMousePos();
+	RivenHotspot *hotspot = _card->getHotspotContainingPoint(mousePos);
 
 	if (hotspot) {
 		if (_curHotspot != hotspot) {
@@ -596,6 +543,38 @@ Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) {
 	return name;
 }
 
+int16 MohawkEngine_Riven::getIdFromName(uint16 nameResource, const Common::String &name) {
+	//TODO: Use proper data structures
+
+	Common::SeekableReadStream *nameStream = getResource(ID_NAME, nameResource);
+	uint16 fieldCount = nameStream->readUint16BE();
+	uint16 *stringOffsets = new uint16[fieldCount];
+
+	for (uint16 i = 0; i < fieldCount; i++)
+		stringOffsets[i] = nameStream->readUint16BE();
+	for (uint16 i = 0; i < fieldCount; i++)
+		nameStream->readUint16BE();	// Skip unknown values
+
+	for (uint16 i = 0; i < fieldCount; i++) {
+		nameStream->seek(stringOffsets[i], SEEK_CUR);
+
+		Common::String readName;
+		char c = (char)nameStream->readByte();
+		while (c) {
+			readName += c;
+			c = (char)nameStream->readByte();
+		}
+
+		if (readName.equalsIgnoreCase(name)) {
+			return i;
+		}
+	}
+
+	delete nameStream;
+	delete[] stringOffsets;
+	return -1;
+}
+
 uint16 MohawkEngine_Riven::matchRMAPToCard(uint32 rmapCode) {
 	uint16 index = 0;
 	Common::SeekableReadStream *rmapStream = getResource(ID_RMAP, 1);
@@ -920,7 +899,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 		return;
 
 	// Only set the sunners variable on the forward hotspot
-	if ((rmapCode == 0x79bd && _curHotspot->getIndex() != 2) || (rmapCode == 0x7beb && _curHotspot->getIndex() != 3))
+	if (_curHotspot->getBlstId() != 3)
 		return;
 
 	// If the alert video is no longer playing, we have nothing left to do
@@ -942,6 +921,19 @@ void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
 		_zipModeData.push_back(zip);
 }
 
+bool MohawkEngine_Riven::isZipVisitedCard(const Common::String &hotspotName) const {
+	bool foundMatch = false;
+
+	if (!hotspotName.empty())
+		for (uint16 j = 0; j < _zipModeData.size(); j++)
+			if (_zipModeData[j].name == hotspotName) {
+				foundMatch = true;
+				break;
+			}
+
+	return foundMatch;
+}
+
 bool ZipMode::operator== (const ZipMode &z) const {
 	return z.name == name && z.id == id;
 }
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index b2e3bf0..4cff24e 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -135,10 +135,8 @@ private:
 	void handleEvents();
 
 	// Hotspot related functions and variables
-	void loadHotspots(uint16);
 	void checkInventoryClick();
 	bool _showHotspots;
-	void updateZipMode();
 	void checkHotspotChange();
 
 	// Variables
@@ -159,6 +157,7 @@ public:
 	void changeToStack(uint16);
 	void refreshCard();
 	Common::String getName(uint16 nameResource, uint16 nameID);
+	int16 getIdFromName(uint16 nameResource, const Common::String &name);
 	Common::String getStackName(uint16 stack) const;
 	RivenCard *getCurCard() const { return _card; }
 	uint16 getCurStack() const { return _curStack; }
@@ -166,12 +165,12 @@ public:
 	uint32 getCurCardRMAP();
 
 	// Hotspot functions/variables
-	Common::Array<RivenHotspot *> _hotspots;
 	RivenHotspot *_curHotspot;
 	Common::Array<ZipMode> _zipModeData;
 	RivenHotspot *getCurHotspot() const { return _curHotspot; }
 	void updateCurrentHotspot();
 	void addZipVisitedCard(uint16 cardId, uint16 cardNameId);
+	bool isZipVisitedCard(const Common::String &hotspotName) const;
 
 	// Variables
 	RivenVariableMap _vars;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 4193eea..b3ec065 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -33,12 +33,15 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	_vm(vm),
 	_id(id) {
 	loadCardResource(id);
+	loadHotspots(id);
 	loadCardPictureList(id);
 	loadCardSoundList(id);
 }
 
 RivenCard::~RivenCard() {
-
+	for (uint i = 0; i < _hotspots.size(); i++) {
+		delete _hotspots[i];
+	}
 }
 
 void RivenCard::loadCardResource(uint16 id) {
@@ -69,6 +72,20 @@ void RivenCard::initializeZipMode() {
 	if (_zipModePlace) {
 		_vm->addZipVisitedCard(_id, _name);
 	}
+
+	// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
+	for (uint32 i = 0; i < _hotspots.size(); i++) {
+		if (_hotspots[i]->isZip()) {
+			if (_vm->_vars["azip"] != 0) {
+				// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
+				Common::String hotspotName = _hotspots[i]->getName();
+				bool visited = _vm->isZipVisitedCard(hotspotName);
+
+				_hotspots[i]->enable(visited);
+			} else // Disable the hotspot if zip mode is disabled
+				_hotspots[i]->enable(false);
+		}
+	}
 }
 
 void RivenCard::runScript(uint16 scriptType) {
@@ -196,6 +213,60 @@ SLSTRecord RivenCard::getSound(uint16 index) const {
 	error("Could not find sound %d in card %d", index, _id);
 }
 
+void RivenCard::loadHotspots(uint16 id) {
+	Common::SeekableReadStream *inStream = _vm->getResource(ID_HSPT, id);
+
+	uint16 hotspotCount = inStream->readUint16BE();
+	_hotspots.resize(hotspotCount);
+
+	for (uint16 i = 0; i < hotspotCount; i++) {
+		_hotspots[i] = new RivenHotspot(_vm, inStream);
+	}
+
+	delete inStream;
+}
+
+void RivenCard::drawHotspotRects() {
+	for (uint16 i = 0; i < _hotspots.size(); i++)
+		_vm->_gfx->drawRect(_hotspots[i]->getRect(), _hotspots[i]->isEnabled());
+}
+
+RivenHotspot *RivenCard::getHotspotContainingPoint(const Common::Point &point) const {
+	RivenHotspot *hotspot = nullptr;
+	for (uint16 i = 0; i < _hotspots.size(); i++)
+		if (_hotspots[i]->isEnabled() && _hotspots[i]->containsPoint(point)) {
+			hotspot = _hotspots[i];
+		}
+
+	return hotspot;
+}
+
+Common::Array<RivenHotspot *> RivenCard::getHotspots() const {
+	return _hotspots;
+}
+
+RivenHotspot *RivenCard::getHotspotByName(const Common::String &name) const {
+	int16 nameId = _vm->getIdFromName(HotspotNames, name);
+
+	for (uint i = 0; i < _hotspots.size(); i++) {
+		if (_hotspots[i]->getNameId() == nameId) {
+			return _hotspots[i];
+		}
+	}
+
+	error("Card %d does not have an hotspot named %s", _id, name.c_str());
+}
+
+RivenHotspot *RivenCard::getHotspotByBlstId(const uint16 blstId) const {
+	for (uint i = 0; i < _hotspots.size(); i++) {
+		if (_hotspots[i]->getBlstId() == blstId) {
+			return _hotspots[i];
+		}
+	}
+
+	return nullptr;
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
@@ -289,4 +360,8 @@ void RivenHotspot::setRect(const Common::Rect &rect) {
 	_rect = rect;
 }
 
+int16 RivenHotspot::getNameId() const {
+	return _nameResource;
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index e7745fc..10b7727 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -31,6 +31,8 @@
 
 namespace Mohawk {
 
+class RivenHotspot;
+
 /**
  * A game view
  *
@@ -74,12 +76,30 @@ public:
 	/** Get the card's sound description with the specified index */
 	SLSTRecord getSound(uint16 index) const;
 
+	/** Draw borders for all the hotspots in the card */
+	void drawHotspotRects();
+
+	/** Enable the zip hotspots if they match to already visited locations */
+	void initializeZipMode();
+
+	/** Get the hotspot containing the specified point */
+	RivenHotspot *getHotspotContainingPoint(const Common::Point &point) const;
+
+	/** Get the hotspot with the specified name */
+	RivenHotspot *getHotspotByName(const Common::String &name) const;
+
+	/** Get the hotspot with the specified BLST id */
+	RivenHotspot *getHotspotByBlstId(const uint16 blstId) const;
+
+	/** Get all the hotspots in the card. To be used for debugging features only */
+	Common::Array<RivenHotspot *> getHotspots() const;
+
 private:
 	void loadCardResource(uint16 id);
+	void loadHotspots(uint16 id);
 	void loadCardPictureList(uint16 id);
 	void loadCardSoundList(uint16 id);
 
-	void initializeZipMode();
 	void defaultLoadScript();
 
 	MohawkEngine_Riven *_vm;
@@ -90,6 +110,8 @@ private:
 	uint16 _zipModePlace;
 	RivenScriptList _scripts;
 
+	Common::Array<RivenHotspot *> _hotspots;
+
 	// Resource lists
 	Common::Array<Picture> _pictureList;
 	Common::Array<SLSTRecord> _soundList;
@@ -132,7 +154,10 @@ public:
 	/** Get the hotspot's name from the current stack's name list */
 	Common::String getName() const;
 
-	/** Get the hotspot's index in the view */
+	/** Get the hotspot's name id */
+	int16 getNameId() const;
+
+	/** Get the hotspot's order in the view */
 	uint16 getIndex() const;
 
 	/** Get the hotspot's enable list id */
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 8c07118..88718db 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -312,23 +312,27 @@ void RivenExternal::resetDomeSliders(uint16 soundId, uint16 startHotspot) {
 	assert(_sliderState == kDomeSliderDefaultState);
 }
 
-void RivenExternal::checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDomeHotspot) {
+void RivenExternal::checkDomeSliders() {
+	RivenHotspot *resetSlidersHotspot = _vm->getCurCard()->getHotspotByName("ResetSliders");
+	RivenHotspot *openDomeHotspot = _vm->getCurCard()->getHotspotByName("OpenDome");
+
 	// Let's see if we're all matched up...
 	if (_vm->_vars["adomecombo"] == _sliderState) {
 		// Set the button hotspot to the open dome hotspot
-		_vm->_hotspots[resetSlidersHotspot]->enable(false);
-		_vm->_hotspots[openDomeHotspot]->enable(true);
+		resetSlidersHotspot->enable(false);
+		openDomeHotspot->enable(true);
 	} else {
 		// Set the button hotspot to the reset sliders hotspot
-		_vm->_hotspots[resetSlidersHotspot]->enable(true);
-		_vm->_hotspots[openDomeHotspot]->enable(false);
+		resetSlidersHotspot->enable(true);
+		openDomeHotspot->enable(false);
 	}
 }
 
 void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
 	// Set the cursor based on _sliderState and what hotspot we're over
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		if (_vm->_hotspots[i + startHotspot]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+		RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + i);
+		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			if (_sliderState & (1 << (24 - i)))
 				_vm->_cursor->setCursor(kRivenOpenHandCursor);
 			else
@@ -339,11 +343,12 @@ void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
 	}
 }
 
-void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot) {
+void RivenExternal::dragDomeSlider(uint16 soundId, uint16 startHotspot) {
 	int16 foundSlider = -1;
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		if (_vm->_hotspots[i + startHotspot]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+		RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + i);
+		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			// If the slider is not at this hotspot, we can't do anything else
 			if (!(_sliderState & (1 << (24 - i))))
 				return;
@@ -367,24 +372,30 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 		while (_vm->_system->getEventManager()->pollEvent(event)) {
 			switch (event.type) {
 			case Common::EVENT_MOUSEMOVE:
-				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot + 1]->containsPoint(event.mouse)) {
-					// We've moved the slider right one space
-					_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
-					foundSlider++;
-					_sliderState |= 1 << (24 - foundSlider);
-
-					// Now play a click sound and redraw
-					_vm->_sound->playSound(soundId);
-					drawDomeSliders(startHotspot);
-				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot - 1]->containsPoint(event.mouse)) {
-					// We've moved the slider left one space
-					_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
-					foundSlider--;
-					_sliderState |= 1 << (24 - foundSlider);
-
-					// Now play a click sound and redraw
-					_vm->_sound->playSound(soundId);
-					drawDomeSliders(startHotspot);
+				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider)))) {
+					RivenHotspot *nextHotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + foundSlider + 1);
+					if (nextHotspot->containsPoint(event.mouse)) {
+						// We've moved the slider right one space
+						_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
+						foundSlider++;
+						_sliderState |= 1 << (24 - foundSlider);
+
+						// Now play a click sound and redraw
+						_vm->_sound->playSound(soundId);
+						drawDomeSliders(startHotspot);
+					}
+				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider)))) {
+					RivenHotspot *previousHotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + foundSlider - 1);
+					if (previousHotspot->containsPoint(event.mouse)) {
+						// We've moved the slider left one space
+						_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
+						foundSlider--;
+						_sliderState |= 1 << (24 - foundSlider);
+
+						// Now play a click sound and redraw
+						_vm->_sound->playSound(soundId);
+						drawDomeSliders(startHotspot);
+					}
 				} else
 					_vm->_system->updateScreen(); // A normal update for the cursor
 				break;
@@ -399,7 +410,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, u
 	}
 
 	// Check to see if we have the right combination
-	checkDomeSliders(resetSlidersHotspot, openDomeHotspot);
+	checkDomeSliders();
 }
 
 void RivenExternal::drawDomeSliders(uint16 startHotspot) {
@@ -414,10 +425,12 @@ void RivenExternal::drawDomeSliders(uint16 startHotspot) {
 	uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		Common::Rect srcRect = _vm->_hotspots[startHotspot + i]->getRect();
+		RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + i);
+
+		Common::Rect srcRect = hotspot->getRect();
 		srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
 
-		Common::Rect dstRect = _vm->_hotspots[startHotspot + i]->getRect();
+		Common::Rect dstRect = hotspot->getRect();
 
 		if (_sliderState & (1 << (24 - i)))
 			_vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect);
@@ -449,14 +462,17 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
 	uint32 &page = _vm->_vars["aatruspage"];
 
 	// Set hotspots depending on the page
+	RivenHotspot *openBook = _vm->_card->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->_card->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->_card->getHotspotByName("prevpage");
 	if (page == 1) {
-		_vm->_hotspots[1]->enable(false);
-		_vm->_hotspots[2]->enable(false);
-		_vm->_hotspots[3]->enable(true);
+		prevPage->enable(false);
+		nextPage->enable(false);
+		openBook->enable(true);
 	} else {
-		_vm->_hotspots[1]->enable(true);
-		_vm->_hotspots[2]->enable(true);
-		_vm->_hotspots[3]->enable(false);
+		prevPage->enable(true);
+		nextPage->enable(true);
+		openBook->enable(false);
 	}
 
 	// Draw the image of the page
@@ -514,14 +530,17 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
 	uint32 page = _vm->_vars["acathpage"];
 
 	// Set hotspots depending on the page
+	RivenHotspot *openBook = _vm->_card->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->_card->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->_card->getHotspotByName("prevpage");
 	if (page == 1) {
-		_vm->_hotspots[1]->enable(false);
-		_vm->_hotspots[2]->enable(false);
-		_vm->_hotspots[3]->enable(true);
+		prevPage->enable(false);
+		nextPage->enable(false);
+		openBook->enable(true);
 	} else {
-		_vm->_hotspots[1]->enable(true);
-		_vm->_hotspots[2]->enable(true);
-		_vm->_hotspots[3]->enable(false);
+		prevPage->enable(true);
+		nextPage->enable(true);
+		openBook->enable(false);
 	}
 
 	// Draw the image of the page
@@ -946,12 +965,16 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
 	_vm->_cursor->setCursor(kRivenMainCursor);
 	_vm->_system->updateScreen();
 
+	RivenHotspot *bait = _vm->getCurCard()->getHotspotByBlstId(9);
+	RivenHotspot *baitPlate = _vm->getCurCard()->getHotspotByBlstId(16);
+
 	// Set the bait if we put it on the plate
-	if (_vm->_hotspots[9]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_hotspots[3]->enable(false); // Disable bait hotspot
-		_vm->_hotspots[9]->enable(true); // Enable baitplate hotspot
+
+		bait->enable(false); // Disable bait hotspot
+		baitPlate->enable(true); // Enable baitplate hotspot
 	}
 }
 
@@ -1005,33 +1028,36 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	_vm->_cursor->setCursor(kRivenMainCursor);
 	_vm->_system->updateScreen();
 
+	RivenHotspot *bait = _vm->getCurCard()->getHotspotByBlstId(9);
+	RivenHotspot *baitPlate = _vm->getCurCard()->getHotspotByBlstId(16);
+
 	// Set the bait if we put it on the plate, remove otherwise
-	if (_vm->_hotspots[9]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
 		_vm->getCurCard()->drawPicture(4);
-		_vm->_hotspots[3]->enable(false); // Disable bait hotspot
-		_vm->_hotspots[9]->enable(true); // Enable baitplate hotspot
+		bait->enable(false); // Disable bait hotspot
+		baitPlate->enable(true); // Enable baitplate hotspot
 	} else {
 		_vm->_vars["bbait"] = 0;
-		_vm->_hotspots[3]->enable(true); // Enable bait hotspot
-		_vm->_hotspots[9]->enable(false); // Disable baitplate hotspot
+		bait->enable(true); // Enable bait hotspot
+		baitPlate->enable(false); // Disable baitplate hotspot
 	}
 }
 
 void RivenExternal::xbisland190_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders(27, 28);
+	checkDomeSliders();
 }
 
 void RivenExternal::xbisland190_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(41, 2);
+	resetDomeSliders(41, 9);
 }
 
 void RivenExternal::xbisland190_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(41, 27, 28, 2);
+	dragDomeSlider(41, 9);
 }
 
 void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(2);
+	checkSliderCursorChange(9);
 }
 
 void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) {
@@ -1192,10 +1218,12 @@ void RivenExternal::xgrotatepins(uint16 argc, uint16 *argv) {
 void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
 	// Handle a click on a section of an island
 
+	RivenHotspot *panel = _vm->getCurCard()->getHotspotByBlstId(13);
+
 	// Get our mouse position and adjust it to the beginning of the hotspot
 	Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
-	mousePos.x -= _vm->_hotspots[3]->getRect().left;
-	mousePos.y -= _vm->_hotspots[3]->getRect().top;
+	mousePos.x -= panel->getRect().left;
+	mousePos.y -= panel->getRect().top;
 
 	// And now adjust it to which box we hit
 	mousePos.x /= 10;
@@ -1280,19 +1308,19 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
 }
 
 void RivenExternal::xgisland25_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders(28, 29);
+	checkDomeSliders();
 }
 
 void RivenExternal::xgisland25_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(16, 2);
+	resetDomeSliders(16, 11);
 }
 
 void RivenExternal::xgisland25_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(16, 28, 29, 2);
+	dragDomeSlider(16, 11);
 }
 
 void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(2);
+	checkSliderCursorChange(11);
 }
 
 void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) {
@@ -1340,9 +1368,11 @@ void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) {
 	}
 
 	// Calculate how much we're moving
-	static const uint16 hotspotPositions[] = { 2, 1, 5, 4, 3 };
+	Common::String buttonName = _vm->_curHotspot->getName();
+	uint32 buttonPos = buttonName.lastChar() - '0';
+
 	uint32 &curPos = _vm->_vars["grviewpos"];
-	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->getIndex() - 2];
+	uint32 newPos = curPos + buttonPos;
 
 	// Now play the movie
 	VideoHandle handle = _vm->_video->playMovieRiven(1);
@@ -1409,9 +1439,11 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	// (It shows the village from the middle of the lake)
 
 	// Calculate how much we're moving
-	static const uint16 hotspotPositions[] = { 1, 5, 4, 2, 0, 0, 3 };
+	Common::String buttonName = _vm->_curHotspot->getName();
+	uint32 buttonPos = buttonName.lastChar() - '0';
+
 	uint32 &curPos = _vm->_vars["glviewpos"];
-	uint32 newPos = curPos + hotspotPositions[_vm->_curHotspot->getIndex() - 2];
+	uint32 newPos = curPos + buttonPos;
 
 	// Now play the movie
 	VideoHandle handle = _vm->_video->playMovieRiven(1);
@@ -1756,15 +1788,15 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
 }
 
 void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(81, 2);
+	resetDomeSliders(81, 10);
 }
 
 void RivenExternal::xjdome25_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(81, 29, 28, 2);
+	dragDomeSlider(81, 10);
 }
 
 void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(2);
+	checkSliderCursorChange(10);
 }
 
 void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) {
@@ -1995,8 +2027,10 @@ void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) {
 	}
 
 	// Enable the correct hotspots for the movement now
-	_vm->_hotspots[2]->enable(!_vm->_hotspots[2]->isEnabled());
-	_vm->_hotspots[3]->enable(!_vm->_hotspots[3]->isEnabled());
+	RivenHotspot *rotateLeft = _vm->getCurCard()->getHotspotByName("rotateLeft");
+	RivenHotspot *rotateRight = _vm->getCurCard()->getHotspotByName("rotateRight");
+	rotateLeft->enable(!rotateLeft->isEnabled());
+	rotateRight->enable(!rotateRight->isEnabled());
 
 	// Update the cursor
 	_vm->updateCurrentHotspot();
@@ -2046,15 +2080,15 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 	uint32 endTime = argv[2] * 1000 / 600;
 
 	// Track down our hotspot
-	// Of course, they're not in any sane order...
-	static const uint16 hotspotMap[] = { 1, 3, 2, 0 };
-	Common::Rect hotspotRect = _vm->_hotspots[hotspotMap[argv[3] - 1]]->getRect();
+	Common::String hotspotName = Common::String::format("touchBook%d", argv[3]);
+	RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByName(hotspotName);
+	Common::Rect hotspotRect = hotspot->getRect();
 
 	debug(0, "xbookclick:");
 	debug(0, "\tVideo Code = %d", argv[0]);
 	debug(0, "\tStart Time = %dms", startTime);
 	debug(0, "\tEnd Time   = %dms", endTime);
-	debug(0, "\tHotspot    = %d -> %d", argv[3], hotspotMap[argv[3] - 1]);
+	debug(0, "\tHotspot    = %d -> %s", argv[3], hotspotName.c_str());
 
 	// Just let the video play while we wait until Gehn opens the trap book for us
 	while (video->getTime() < startTime && !_vm->shouldQuit()) {
@@ -2157,9 +2191,13 @@ void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {
 	_vm->_video->playMovieBlockingRiven(1);
 
 	// Set the hotspots into their correct states
-	_vm->_hotspots[2]->enable(false);
-	_vm->_hotspots[5]->enable(false);
-	_vm->_hotspots[6]->enable(true);
+	RivenHotspot *closeBook = _vm->getCurCard()->getHotspotByName("closeBook");
+	RivenHotspot *nullHotspot = _vm->getCurCard()->getHotspotByName("null");
+	RivenHotspot *openBook = _vm->getCurCard()->getHotspotByName("openBook");
+
+	closeBook->enable(false);
+	nullHotspot->enable(false);
+	openBook->enable(true);
 
 	// We now need to draw PLST 1 and refresh, but PLST 1 is
 	// drawn when refreshing anyway, so don't worry about that.
@@ -2284,19 +2322,19 @@ void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) {
 }
 
 void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders(31, 5);
+	checkDomeSliders();
 }
 
 void RivenExternal::xpisland25_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(10, 6);
+	resetDomeSliders(10, 14);
 }
 
 void RivenExternal::xpisland25_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(10, 31, 5, 6);
+	dragDomeSlider(10, 14);
 }
 
 void RivenExternal::xpisland25_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(6);
+	checkSliderCursorChange(14);
 }
 
 // ------------------------------------------------------------------------------------
@@ -2484,7 +2522,8 @@ void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {
 
 	// If we have hit the correct 5 buttons in a row, activate the hotspot to open up the
 	// telescope cover.
-	_vm->_hotspots[9]->enable(correctDigits == 5);
+	RivenHotspot *openCover = _vm->getCurCard()->getHotspotByName("openCover");
+	openCover->enable(correctDigits == 5);
 }
 
 // Atrus' Journal and Trap Book are added to inventory
@@ -2614,20 +2653,23 @@ void RivenExternal::xt7600_setupmarbles(uint16 argc, uint16 *argv) {
 void RivenExternal::setMarbleHotspots() {
 	// Set the hotspots
 	for (uint16 i = 0; i < kMarbleCount; i++) {
-		uint32 &marblePos = _vm->_vars[s_marbleNames[i]];
+		uint32 marblePos = _vm->_vars[s_marbleNames[i]];
+		RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
 
 		if (marblePos == 0) // In the receptacle
-			_vm->_hotspots[i + 3]->setRect(_marbleBaseHotspots[i]);
+			marbleHotspot->setRect(_marbleBaseHotspots[i]);
 		else                 // On the grid
-			_vm->_hotspots[i + 3]->setRect(generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)));
+			marbleHotspot->setRect(generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)));
 	}
 }
 
 void RivenExternal::xt7800_setup(uint16 argc, uint16 *argv) {
 	// First, let's store the base receptacle hotspots for the marbles
 	if (_marbleBaseHotspots.empty())
-		for (uint16 i = 0; i < kMarbleCount; i++)
-			_marbleBaseHotspots.push_back(_vm->_hotspots[i + 3]->getRect());
+		for (uint16 i = 0; i < kMarbleCount; i++) {
+			RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
+			_marbleBaseHotspots.push_back(marbleHotspot->getRect());
+		}
 
 	// Move the marble hotspots based on their position variables
 	setMarbleHotspots();
@@ -2640,7 +2682,9 @@ void RivenExternal::drawMarbles() {
 		if (_vm->_vars["themarble"] - 1 == i)
 			continue;
 
-		Common::Rect rect = _vm->_hotspots[i + 3]->getRect();
+		RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
+
+		Common::Rect rect = marbleHotspot->getRect();
 		// Trim the rect down a bit
 		rect.left += 3;
 		rect.top += 3;
@@ -2662,11 +2706,13 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
 	uint32 &marble = _vm->_vars["themarble"];
 	marble = 0;
 
-	for (uint32 i = 0; i < kMarbleCount; i++)
-		if (_vm->_hotspots[i + 3]->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+	for (uint32 i = 0; i < kMarbleCount; i++) {
+		RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
+		if (marbleHotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			marble = i + 1;
 			break;
 		}
+	}
 
 	// xtakeit() shouldn't be called if we're not on a marble hotspot
 	assert(marble != 0);
@@ -2735,19 +2781,19 @@ void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) {
 }
 
 void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders(29, 30);
+	checkDomeSliders();
 }
 
 void RivenExternal::xtisland5056_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(37, 3);
+	resetDomeSliders(37, 24);
 }
 
 void RivenExternal::xtisland5056_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(37, 29, 30, 3);
+	dragDomeSlider(37, 24);
 }
 
 void RivenExternal::xtisland5056_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(3);
+	checkSliderCursorChange(24);
 }
 
 void RivenExternal::xtatboundary(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
index 58dfde1..f1740f4 100644
--- a/engines/mohawk/riven_external.h
+++ b/engines/mohawk/riven_external.h
@@ -64,9 +64,9 @@ private:
 	void runDomeCheck();
 	void runDomeButtonMovie();
 	void resetDomeSliders(uint16 soundId, uint16 startHotspot);
-	void checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDomeHotspot);
+	void checkDomeSliders();
 	void checkSliderCursorChange(uint16 startHotspot);
-	void dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot);
+	void dragDomeSlider(uint16 soundId, uint16 startHotspot);
 	void drawDomeSliders(uint16 startHotspot);
 	void drawMarbles();
 	void setMarbleHotspots();
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index c299f86..f43c33f 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -358,11 +358,9 @@ void RivenSimpleCommand::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 9: enable hotspot (blst_id)
 void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
-	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
-		if (_vm->_hotspots[i]->getBlstId() == argv[0]) {
-			debug(2, "Enabling hotspot with BLST ID %d", argv[0]);
-			_vm->_hotspots[i]->enable(true);
-		}
+	RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(argv[0]);
+	if (hotspot) {
+		hotspot->enable(true);
 	}
 
 	// Recheck our current hotspot because it may have now changed
@@ -371,11 +369,9 @@ void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 10: disable hotspot (blst_id)
 void RivenSimpleCommand::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
-	for (uint16 i = 0; i < _vm->_hotspots.size(); i++) {
-		if (_vm->_hotspots[i]->getBlstId() == argv[0]) {
-			debug(2, "Disabling hotspot with BLST ID %d", argv[0]);
-			_vm->_hotspots[i]->enable(false);
-		}
+	RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(argv[0]);
+	if (hotspot) {
+		hotspot->enable(false);
 	}
 
 	// Recheck our current hotspot because it may have now changed
@@ -600,10 +596,12 @@ void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 		uint16 enabled = blst->readUint16BE();
 		uint16 hotspotID = blst->readUint16BE();
 
-		if (argv[0] == index)
-			for (uint16 j = 0; j < _vm->_hotspots.size(); j++)
-				if (_vm->_hotspots[j]->getBlstId() == hotspotID)
-					_vm->_hotspots[j]->enable(enabled == 1);
+		if (argv[0] == index) {
+			RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(hotspotID);
+			if (hotspot) {
+				hotspot->enable(enabled == 1);
+			}
+		}
 	}
 
 	delete blst;


Commit: 4bdf88496d959c88f975c7d5f4e2ca6f25b454f4
    https://github.com/scummvm/scummvm/commit/4bdf88496d959c88f975c7d5f4e2ca6f25b454f4
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move Riven's name lists to a separate class

Changed paths:
  A engines/mohawk/riven_stack.cpp
  A engines/mohawk/riven_stack.h
    engines/mohawk/console.cpp
    engines/mohawk/module.mk
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h
    engines/mohawk/riven_vars.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 41dd535..b95b999 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -564,50 +564,6 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 
 	_vm->changeToStack(newStack);
 
-	// Load in Variable Names
-	Common::SeekableReadStream *nameStream = _vm->getResource(ID_NAME, VariableNames);
-	Common::StringArray varNames;
-
-	uint16 namesCount = nameStream->readUint16BE();
-	uint16 *stringOffsets = new uint16[namesCount];
-	for (uint16 i = 0; i < namesCount; i++)
-		stringOffsets[i] = nameStream->readUint16BE();
-	nameStream->seek(namesCount * 2, SEEK_CUR);
-	int32 curNamesPos = nameStream->pos();
-
-	for (uint32 i = 0; i < namesCount; i++) {
-		nameStream->seek(curNamesPos + stringOffsets[i]);
-
-		Common::String name;
-		for (char c = nameStream->readByte(); c; c = nameStream->readByte())
-			name += c;
-		varNames.push_back(name);
-	}
-	delete nameStream;
-	delete[] stringOffsets;
-
-	// Load in External Command Names
-	nameStream = _vm->getResource(ID_NAME, ExternalCommandNames);
-	Common::StringArray xNames;
-
-	namesCount = nameStream->readUint16BE();
-	stringOffsets = new uint16[namesCount];
-	for (uint16 i = 0; i < namesCount; i++)
-		stringOffsets[i] = nameStream->readUint16BE();
-	nameStream->seek(namesCount * 2, SEEK_CUR);
-	curNamesPos = nameStream->pos();
-
-	for (uint32 i = 0; i < namesCount; i++) {
-		nameStream->seek(curNamesPos + stringOffsets[i]);
-
-		Common::String name;
-		for (char c = nameStream->readByte(); c; c = nameStream->readByte())
-			name += c;
-		xNames.push_back(name);
-	}
-	delete nameStream;
-	delete[] stringOffsets;
-
 	// Get CARD/HSPT data and dump their scripts
 	if (!scumm_stricmp(argv[2], "CARD")) {
 		// Use debugN to print these because the scripts can get very large and would
@@ -623,7 +579,7 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 		RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream);
 		for (uint32 i = 0; i < scriptList.size(); i++) {
 			debugN("Stream Type %d:\n", scriptList[i].type);
-			scriptList[i].script->dumpScript(varNames, xNames, 0);
+			scriptList[i].script->dumpScript(0);
 		}
 		delete cardStream;
 	} else if (!scumm_stricmp(argv[2], "HSPT")) {
@@ -640,8 +596,8 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 			hsptStream->seek(22, SEEK_CUR);	// Skip non-script related stuff
 			RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream);
 			for (uint32 j = 0; j < scriptList.size(); j++) {
-				debugN("\tStream Type %d:\n", scriptList[i].type);
-				scriptList[j].script->dumpScript(varNames, xNames, 1);
+				debugN("\tStream Type %d:\n", scriptList[j].type);
+				scriptList[j].script->dumpScript(1);
 			}
 		}
 
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 8a186b1..7de2a16 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -59,6 +59,7 @@ MODULE_OBJS += \
 	riven_saveload.o \
 	riven_scripts.o \
 	riven_sound.o \
+	riven_stack.o \
 	riven_vars.o
 endif
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 8e30080..4d30755 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -50,7 +50,8 @@ Common::Rect *g_cathJournalRect3;
 Common::Rect *g_trapBookRect3;
 Common::Rect *g_demoExitRect;
 
-MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
+MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) :
+		MohawkEngine(syst, gamedesc) {
 	_showHotspots = false;
 	_gameOver = false;
 	_activatedPLST = false;
@@ -345,6 +346,13 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 	if (_mhk.empty())
 		error("Could not load stack %s", getStackName(_curStack).c_str());
 
+	// Load stack specific names
+	_varNames = RivenNameList(this, kVariableNames);
+	_externalCommandNames = RivenNameList(this, kExternalCommandNames);
+	_stackNames = RivenNameList(this, kStackNames);
+	_cardNames = RivenNameList(this, kCardNames);
+	_hotspotNames = RivenNameList(this, kHotspotNames);
+
 	// Stop any currently playing sounds
 	_sound->stopAllSLST();
 }
@@ -516,65 +524,6 @@ Common::SeekableReadStream *MohawkEngine_Riven::getExtrasResource(uint32 tag, ui
 	return _extrasFile->getResource(tag, id);
 }
 
-Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) {
-	Common::SeekableReadStream* nameStream = getResource(ID_NAME, nameResource);
-	uint16 fieldCount = nameStream->readUint16BE();
-	uint16* stringOffsets = new uint16[fieldCount];
-	Common::String name;
-	char c;
-
-	if (nameID < fieldCount) {
-		for (uint16 i = 0; i < fieldCount; i++)
-			stringOffsets[i] = nameStream->readUint16BE();
-		for (uint16 i = 0; i < fieldCount; i++)
-			nameStream->readUint16BE();	// Skip unknown values
-
-		nameStream->seek(stringOffsets[nameID], SEEK_CUR);
-		c = (char)nameStream->readByte();
-
-		while (c) {
-			name += c;
-			c = (char)nameStream->readByte();
-		}
-	}
-
-	delete nameStream;
-	delete[] stringOffsets;
-	return name;
-}
-
-int16 MohawkEngine_Riven::getIdFromName(uint16 nameResource, const Common::String &name) {
-	//TODO: Use proper data structures
-
-	Common::SeekableReadStream *nameStream = getResource(ID_NAME, nameResource);
-	uint16 fieldCount = nameStream->readUint16BE();
-	uint16 *stringOffsets = new uint16[fieldCount];
-
-	for (uint16 i = 0; i < fieldCount; i++)
-		stringOffsets[i] = nameStream->readUint16BE();
-	for (uint16 i = 0; i < fieldCount; i++)
-		nameStream->readUint16BE();	// Skip unknown values
-
-	for (uint16 i = 0; i < fieldCount; i++) {
-		nameStream->seek(stringOffsets[i], SEEK_CUR);
-
-		Common::String readName;
-		char c = (char)nameStream->readByte();
-		while (c) {
-			readName += c;
-			c = (char)nameStream->readByte();
-		}
-
-		if (readName.equalsIgnoreCase(name)) {
-			return i;
-		}
-	}
-
-	delete nameStream;
-	delete[] stringOffsets;
-	return -1;
-}
-
 uint16 MohawkEngine_Riven::matchRMAPToCard(uint32 rmapCode) {
 	uint16 index = 0;
 	Common::SeekableReadStream *rmapStream = getResource(ID_RMAP, 1);
@@ -911,7 +860,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 }
 
 void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
-	Common::String cardName = getName(CardNames, cardNameId);
+	Common::String cardName = getName(kCardNames, cardNameId);
 	if (cardName.empty())
 		return;
 	ZipMode zip;
@@ -934,6 +883,40 @@ bool MohawkEngine_Riven::isZipVisitedCard(const Common::String &hotspotName) con
 	return foundMatch;
 }
 
+Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) {
+	switch (nameResource) {
+		case kVariableNames:
+			return _varNames.getName(nameID);
+		case kExternalCommandNames:
+			return _externalCommandNames.getName(nameID);
+		case kStackNames:
+			return _stackNames.getName(nameID);
+		case kCardNames:
+			return _cardNames.getName(nameID);
+		case kHotspotNames:
+			return _hotspotNames.getName(nameID);
+		default:
+			error("Unknown name resource %d", nameResource);
+	}
+}
+
+int16 MohawkEngine_Riven::getIdFromName(uint16 nameResource, const Common::String &name) {
+	switch (nameResource) {
+		case kVariableNames:
+			return _varNames.getNameId(name);
+		case kExternalCommandNames:
+			return _externalCommandNames.getNameId(name);
+		case kStackNames:
+			return _stackNames.getNameId(name);
+		case kCardNames:
+			return _cardNames.getNameId(name);
+		case kHotspotNames:
+			return _hotspotNames.getNameId(name);
+		default:
+			error("Unknown name resource %d", nameResource);
+	}
+}
+
 bool ZipMode::operator== (const ZipMode &z) const {
 	return z.name == name && z.id == id;
 }
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 4cff24e..53789af 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -25,6 +25,7 @@
 
 #include "mohawk/installer_archive.h"
 #include "mohawk/mohawk.h"
+#include "mohawk/riven_stack.h"
 #include "mohawk/riven_scripts.h"
 
 #include "common/hashmap.h"
@@ -63,11 +64,11 @@ enum {
 
 // NAME Resource ID's
 enum {
-	CardNames = 1,
-	HotspotNames = 2,
-	ExternalCommandNames = 3,
-	VariableNames = 4,
-	StackNames = 5
+	kCardNames = 1,
+	kHotspotNames = 2,
+	kExternalCommandNames = 3,
+	kVariableNames = 4,
+	kStackNames = 5
 };
 
 enum RivenTransitionSpeed {
@@ -134,6 +135,13 @@ private:
 	uint16 _curStack;
 	void handleEvents();
 
+	// Stack resource names
+	RivenNameList _varNames;
+	RivenNameList _externalCommandNames;
+	RivenNameList _hotspotNames;
+	RivenNameList _cardNames;
+	RivenNameList _stackNames;
+
 	// Hotspot related functions and variables
 	void checkInventoryClick();
 	bool _showHotspots;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index b3ec065..39a818c 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -246,7 +246,7 @@ Common::Array<RivenHotspot *> RivenCard::getHotspots() const {
 }
 
 RivenHotspot *RivenCard::getHotspotByName(const Common::String &name) const {
-	int16 nameId = _vm->getIdFromName(HotspotNames, name);
+	int16 nameId = _vm->getIdFromName(kHotspotNames, name);
 
 	for (uint i = 0; i < _hotspots.size(); i++) {
 		if (_hotspots[i]->getNameId() == nameId) {
@@ -345,7 +345,7 @@ Common::String RivenHotspot::getName() const {
 	if (_nameResource < 0)
 		return Common::String();
 
-	return _vm->getName(HotspotNames, _nameResource);
+	return _vm->getName(kHotspotNames, _nameResource);
 }
 
 uint16 RivenHotspot::getIndex() const {
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 88718db..1be119d 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -198,7 +198,7 @@ void RivenExternal::setupCommands() {
 }
 
 void RivenExternal::runCommand(uint16 argc, uint16 *argv) {
-	Common::String externalCommandName = _vm->getName(ExternalCommandNames, argv[0]);
+	Common::String externalCommandName = _vm->getName(kExternalCommandNames, argv[0]);
 
 	for (uint16 i = 0; i < _externalCommands.size(); i++)
 		if (externalCommandName == _externalCommands[i]->desc) {
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index f43c33f..0595c77 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -163,9 +163,9 @@ RivenScript::~RivenScript() {
 	}
 }
 
-void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
+void RivenScript::dumpScript(byte tabs) {
 	for (uint16 i = 0; i < _commands.size(); i++) {
-		_commands[i]->dump(varNames, xNames, tabs);
+		_commands[i]->dump(tabs);
 	}
 }
 
@@ -455,7 +455,7 @@ void RivenSimpleCommand::incrementVariable(uint16 op, uint16 argc, uint16 *argv)
 
 // Command 27: go to stack (stack name, code high, code low)
 void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
-	Common::String stackName = _vm->getName(StackNames, argv[0]);
+	Common::String stackName = _vm->getName(kStackNames, argv[0]);
 	int8 index = -1;
 
 	for (byte i = 0; i < 8; i++)
@@ -650,14 +650,15 @@ void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_video->activateMLST(argv[0], _vm->getCurCard()->getId());
 }
 
-void RivenSimpleCommand::dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
+void RivenSimpleCommand::dump(byte tabs) {
 	printTabs(tabs);
 
 	if (_type == 7) { // Use the variable name
-		uint16 var = _arguments[0];
-		debugN("%s = %d;\n", varNames[var].c_str(), _arguments[1]);
+		Common::String varName = _vm->getName(kVariableNames, _arguments[0]);
+		debugN("%s = %d;\n", varName.c_str(), _arguments[1]);
 	} else if (_type == 17) { // Use the external command name
-		debugN("%s(", xNames[_arguments[0]].c_str());
+		Common::String externalCommandName = _vm->getName(kVariableNames, _arguments[0]);
+		debugN("%s(", externalCommandName.c_str());
 		uint16 varCount = _arguments[1];
 		for (uint16 j = 0; j < varCount; j++) {
 			debugN("%d", _arguments[1 + j]);
@@ -666,8 +667,8 @@ void RivenSimpleCommand::dump(const Common::StringArray &varNames, const Common:
 		}
 		debugN(");\n");
 	} else if (_type == 24) { // Use the variable name
-		uint16 var = _arguments[0];
-		debugN("%s += %d;\n", varNames[var].c_str(), _arguments[1]);
+		Common::String varName = _vm->getName(kVariableNames, _arguments[0]);
+		debugN("%s += %d;\n", varName.c_str(), _arguments[1]);
 	} else {
 		debugN("%s(", _opcodes[_type].desc);
 		for (uint16 j = 0; j < _arguments.size(); j++) {
@@ -727,15 +728,16 @@ RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm,
 	return command;
 }
 
-void RivenSwitchCommand::dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
-	printTabs(tabs); debugN("switch (%s) {\n", varNames[_variableId].c_str());
+void RivenSwitchCommand::dump(byte tabs) {
+	Common::String varName = _vm->getName(kVariableNames, _variableId);
+	printTabs(tabs); debugN("switch (%s) {\n", varName.c_str());
 	for (uint16 j = 0; j < _branches.size(); j++) {
 		printTabs(tabs + 1);
 		if (_branches[j].value == 0xFFFF)
 			debugN("default:\n");
 		else
 			debugN("case %d:\n", _branches[j].value);
-		_branches[j].script->dumpScript(varNames, xNames, tabs + 2);
+		_branches[j].script->dumpScript(tabs + 2);
 		printTabs(tabs + 2); debugN("break;\n");
 	}
 	printTabs(tabs); debugN("}\n");
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index b05b99e..8710026 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -79,7 +79,7 @@ public:
 	void run();
 
 	/** Print script details to the standard output */
-	void dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
+	void dumpScript(byte tabs);
 
 	/** Stop the script after the current command */
 	void stopRunning() { _continueRunning = false; }
@@ -158,7 +158,7 @@ public:
 	virtual ~RivenCommand();
 
 	/** Print details about the command to standard output */
-	virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) = 0;
+	virtual void dump(byte tabs) = 0;
 
 	/** Execute the command */
 	virtual void execute() = 0;
@@ -180,7 +180,7 @@ public:
 	virtual ~RivenSimpleCommand();
 
 	// RivenCommand API
-	virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) override;
+	virtual void dump(byte tabs) override;
 	virtual void execute() override;
 
 private:
@@ -253,7 +253,7 @@ public:
 	virtual ~RivenSwitchCommand();
 
 	// RivenCommand API
-	virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) override;
+	virtual void dump(byte tabs) override;
 	virtual void execute() override;
 
 private:
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
new file mode 100644
index 0000000..3ff34e7
--- /dev/null
+++ b/engines/mohawk/riven_stack.cpp
@@ -0,0 +1,102 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stack.h"
+
+#include "mohawk/riven.h"
+#include "mohawk/resource.h"
+
+namespace Mohawk {
+
+RivenNameList::RivenNameList() {
+
+}
+
+RivenNameList::RivenNameList(MohawkEngine_Riven *vm, uint16 id) {
+	loadResource(vm, id);
+}
+
+RivenNameList::~RivenNameList() {
+
+}
+
+void RivenNameList::loadResource(MohawkEngine_Riven *vm, uint16 id) {
+	Common::SeekableReadStream *nameStream = vm->getResource(ID_NAME, id);
+
+	uint16 namesCount = nameStream->readUint16BE();
+
+	Common::Array<uint16> stringOffsets;
+	stringOffsets.resize(namesCount);
+	for (uint16 i = 0; i < namesCount; i++) {
+		stringOffsets[i] = nameStream->readUint16BE();
+	}
+
+	_index.resize(namesCount);
+	for (uint16 i = 0; i < namesCount; i++) {
+		_index[i] = nameStream->readUint16BE();
+	}
+
+	int32 curNamesPos = nameStream->pos();
+
+	_names.resize(namesCount);
+	for (uint32 i = 0; i < namesCount; i++) {
+		nameStream->seek(curNamesPos + stringOffsets[i]);
+
+		Common::String name;
+		for (char c = nameStream->readByte(); c; c = nameStream->readByte())
+			name += c;
+
+		_names[i] = name;
+	}
+
+	delete nameStream;
+}
+
+Common::String RivenNameList::getName(uint16 nameID) const {
+	return _names[nameID];
+}
+
+int16 RivenNameList::getNameId(const Common::String &name) const {
+	int low = 0;
+	int high = _index.size() - 1;
+	int midpoint = 0;
+
+	// Binary search using the sorted _index array
+	while (low <= high)	{
+		midpoint = low + (high - low) / 2;
+
+		const Common::String &midpointName = _names[_index[midpoint]];
+
+		int comparison = name.compareToIgnoreCase(midpointName);
+		if (comparison == 0) {
+			return _index[midpoint];
+		} else if (comparison < 0) {
+			high = midpoint - 1;
+		} else {
+			low = midpoint + 1;
+		}
+	}
+
+	return -1;
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
new file mode 100644
index 0000000..ca513a1
--- /dev/null
+++ b/engines/mohawk/riven_stack.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACK_H
+#define RIVEN_STACK_H
+
+#include "common/str-array.h"
+
+namespace Mohawk {
+
+class MohawkEngine_Riven;
+
+/**
+ * Name lists provide bidirectional association between an object's name and its id
+ */
+class RivenNameList {
+public:
+	RivenNameList();
+	RivenNameList(MohawkEngine_Riven *vm, uint16 id);
+	~RivenNameList();
+
+	/** Get the name of an object using its id */
+	Common::String getName(uint16 nameID) const;
+
+	/**
+	 * Get the id of an object using its name
+	 *
+	 * This query is case insensitive.
+	 */
+	int16 getNameId(const Common::String &name) const;
+
+private:
+	Common::StringArray _names;
+	Common::Array<uint16> _index;
+
+	void loadResource(MohawkEngine_Riven *vm, uint16 id);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index f09aba7..c45b464 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -268,7 +268,7 @@ static const char *variableNames[] = {
 };
 
 uint32 &MohawkEngine_Riven::getStackVar(uint32 index) {
-	Common::String name = getName(VariableNames, index);
+	Common::String name = getName(kVariableNames, index);
 
 	if (!_vars.contains(name))
 		error("Could not find variable '%s' (stack variable %d)", name.c_str(), index);


Commit: c1e0b8684b5d89fc5c10088a3222a8760e3b4ade
    https://github.com/scummvm/scummvm/commit/c1e0b8684b5d89fc5c10088a3222a8760e3b4ade
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move BLST list handling to RivenCard

Changed paths:
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 39a818c..158c72f 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -36,6 +36,7 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	loadHotspots(id);
 	loadCardPictureList(id);
 	loadCardSoundList(id);
+	loadCardHotspotEnableList(id);
 }
 
 RivenCard::~RivenCard() {
@@ -267,6 +268,33 @@ RivenHotspot *RivenCard::getHotspotByBlstId(const uint16 blstId) const {
 	return nullptr;
 }
 
+void RivenCard::loadCardHotspotEnableList(uint16 id) {
+	Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, id);
+
+	uint16 recordCount = blst->readUint16BE();
+	_hotspotEnableList.resize(recordCount);
+
+	for (uint16 i = 0; i < recordCount; i++) {
+		HotspotEnableRecord &record = _hotspotEnableList[i];
+		record.index = blst->readUint16BE();
+		record.enabled = blst->readUint16BE();
+		record.hotspotId = blst->readUint16BE();
+	}
+
+	delete blst;
+}
+
+void RivenCard::activateHotspotEnableRecord(uint16 index) {
+	for (uint16 i = 0; i < _hotspotEnableList.size(); i++) {
+		const HotspotEnableRecord &record = _hotspotEnableList[i];
+		if (record.index == index) {
+			RivenHotspot *hotspot = getHotspotByBlstId(record.hotspotId);
+			hotspot->enable(record.enabled == 1);
+			break;
+		}
+	}
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 10b7727..6648d26 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -94,14 +94,24 @@ public:
 	/** Get all the hotspots in the card. To be used for debugging features only */
 	Common::Array<RivenHotspot *> getHotspots() const;
 
+	/** Activate a hotspot using a hotspot enable list entry */
+	void activateHotspotEnableRecord(uint16 index);
+
 private:
 	void loadCardResource(uint16 id);
 	void loadHotspots(uint16 id);
 	void loadCardPictureList(uint16 id);
 	void loadCardSoundList(uint16 id);
+	void loadCardHotspotEnableList(uint16 id);
 
 	void defaultLoadScript();
 
+	struct HotspotEnableRecord {
+		uint16 index;
+		uint16 enabled;
+		uint16 hotspotId;
+	};
+
 	MohawkEngine_Riven *_vm;
 
 	// General card data
@@ -115,6 +125,7 @@ private:
 	// Resource lists
 	Common::Array<Picture> _pictureList;
 	Common::Array<SLSTRecord> _soundList;
+	Common::Array<HotspotEnableRecord> _hotspotEnableList;
 };
 
 /**
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 0595c77..2f3780d 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -576,8 +576,8 @@ void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 		return;
 
 	_vm->_activatedSLST = true;
-	SLSTRecord picture = _vm->getCurCard()->getSound(argv[0]);
-	_vm->_sound->playSLST(picture);
+	SLSTRecord slstRecord = _vm->getCurCard()->getSound(argv[0]);
+	_vm->_sound->playSLST(slstRecord);
 }
 
 // Command 41: activate MLST record and play
@@ -588,23 +588,7 @@ void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *arg
 
 // Command 43: activate BLST record (card hotspot enabling lists)
 void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
-	Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, _vm->getCurCard()->getId());
-	uint16 recordCount = blst->readUint16BE();
-
-	for (uint16 i = 0; i < recordCount; i++) {
-		uint16 index = blst->readUint16BE();	// record index
-		uint16 enabled = blst->readUint16BE();
-		uint16 hotspotID = blst->readUint16BE();
-
-		if (argv[0] == index) {
-			RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(hotspotID);
-			if (hotspot) {
-				hotspot->enable(enabled == 1);
-			}
-		}
-	}
-
-	delete blst;
+	_vm->getCurCard()->activateHotspotEnableRecord(argv[0]);
 
 	// Recheck our current hotspot because it may have now changed
 	_vm->updateCurrentHotspot();


Commit: 871516a9697db1914d703f0abb48a2f084452b0c
    https://github.com/scummvm/scummvm/commit/871516a9697db1914d703f0abb48a2f084452b0c
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the effect list to RivenCard

Changed paths:
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 158c72f..cdf9cf1 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -37,6 +37,7 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	loadCardPictureList(id);
 	loadCardSoundList(id);
 	loadCardHotspotEnableList(id);
+	loadCardWaterEffectList(id);
 }
 
 RivenCard::~RivenCard() {
@@ -269,7 +270,7 @@ RivenHotspot *RivenCard::getHotspotByBlstId(const uint16 blstId) const {
 }
 
 void RivenCard::loadCardHotspotEnableList(uint16 id) {
-	Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, id);
+	Common::SeekableReadStream *blst = _vm->getResource(ID_BLST, id);
 
 	uint16 recordCount = blst->readUint16BE();
 	_hotspotEnableList.resize(recordCount);
@@ -295,6 +296,36 @@ void RivenCard::activateHotspotEnableRecord(uint16 index) {
 	}
 }
 
+void RivenCard::loadCardWaterEffectList(uint16 id) {
+	Common::SeekableReadStream *flst = _vm->getResource(ID_FLST, id);
+
+	uint16 recordCount = flst->readUint16BE();
+	_waterEffectList.resize(recordCount);
+
+	for (uint16 i = 0; i < recordCount; i++) {
+		WaterEffectRecord &record = _waterEffectList[i];
+		record.index = flst->readUint16BE();
+		record.sfxeId = flst->readUint16BE();
+		record.u0 = flst->readUint16BE();
+
+		if (record.u0 != 0) {
+			warning("FLST u0 non-zero");
+		}
+	}
+
+	delete flst;
+}
+
+void RivenCard::activateWaterEffect(uint16 index) {
+	for (uint16 i = 0; i < _waterEffectList.size(); i++) {
+		const WaterEffectRecord &record = _waterEffectList[i];
+		if (record.index == index) {
+			_vm->_gfx->scheduleWaterEffect(record.sfxeId);
+			break;
+		}
+	}
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 6648d26..007a26d 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -97,12 +97,16 @@ public:
 	/** Activate a hotspot using a hotspot enable list entry */
 	void activateHotspotEnableRecord(uint16 index);
 
+	/** Activate a water effect list entry */
+	void activateWaterEffect(uint16 index);
+
 private:
 	void loadCardResource(uint16 id);
 	void loadHotspots(uint16 id);
 	void loadCardPictureList(uint16 id);
 	void loadCardSoundList(uint16 id);
 	void loadCardHotspotEnableList(uint16 id);
+	void loadCardWaterEffectList(uint16 id);
 
 	void defaultLoadScript();
 
@@ -112,6 +116,12 @@ private:
 		uint16 hotspotId;
 	};
 
+	struct WaterEffectRecord {
+		uint16 index;
+		uint16 sfxeId;
+		uint16 u0;
+	};
+
 	MohawkEngine_Riven *_vm;
 
 	// General card data
@@ -126,6 +136,7 @@ private:
 	Common::Array<Picture> _pictureList;
 	Common::Array<SLSTRecord> _soundList;
 	Common::Array<HotspotEnableRecord> _hotspotEnableList;
+	Common::Array<WaterEffectRecord> _waterEffectList;
 };
 
 /**
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 2f3780d..e951043 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -596,23 +596,7 @@ void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 44: activate FLST record (information on which SFXE resource this card should use)
 void RivenSimpleCommand::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
-	Common::SeekableReadStream* flst = _vm->getResource(ID_FLST, _vm->getCurCard()->getId());
-	uint16 recordCount = flst->readUint16BE();
-
-	for (uint16 i = 0; i < recordCount; i++) {
-		uint16 index = flst->readUint16BE();
-		uint16 sfxeID = flst->readUint16BE();
-
-		if (flst->readUint16BE() != 0)
-			warning("FLST u0 non-zero");
-
-		if (index == argv[0]) {
-			_vm->_gfx->scheduleWaterEffect(sfxeID);
-			break;
-		}
-	}
-
-	delete flst;
+	_vm->getCurCard()->activateWaterEffect(argv[0]);
 }
 
 // Command 45: do zip mode


Commit: c1331e124f61b22446de5ff81171f2cf3bac59ba
    https://github.com/scummvm/scummvm/commit/c1331e124f61b22446de5ff81171f2cf3bac59ba
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the current hotspot to RivenCard

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 4d30755..f31f12d 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -68,7 +68,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_saveLoad = nullptr;
 	_optionsDialog = nullptr;
 	_card = nullptr;
-	_curHotspot = nullptr;
 	removeTimer();
 
 	// NOTE: We can never really support CD swapping. All of the music files
@@ -212,7 +211,7 @@ void MohawkEngine_Riven::handleEvents() {
 	while (_eventMan->pollEvent(event)) {
 		switch (event.type) {
 		case Common::EVENT_MOUSEMOVE:
-			checkHotspotChange();
+			_card->onMouseMove(event.mouse);
 
 			if (!(getFeatures() & GF_DEMO)) {
 				// Check to show the inventory, but it is always "showing" in the demo
@@ -225,19 +224,17 @@ void MohawkEngine_Riven::handleEvents() {
 			needsUpdate = true;
 			break;
 		case Common::EVENT_LBUTTONDOWN:
-			if (_curHotspot) {
+			if (_card->getCurHotspot()) {
 				checkSunnerAlertClick();
-				_curHotspot->runScript(kMouseDownScript);
 			}
+			_card->onMouseDown(_eventMan->getMousePos());
 			break;
 		case Common::EVENT_LBUTTONUP:
 			// See RivenScript::switchCard() for more information on why we sometimes
 			// disable the next up event.
 			if (!_ignoreNextMouseUp) {
-				if (_curHotspot)
-					_curHotspot->runScript(kMouseUpScript);
-				else
-					checkInventoryClick();
+				_card->onMouseUp(_eventMan->getMousePos());
+				checkInventoryClick();
 			}
 			_ignoreNextMouseUp = false;
 			break;
@@ -291,8 +288,7 @@ void MohawkEngine_Riven::handleEvents() {
 		}
 	}
 
-	if (_curHotspot)
-		_curHotspot->runScript(kMouseInsideScript);
+	_card->onMouseUpdate();
 
 	// Update the screen if we need to
 	if (needsUpdate)
@@ -425,26 +421,8 @@ void MohawkEngine_Riven::refreshCard() {
 	installCardTimer();
 }
 
-void MohawkEngine_Riven::checkHotspotChange() {
-	Common::Point mousePos = _eventMan->getMousePos();
-	RivenHotspot *hotspot = _card->getHotspotContainingPoint(mousePos);
-
-	if (hotspot) {
-		if (_curHotspot != hotspot) {
-			_curHotspot = hotspot;
-			_cursor->setCursor(hotspot->getMouseCursor());
-			_system->updateScreen();
-		}
-	} else {
-		_curHotspot = nullptr;
-		_cursor->setCursor(kRivenMainCursor);
-		_system->updateScreen();
-	}
-}
-
 void MohawkEngine_Riven::updateCurrentHotspot() {
-	_curHotspot = nullptr;
-	checkHotspotChange();
+	_card->onMouseMove(_eventMan->getMousePos());
 }
 
 void MohawkEngine_Riven::checkInventoryClick() {
@@ -848,7 +826,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 		return;
 
 	// Only set the sunners variable on the forward hotspot
-	if (_curHotspot->getBlstId() != 3)
+	if (_card->getCurHotspot()->getBlstId() != 3)
 		return;
 
 	// If the alert video is no longer playing, we have nothing left to do
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 53789af..1c8f3f1 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -145,7 +145,6 @@ private:
 	// Hotspot related functions and variables
 	void checkInventoryClick();
 	bool _showHotspots;
-	void checkHotspotChange();
 
 	// Variables
 	void initVars();
@@ -173,9 +172,7 @@ public:
 	uint32 getCurCardRMAP();
 
 	// Hotspot functions/variables
-	RivenHotspot *_curHotspot;
 	Common::Array<ZipMode> _zipModeData;
-	RivenHotspot *getCurHotspot() const { return _curHotspot; }
 	void updateCurrentHotspot();
 	void addZipVisitedCard(uint16 cardId, uint16 cardNameId);
 	bool isZipVisitedCard(const Common::String &hotspotName) const;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index cdf9cf1..15271b7 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -22,6 +22,7 @@
 
 #include "mohawk/riven_card.h"
 
+#include "mohawk/cursors.h"
 #include "mohawk/riven_graphics.h"
 
 #include "mohawk/resource.h"
@@ -31,7 +32,9 @@ namespace Mohawk {
 
 RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	_vm(vm),
-	_id(id) {
+	_id(id),
+	_hoveredHotspot(nullptr),
+	_pressedHotspot(nullptr) {
 	loadCardResource(id);
 	loadHotspots(id);
 	loadCardPictureList(id);
@@ -326,6 +329,82 @@ void RivenCard::activateWaterEffect(uint16 index) {
 	}
 }
 
+RivenHotspot *RivenCard::getCurHotspot() const {
+	return _hoveredHotspot;
+}
+
+void RivenCard::onMouseDown(const Common::Point &mouse) {
+	onMouseMove(mouse);
+
+	_pressedHotspot = _hoveredHotspot;
+	if (_pressedHotspot) {
+		RivenScriptPtr script = _pressedHotspot->getScript(kMouseDownScript);
+		_vm->_scriptMan->runScript(script, false);
+	}
+}
+
+void RivenCard::onMouseUp(const Common::Point &mouse) {
+	onMouseMove(mouse);
+
+	if (_pressedHotspot && _pressedHotspot == _hoveredHotspot) {
+		RivenScriptPtr script = _pressedHotspot->getScript(kMouseUpScript);
+		_vm->_scriptMan->runScript(script, false);
+	}
+
+	_pressedHotspot = nullptr;
+}
+
+void RivenCard::onMouseMove(const Common::Point &mouse) {
+	RivenHotspot *hotspot = getHotspotContainingPoint(mouse);
+
+	if (hotspot) {
+		if (hotspot != _hoveredHotspot) {
+			if (_hoveredHotspot) {
+				RivenScriptPtr script = _hoveredHotspot->getScript(kMouseLeaveScript);
+				_vm->_scriptMan->runScript(script, false);
+			}
+
+			_hoveredHotspot = hotspot;
+			RivenScriptPtr script = _hoveredHotspot->getScript(kMouseEnterScript);
+			_vm->_scriptMan->runScript(script, false);
+		}
+	} else {
+		_hoveredHotspot = nullptr;
+	}
+}
+
+void RivenCard::onMouseDragUpdate() {
+	if (_pressedHotspot) {
+		RivenScriptPtr script = _pressedHotspot->getScript(kMouseDragScript);
+		_vm->_scriptMan->runScript(script, false);
+	}
+}
+
+void RivenCard::onMouseUpdate() {
+	RivenScriptPtr script;
+	if (_hoveredHotspot) {
+		script = _hoveredHotspot->getScript(kMouseInsideScript);
+	}
+
+	if (script && !script->empty()) {
+		_vm->_scriptMan->runScript(script, false);
+	} else {
+		updateMouseCursor();
+	}
+}
+
+void RivenCard::updateMouseCursor() {
+	uint16 cursor;
+	if (_hoveredHotspot) {
+		cursor = _hoveredHotspot->getMouseCursor();
+	} else {
+		cursor = kRivenMainCursor;
+	}
+
+	_vm->_cursor->setCursor(cursor);
+	_vm->_system->updateScreen();
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
@@ -363,13 +442,13 @@ void RivenHotspot::loadFromStream(Common::ReadStream *stream) {
 	_scripts = _vm->_scriptMan->readScripts(stream);
 }
 
-void RivenHotspot::runScript(uint16 scriptType) {
+RivenScriptPtr RivenHotspot::getScript(uint16 scriptType) const {
 	for (uint16 i = 0; i < _scripts.size(); i++)
 		if (_scripts[i].type == scriptType) {
-			RivenScriptPtr script = _scripts[i].script;
-			_vm->_scriptMan->runScript(script, false);
-			break;
+			return _scripts[i].script;
 		}
+
+	return RivenScriptPtr();
 }
 
 bool RivenHotspot::isEnabled() const {
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 007a26d..98e65ae 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -91,6 +91,8 @@ public:
 	/** Get the hotspot with the specified BLST id */
 	RivenHotspot *getHotspotByBlstId(const uint16 blstId) const;
 
+	RivenHotspot *getCurHotspot() const;
+
 	/** Get all the hotspots in the card. To be used for debugging features only */
 	Common::Array<RivenHotspot *> getHotspots() const;
 
@@ -100,6 +102,21 @@ public:
 	/** Activate a water effect list entry */
 	void activateWaterEffect(uint16 index);
 
+	/** Handle a mouse down event */
+	void onMouseDown(const Common::Point &mouse);
+
+	/** Handle a mouse up event */
+	void onMouseUp(const Common::Point &mouse);
+
+	/** Handle a mouse move event */
+	void onMouseMove(const Common::Point &mouse);
+
+	/** Frame update handler for the mouse cursor */
+	void onMouseUpdate();
+
+	/** Frame update handler for mouse dragging */
+	void onMouseDragUpdate();
+
 private:
 	void loadCardResource(uint16 id);
 	void loadHotspots(uint16 id);
@@ -131,12 +148,16 @@ private:
 	RivenScriptList _scripts;
 
 	Common::Array<RivenHotspot *> _hotspots;
+	RivenHotspot *_hoveredHotspot;
+	RivenHotspot *_pressedHotspot;
 
 	// Resource lists
 	Common::Array<Picture> _pictureList;
 	Common::Array<SLSTRecord> _soundList;
 	Common::Array<HotspotEnableRecord> _hotspotEnableList;
 	Common::Array<WaterEffectRecord> _waterEffectList;
+
+	void updateMouseCursor();
 };
 
 /**
@@ -149,8 +170,8 @@ class RivenHotspot {
 public:
 	RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream);
 
-	/** Run one of the hotspot's scripts */
-	void runScript(uint16 scriptType);
+	/** Get the one of the hotspot's scripts */
+	RivenScriptPtr getScript(uint16 scriptType) const;
 
 	/** Enable or disable the hotspot */
 	void enable(bool e);
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 1be119d..6740f13 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -1368,7 +1368,7 @@ void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) {
 	}
 
 	// Calculate how much we're moving
-	Common::String buttonName = _vm->_curHotspot->getName();
+	Common::String buttonName = _vm->getCurCard()->getCurHotspot()->getName();
 	uint32 buttonPos = buttonName.lastChar() - '0';
 
 	uint32 &curPos = _vm->_vars["grviewpos"];
@@ -1439,7 +1439,7 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	// (It shows the village from the middle of the lake)
 
 	// Calculate how much we're moving
-	Common::String buttonName = _vm->_curHotspot->getName();
+	Common::String buttonName = _vm->getCurCard()->getCurHotspot()->getName();
 	uint32 buttonPos = buttonName.lastChar() - '0';
 
 	uint32 &curPos = _vm->_vars["glviewpos"];
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index e951043..6a86a98 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -114,6 +114,10 @@ void RivenScriptManager::clearStoredMovieOpcode() {
 }
 
 void RivenScriptManager::runScript(const RivenScriptPtr &script, bool queue) {
+	if (!script || script->empty()) {
+		return;
+	}
+
 	if (!queue) {
 		script->run();
 	} else {
@@ -179,6 +183,10 @@ void RivenScript::addCommand(RivenCommand *command) {
 	_commands.push_back(command);
 }
 
+bool RivenScript::empty() const {
+	return _commands.empty();
+}
+
 RivenCommand::RivenCommand(MohawkEngine_Riven *vm) :
 		_vm(vm) {
 
@@ -601,10 +609,10 @@ void RivenSimpleCommand::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 45: do zip mode
 void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
-	assert(_vm->getCurHotspot());
+	assert(_vm->getCurCard() && _vm->getCurCard()->getCurHotspot());
 
 	// Check the ZIPS records to see if we have a match to the hotspot name
-	Common::String hotspotName = _vm->getCurHotspot()->getName();
+	Common::String hotspotName = _vm->getCurCard()->getCurHotspot()->getName();
 
 	for (uint16 i = 0; i < _vm->_zipModeData.size(); i++)
 		if (_vm->_zipModeData[i].name == hotspotName) {
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 8710026..05cbd15 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -70,6 +70,9 @@ public:
 	/** Append a command to the script */
 	void addCommand(RivenCommand *command);
 
+	/** True if the script does not contain any command */
+	bool empty() const;
+
 	/**
 	 * Run the script
 	 *


Commit: 9b2c90c0b3323aac8d3fd49c20fa1121946996e2
    https://github.com/scummvm/scummvm/commit/9b2c90c0b3323aac8d3fd49c20fa1121946996e2
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: The ignoreNextMouseUp workaround is not necessary anymore

We now check the mouse up event happens on the same hotspot as the
mouse down event.

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index f31f12d..9ed7b22 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -56,7 +56,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_gameOver = false;
 	_activatedPLST = false;
 	_activatedSLST = false;
-	_ignoreNextMouseUp = false;
 	_extrasFile = nullptr;
 	_curStack = kStackUnknown;
 	_gfx = nullptr;
@@ -230,13 +229,8 @@ void MohawkEngine_Riven::handleEvents() {
 			_card->onMouseDown(_eventMan->getMousePos());
 			break;
 		case Common::EVENT_LBUTTONUP:
-			// See RivenScript::switchCard() for more information on why we sometimes
-			// disable the next up event.
-			if (!_ignoreNextMouseUp) {
-				_card->onMouseUp(_eventMan->getMousePos());
-				checkInventoryClick();
-			}
-			_ignoreNextMouseUp = false;
+			_card->onMouseUp(_eventMan->getMousePos());
+			checkInventoryClick();
 			break;
 		case Common::EVENT_KEYDOWN:
 			switch (event.kbd.keycode) {
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 1c8f3f1..0e951b6 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -155,7 +155,6 @@ private:
 
 	// Miscellaneous
 	bool _gameOver;
-	bool _ignoreNextMouseUp;
 	void checkSunnerAlertClick();
 
 public:
@@ -183,7 +182,6 @@ public:
 
 	// Miscellaneous
 	void setGameOver() { _gameOver = true; }
-	void ignoreNextMouseUp() { _ignoreNextMouseUp = true; }
 	Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id);
 	bool _activatedPLST;
 	bool _activatedSLST;
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 6a86a98..d220045 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -303,13 +303,6 @@ void RivenSimpleCommand::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
 // Command 2: go to card (card id)
 void RivenSimpleCommand::switchCard(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->changeToCard(argv[0]);
-
-	// WORKAROUND: If we changed card on a mouse down event,
-	// we want to ignore the next mouse up event so we don't
-	// change card when lifting the mouse on the next card.
-// TODO: Restore
-//	if (_scriptType == kMouseDownScript)
-//		_vm->ignoreNextMouseUp();
 }
 
 // Command 3: play an SLST from the script


Commit: aa0c89da03b3a3c9ef3a945178ca56d79331726c
    https://github.com/scummvm/scummvm/commit/aa0c89da03b3a3c9ef3a945178ca56d79331726c
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move running the card leave script to the RivenCard destructor

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 9ed7b22..2e03500 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -387,9 +387,6 @@ void MohawkEngine_Riven::changeToCard(uint16 dest) {
 			}
 	}
 
-	if (_card)
-		_card->runScript(kCardLeaveScript);
-
 	delete _card;
 	_card = new RivenCard(this, dest);
 
@@ -400,9 +397,6 @@ void MohawkEngine_Riven::refreshCard() {
 	// Clear any timer still floating around
 	removeTimer();
 
-	_gfx->clearWaterEffects();
-	_video->stopVideos();
-
 	_card->open();
 
 	if (_showHotspots)
@@ -838,7 +832,7 @@ void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
 	ZipMode zip;
 	zip.name = cardName;
 	zip.id = cardId;
-	if (!(Common::find(_zipModeData.begin(), _zipModeData.end(), zip) != _zipModeData.end()))
+	if (Common::find(_zipModeData.begin(), _zipModeData.end(), zip) == _zipModeData.end())
 		_zipModeData.push_back(zip);
 }
 
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 15271b7..402c10b 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -44,9 +44,14 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 }
 
 RivenCard::~RivenCard() {
+	runLeaveScripts();
+
 	for (uint i = 0; i < _hotspots.size(); i++) {
 		delete _hotspots[i];
 	}
+
+	_vm->_gfx->clearWaterEffects();
+	_vm->_video->stopVideos();
 }
 
 void RivenCard::loadCardResource(uint16 id) {
@@ -93,13 +98,18 @@ void RivenCard::initializeZipMode() {
 	}
 }
 
-void RivenCard::runScript(uint16 scriptType) {
+RivenScriptPtr RivenCard::getScript(uint16 scriptType) const {
 	for (uint16 i = 0; i < _scripts.size(); i++)
 		if (_scripts[i].type == scriptType) {
-			RivenScriptPtr script = _scripts[i].script;
-			_vm->_scriptMan->runScript(script, false);
-			break;
+			return _scripts[i].script;
 		}
+
+	return RivenScriptPtr(new RivenScript());
+}
+
+void RivenCard::runScript(uint16 scriptType) {
+	RivenScriptPtr script = getScript(scriptType);
+	_vm->_scriptMan->runScript(script, false);
 }
 
 uint16 RivenCard::getId() const {
@@ -381,12 +391,12 @@ void RivenCard::onMouseDragUpdate() {
 }
 
 void RivenCard::onMouseUpdate() {
-	RivenScriptPtr script;
+	RivenScriptPtr script(new RivenScript());
 	if (_hoveredHotspot) {
 		script = _hoveredHotspot->getScript(kMouseInsideScript);
 	}
 
-	if (script && !script->empty()) {
+	if (!script->empty()) {
 		_vm->_scriptMan->runScript(script, false);
 	} else {
 		updateMouseCursor();
@@ -405,6 +415,22 @@ void RivenCard::updateMouseCursor() {
 	_vm->_system->updateScreen();
 }
 
+void RivenCard::runLeaveScripts() {
+	RivenScriptPtr script(new RivenScript());
+
+	if (_pressedHotspot) {
+		script += _pressedHotspot->getScript(kMouseUpScript);
+	}
+
+	if (_hoveredHotspot) {
+		script += _hoveredHotspot->getScript(kMouseLeaveScript);
+	}
+
+	script += getScript(kCardLeaveScript);
+
+	_vm->_scriptMan->runScript(script, false);
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
@@ -448,7 +474,7 @@ RivenScriptPtr RivenHotspot::getScript(uint16 scriptType) const {
 			return _scripts[i].script;
 		}
 
-	return RivenScriptPtr();
+	return RivenScriptPtr(new RivenScript());
 }
 
 bool RivenHotspot::isEnabled() const {
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 98e65ae..f387201 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -125,7 +125,11 @@ private:
 	void loadCardHotspotEnableList(uint16 id);
 	void loadCardWaterEffectList(uint16 id);
 
+	RivenScriptPtr getScript(uint16 scriptType) const;
 	void defaultLoadScript();
+	void runLeaveScripts();
+
+	void updateMouseCursor();
 
 	struct HotspotEnableRecord {
 		uint16 index;
@@ -156,8 +160,6 @@ private:
 	Common::Array<SLSTRecord> _soundList;
 	Common::Array<HotspotEnableRecord> _hotspotEnableList;
 	Common::Array<WaterEffectRecord> _waterEffectList;
-
-	void updateMouseCursor();
 };
 
 /**
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index d220045..18c666d 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -114,10 +114,6 @@ void RivenScriptManager::clearStoredMovieOpcode() {
 }
 
 void RivenScriptManager::runScript(const RivenScriptPtr &script, bool queue) {
-	if (!script || script->empty()) {
-		return;
-	}
-
 	if (!queue) {
 		script->run();
 	} else {
@@ -187,6 +183,16 @@ bool RivenScript::empty() const {
 	return _commands.empty();
 }
 
+RivenScript &RivenScript::operator+=(const RivenScript &other) {
+	_commands.push_back(other._commands);
+	return *this;
+}
+
+RivenScriptPtr &operator+=(RivenScriptPtr &lhs, const RivenScriptPtr &rhs) {
+	*lhs += *rhs;
+	return lhs;
+}
+
 RivenCommand::RivenCommand(MohawkEngine_Riven *vm) :
 		_vm(vm) {
 
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 05cbd15..0d575d7 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -87,11 +87,17 @@ public:
 	/** Stop the script after the current command */
 	void stopRunning() { _continueRunning = false; }
 
+	/** Append the commands of the other script to this script */
+	RivenScript &operator+=(const RivenScript &other);
+
 private:
 	Common::Array<RivenCommand *> _commands;
 	bool _continueRunning;
 };
 
+/** Append the commands of the rhs Script to those of the lhs Script */
+RivenScriptPtr &operator+=(RivenScriptPtr &lhs, const RivenScriptPtr &rhs);
+
 /**
  * A script and its type
  *


Commit: f752066a8e4ab50a436c053bf2690a81492c4b15
    https://github.com/scummvm/scummvm/commit/f752066a8e4ab50a436c053bf2690a81492c4b15
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Introduce a new RivenStack class

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_saveload.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index b95b999..975c4c2 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -471,7 +471,7 @@ bool RivenConsole::Cmd_StopSound(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_CurStack(int argc, const char **argv) {
-	debugPrintf("Current Stack: %s\n", _vm->getStackName(_vm->getCurStack()).c_str());
+	debugPrintf("Current Stack: %s\n", _vm->getStackName(_vm->getCurStack()->getId()).c_str());
 
 	return true;
 }
@@ -510,7 +510,7 @@ bool RivenConsole::Cmd_ChangeStack(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
-	Common::Array<RivenHotspot *> hotspots = _vm->_card->getHotspots();
+	Common::Array<RivenHotspot *> hotspots = _vm->getCurCard()->getHotspots();
 
 	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), hotspots.size());
 
@@ -547,7 +547,7 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 		return true;
 	}
 
-	uint16 oldStack = _vm->getCurStack();
+	uint16 oldStack = _vm->getCurStack()->getId();
 	uint newStack = kStackUnknown;
 
 	for (uint i = kStackFirst; i <= kStackLast; i++) {
@@ -630,7 +630,7 @@ bool RivenConsole::Cmd_ListZipCards(int argc, const char **argv) {
 
 bool RivenConsole::Cmd_GetRMAP(int argc, const char **argv) {
 	uint32 rmapCode = _vm->getCurCardRMAP();
-	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getCurStack()).c_str(), _vm->getCurCard()->getId(), rmapCode);
+	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getCurStack()->getId()).c_str(), _vm->getCurCard()->getId(), rmapCode);
 	return true;
 }
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 2e03500..f42c29d 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -57,7 +57,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_activatedPLST = false;
 	_activatedSLST = false;
 	_extrasFile = nullptr;
-	_curStack = kStackUnknown;
+	_stack = nullptr;
 	_gfx = nullptr;
 	_sound = nullptr;
 	_externalScriptHandler = nullptr;
@@ -93,6 +93,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 
 MohawkEngine_Riven::~MohawkEngine_Riven() {
 	delete _card;
+	delete _stack;
 	delete _sound;
 	delete _gfx;
 	delete _console;
@@ -260,7 +261,7 @@ void MohawkEngine_Riven::handleEvents() {
 			case Common::KEYCODE_r:
 				// Return to the main menu in the demo on ctrl+r
 				if (event.kbd.flags & Common::KBD_CTRL && getFeatures() & GF_DEMO) {
-					if (_curStack != kStackAspit)
+					if (_stack->getId() != kStackAspit)
 						changeToStack(kStackAspit);
 					changeToCard(1);
 				}
@@ -268,7 +269,7 @@ void MohawkEngine_Riven::handleEvents() {
 			case Common::KEYCODE_p:
 				// Play the intro videos in the demo on ctrl+p
 				if (event.kbd.flags & Common::KBD_CTRL && getFeatures() & GF_DEMO) {
-					if (_curStack != kStackAspit)
+					if (_stack->getId() != kStackAspit)
 						changeToStack(kStackAspit);
 					changeToCard(6);
 				}
@@ -301,10 +302,11 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 	static const char *endings[] = { "_Data3.mhk", "_Data2.mhk", "_Data1.mhk", "_Data.mhk", "_Sounds.mhk" };
 
 	// Don't change stack to the current stack (if the files are loaded)
-	if (_curStack == n && !_mhk.empty())
+	if (_stack && _stack->getId() == n && !_mhk.empty())
 		return;
 
-	_curStack = n;
+	delete _stack;
+	_stack = new RivenStack(this, n);
 
 	// Stop any videos playing
 	_video->stopVideos();
@@ -319,7 +321,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 	_mhk.clear();
 
 	// Get the prefix character for the destination stack
-	char prefix = getStackName(_curStack)[0];
+	char prefix = getStackName(n)[0];
 
 	// Load any file that fits the patterns
 	for (int i = 0; i < ARRAYSIZE(endings); i++) {
@@ -334,7 +336,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 
 	// Make sure we have loaded files
 	if (_mhk.empty())
-		error("Could not load stack %s", getStackName(_curStack).c_str());
+		error("Could not load stack %s", getStackName(n).c_str());
 
 	// Load stack specific names
 	_varNames = RivenNameList(this, kVariableNames);
@@ -381,7 +383,7 @@ void MohawkEngine_Riven::changeToCard(uint16 dest) {
 
 	if (!(getFeatures() & GF_DEMO)) {
 		for (byte i = 0; i < 13; i++)
-			if (_curStack == rivenSpecialChange[i].startStack && dest == matchRMAPToCard(rivenSpecialChange[i].startCardRMAP)) {
+			if (_stack->getId() == rivenSpecialChange[i].startStack && dest == matchRMAPToCard(rivenSpecialChange[i].startCardRMAP)) {
 				changeToStack(rivenSpecialChange[i].targetStack);
 				dest = matchRMAPToCard(rivenSpecialChange[i].targetCardRMAP);
 			}
@@ -423,15 +425,15 @@ void MohawkEngine_Riven::checkInventoryClick() {
 	// In the demo, check if we've clicked the exit button
 	if (getFeatures() & GF_DEMO) {
 		if (g_demoExitRect->contains(mousePos)) {
-			if (_curStack == kStackAspit && _card->getId() == 1) {
+			if (_stack->getId() == kStackAspit && _card->getId() == 1) {
 				// From the main menu, go to the "quit" screen
 				changeToCard(12);
-			} else if (_curStack == kStackAspit && _card->getId() == 12) {
+			} else if (_stack->getId() == kStackAspit && _card->getId() == 12) {
 				// From the "quit" screen, just quit
 				_gameOver = true;
 			} else {
 				// Otherwise, return to the main menu
-				if (_curStack != kStackAspit)
+				if (_stack->getId() != kStackAspit)
 					changeToStack(kStackAspit);
 				changeToCard(1);
 			}
@@ -440,11 +442,11 @@ void MohawkEngine_Riven::checkInventoryClick() {
 	}
 
 	// No inventory shown on aspit
-	if (_curStack == kStackAspit)
+	if (_stack->getId() == kStackAspit)
 		return;
 
 	// Set the return stack/card id's.
-	_vars["returnstackid"] = _curStack;
+	_vars["returnstackid"] = _stack->getId();
 	_vars["returncardid"] = _card->getId();
 
 	// See RivenGraphics::showInventory() for an explanation
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 0e951b6..b8203a5 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -110,8 +110,6 @@ public:
 	Common::RandomSource *_rnd;
 	RivenScriptManager *_scriptMan;
 
-	RivenCard *_card;
-
 	GUI::Debugger *getDebugger();
 
 	bool canLoadGameStateCurrently() { return !(getFeatures() & GF_DEMO); }
@@ -132,7 +130,8 @@ private:
 	InstallerArchive _installerArchive;
 
 	// Stack/Card-related functions and variables
-	uint16 _curStack;
+	RivenCard *_card;
+	RivenStack *_stack;
 	void handleEvents();
 
 	// Stack resource names
@@ -166,7 +165,7 @@ public:
 	int16 getIdFromName(uint16 nameResource, const Common::String &name);
 	Common::String getStackName(uint16 stack) const;
 	RivenCard *getCurCard() const { return _card; }
-	uint16 getCurStack() const { return _curStack; }
+	RivenStack *getCurStack() const { return _stack; }
 	uint16 matchRMAPToCard(uint32);
 	uint32 getCurCardRMAP();
 
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 6740f13..f021ca6 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -418,7 +418,7 @@ void RivenExternal::drawDomeSliders(uint16 startHotspot) {
 
 	// On pspit, the rect is different by two pixels
 	// (alternatively, we could just use hotspot 3 here, but only on pspit is there a hotspot for this)
-	if (_vm->getCurStack() == kStackPspit)
+	if (_vm->getCurStack()->getId() == kStackPspit)
 		dstAreaRect.translate(-2, 0);
 
 	// Find out bitmap id
@@ -462,9 +462,9 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
 	uint32 &page = _vm->_vars["aatruspage"];
 
 	// Set hotspots depending on the page
-	RivenHotspot *openBook = _vm->_card->getHotspotByName("openBook");
-	RivenHotspot *nextPage = _vm->_card->getHotspotByName("nextpage");
-	RivenHotspot *prevPage = _vm->_card->getHotspotByName("prevpage");
+	RivenHotspot *openBook = _vm->getCurCard()->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->getCurCard()->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->getCurCard()->getHotspotByName("prevpage");
 	if (page == 1) {
 		prevPage->enable(false);
 		nextPage->enable(false);
@@ -502,7 +502,7 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
@@ -522,7 +522,7 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
@@ -530,9 +530,9 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
 	uint32 page = _vm->_vars["acathpage"];
 
 	// Set hotspots depending on the page
-	RivenHotspot *openBook = _vm->_card->getHotspotByName("openBook");
-	RivenHotspot *nextPage = _vm->_card->getHotspotByName("nextpage");
-	RivenHotspot *prevPage = _vm->_card->getHotspotByName("prevpage");
+	RivenHotspot *openBook = _vm->getCurCard()->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->getCurCard()->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->getCurCard()->getHotspotByName("prevpage");
 	if (page == 1) {
 		prevPage->enable(false);
 		nextPage->enable(false);
@@ -591,7 +591,7 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(3);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
@@ -608,7 +608,7 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(2);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {
@@ -768,7 +768,7 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
@@ -785,7 +785,7 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
@@ -2228,7 +2228,7 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
@@ -2245,7 +2245,7 @@ void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->_card->drawPicture(page);
+	_vm->getCurCard()->drawPicture(page);
 }
 
 uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) {
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index e0cfa36..032b059 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -263,7 +263,7 @@ void RivenGraphics::showInventory() {
 		drawInventoryImage(101, g_demoExitRect);
 	} else {
 		// We don't want to show the inventory on setup screens or in other journals.
-		if (_vm->getCurStack() == kStackAspit)
+		if (_vm->getCurStack()->getId() == kStackAspit)
 			return;
 
 		// There are three books and three vars. We have three different
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 766ad30..90ca25d 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -403,7 +403,7 @@ Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &desc
 	Common::String filename = buildSaveFilename(slot);
 
 	// Convert class variables to variable numbers
-	_vm->_vars["currentstackid"] = _vm->getCurStack();
+	_vm->_vars["currentstackid"] = _vm->getCurStack()->getId();
 	_vm->_vars["currentcardid"] = _vm->getCurCard()->getId();
 
 	Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 18c666d..f6faff0 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -393,7 +393,7 @@ void RivenSimpleCommand::stopSound(uint16 op, uint16 argc, uint16 *argv) {
 	// would cause all ambient sounds not to play. An alternative
 	// fix would be to stop all scripts on a stack change, but this
 	// does fine for now.
-	if (_vm->getCurStack() == kStackTspit && (_vm->getCurCardRMAP() == 0x6e9a || _vm->getCurCardRMAP() == 0xfeeb))
+	if (_vm->getCurStack()->getId() == kStackTspit && (_vm->getCurCardRMAP() == 0x6e9a || _vm->getCurCardRMAP() == 0xfeeb))
 		return;
 
 	// The argument is a bitflag for the setting.
@@ -579,7 +579,7 @@ void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
 void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 	// WORKAROUND: Disable the SLST that is played during Riven's intro.
 	// Riven X does this too (spoke this over with Jeff)
-	if (_vm->getCurStack() == kStackTspit && _vm->getCurCardRMAP() == 0x6e9a && argv[0] == 2)
+	if (_vm->getCurStack()->getId() == kStackTspit && _vm->getCurCardRMAP() == 0x6e9a && argv[0] == 2)
 		return;
 
 	_vm->_activatedSLST = true;
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index 3ff34e7..77f7a99 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -27,6 +27,20 @@
 
 namespace Mohawk {
 
+RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
+		_vm(vm),
+		_id(id) {
+
+}
+
+RivenStack::~RivenStack() {
+
+}
+
+uint16 RivenStack::getId() const {
+	return _id;
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index ca513a1..331a082 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -28,6 +28,28 @@
 namespace Mohawk {
 
 class MohawkEngine_Riven;
+class RivenNameList;
+
+/**
+ * A game level
+ *
+ * The names Card and Stack are legacy from the HyperCard engine used in
+ * the original mac version of Myst.
+ *
+ * Stacks contain behaviors that are specific to a game level.
+ */
+class RivenStack {
+public:
+	RivenStack(MohawkEngine_Riven *vm, uint16 id);
+	virtual ~RivenStack();
+
+	/** Get the id of the stack */
+	uint16 getId() const;
+private:
+	MohawkEngine_Riven *_vm;
+
+	uint16 _id;
+};
 
 /**
  * Name lists provide bidirectional association between an object's name and its id


Commit: 3c8decec0a416d874ae6db93c7e4977ec36432fb
    https://github.com/scummvm/scummvm/commit/3c8decec0a416d874ae6db93c7e4977ec36432fb
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the resource names to RivenStack

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_saveload.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h
    engines/mohawk/riven_vars.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 975c4c2..fbf4c23 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -44,6 +44,7 @@
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_external.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_stack.h"
 #endif
 
 namespace Mohawk {
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index f42c29d..70f4ab5 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -36,6 +36,7 @@
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_saveload.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_stack.h"
 #include "mohawk/dialogs.h"
 #include "mohawk/video.h"
 #include "mohawk/console.h"
@@ -305,9 +306,6 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 	if (_stack && _stack->getId() == n && !_mhk.empty())
 		return;
 
-	delete _stack;
-	_stack = new RivenStack(this, n);
-
 	// Stop any videos playing
 	_video->stopVideos();
 	_video->clearMLST();
@@ -338,15 +336,11 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 	if (_mhk.empty())
 		error("Could not load stack %s", getStackName(n).c_str());
 
-	// Load stack specific names
-	_varNames = RivenNameList(this, kVariableNames);
-	_externalCommandNames = RivenNameList(this, kExternalCommandNames);
-	_stackNames = RivenNameList(this, kStackNames);
-	_cardNames = RivenNameList(this, kCardNames);
-	_hotspotNames = RivenNameList(this, kHotspotNames);
-
 	// Stop any currently playing sounds
 	_sound->stopAllSLST();
+
+	delete _stack;
+	_stack = new RivenStack(this, n);
 }
 
 // Riven uses some hacks to change stacks for linking books
@@ -828,7 +822,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 }
 
 void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
-	Common::String cardName = getName(kCardNames, cardNameId);
+	Common::String cardName = getCurStack()->getName(kCardNames, cardNameId);
 	if (cardName.empty())
 		return;
 	ZipMode zip;
@@ -851,40 +845,6 @@ bool MohawkEngine_Riven::isZipVisitedCard(const Common::String &hotspotName) con
 	return foundMatch;
 }
 
-Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) {
-	switch (nameResource) {
-		case kVariableNames:
-			return _varNames.getName(nameID);
-		case kExternalCommandNames:
-			return _externalCommandNames.getName(nameID);
-		case kStackNames:
-			return _stackNames.getName(nameID);
-		case kCardNames:
-			return _cardNames.getName(nameID);
-		case kHotspotNames:
-			return _hotspotNames.getName(nameID);
-		default:
-			error("Unknown name resource %d", nameResource);
-	}
-}
-
-int16 MohawkEngine_Riven::getIdFromName(uint16 nameResource, const Common::String &name) {
-	switch (nameResource) {
-		case kVariableNames:
-			return _varNames.getNameId(name);
-		case kExternalCommandNames:
-			return _externalCommandNames.getNameId(name);
-		case kStackNames:
-			return _stackNames.getNameId(name);
-		case kCardNames:
-			return _cardNames.getNameId(name);
-		case kHotspotNames:
-			return _hotspotNames.getNameId(name);
-		default:
-			error("Unknown name resource %d", nameResource);
-	}
-}
-
 bool ZipMode::operator== (const ZipMode &z) const {
 	return z.name == name && z.id == id;
 }
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index b8203a5..3fd1905 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -25,7 +25,6 @@
 
 #include "mohawk/installer_archive.h"
 #include "mohawk/mohawk.h"
-#include "mohawk/riven_stack.h"
 #include "mohawk/riven_scripts.h"
 
 #include "common/hashmap.h"
@@ -42,6 +41,7 @@ class RivenExternal;
 class RivenConsole;
 class RivenSaveLoad;
 class RivenOptionsDialog;
+class RivenStack;
 class RivenCard;
 class RivenHotspot;
 class RivenSoundManager;
@@ -62,15 +62,6 @@ enum {
 	kStackLast = kStackAspit
 };
 
-// NAME Resource ID's
-enum {
-	kCardNames = 1,
-	kHotspotNames = 2,
-	kExternalCommandNames = 3,
-	kVariableNames = 4,
-	kStackNames = 5
-};
-
 enum RivenTransitionSpeed {
 	kRivenTransitionSpeedNone = 5000,
 	kRivenTransitionSpeedFastest = 5001,
@@ -134,13 +125,6 @@ private:
 	RivenStack *_stack;
 	void handleEvents();
 
-	// Stack resource names
-	RivenNameList _varNames;
-	RivenNameList _externalCommandNames;
-	RivenNameList _hotspotNames;
-	RivenNameList _cardNames;
-	RivenNameList _stackNames;
-
 	// Hotspot related functions and variables
 	void checkInventoryClick();
 	bool _showHotspots;
@@ -161,8 +145,6 @@ public:
 	void changeToCard(uint16 dest);
 	void changeToStack(uint16);
 	void refreshCard();
-	Common::String getName(uint16 nameResource, uint16 nameID);
-	int16 getIdFromName(uint16 nameResource, const Common::String &name);
 	Common::String getStackName(uint16 stack) const;
 	RivenCard *getCurCard() const { return _card; }
 	RivenStack *getCurStack() const { return _stack; }
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 402c10b..a5c64f2 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -24,6 +24,7 @@
 
 #include "mohawk/cursors.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_stack.h"
 
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
@@ -261,7 +262,7 @@ Common::Array<RivenHotspot *> RivenCard::getHotspots() const {
 }
 
 RivenHotspot *RivenCard::getHotspotByName(const Common::String &name) const {
-	int16 nameId = _vm->getIdFromName(kHotspotNames, name);
+	int16 nameId = _vm->getCurStack()->getIdFromName(kHotspotNames, name);
 
 	for (uint i = 0; i < _hotspots.size(); i++) {
 		if (_hotspots[i]->getNameId() == nameId) {
@@ -509,7 +510,7 @@ Common::String RivenHotspot::getName() const {
 	if (_nameResource < 0)
 		return Common::String();
 
-	return _vm->getName(kHotspotNames, _nameResource);
+	return _vm->getCurStack()->getName(kHotspotNames, _nameResource);
 }
 
 uint16 RivenHotspot::getIndex() const {
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index f021ca6..11f0b96 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven_external.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_stack.h"
 #include "mohawk/video.h"
 
 #include "gui/message.h"
@@ -198,7 +199,7 @@ void RivenExternal::setupCommands() {
 }
 
 void RivenExternal::runCommand(uint16 argc, uint16 *argv) {
-	Common::String externalCommandName = _vm->getName(kExternalCommandNames, argv[0]);
+	Common::String externalCommandName = _vm->getCurStack()->getName(kExternalCommandNames, argv[0]);
 
 	for (uint16 i = 0; i < _externalCommands.size(); i++)
 		if (externalCommandName == _externalCommands[i]->desc) {
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 032b059..9346ae9 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -25,6 +25,7 @@
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_stack.h"
 
 #include "common/system.h"
 #include "engines/util.h"
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 90ca25d..6be876d 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -24,6 +24,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_saveload.h"
+#include "mohawk/riven_stack.h"
 
 #include "common/system.h"
 #include "graphics/thumbnail.h"
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index f6faff0..cfc97bf 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -27,6 +27,7 @@
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_scripts.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_stack.h"
 #include "mohawk/video.h"
 
 #include "common/memstream.h"
@@ -462,7 +463,7 @@ void RivenSimpleCommand::incrementVariable(uint16 op, uint16 argc, uint16 *argv)
 
 // Command 27: go to stack (stack name, code high, code low)
 void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
-	Common::String stackName = _vm->getName(kStackNames, argv[0]);
+	Common::String stackName = _vm->getCurStack()->getName(kStackNames, argv[0]);
 	int8 index = -1;
 
 	for (byte i = 0; i < 8; i++)
@@ -629,10 +630,10 @@ void RivenSimpleCommand::dump(byte tabs) {
 	printTabs(tabs);
 
 	if (_type == 7) { // Use the variable name
-		Common::String varName = _vm->getName(kVariableNames, _arguments[0]);
+		Common::String varName = _vm->getCurStack()->getName(kVariableNames, _arguments[0]);
 		debugN("%s = %d;\n", varName.c_str(), _arguments[1]);
 	} else if (_type == 17) { // Use the external command name
-		Common::String externalCommandName = _vm->getName(kVariableNames, _arguments[0]);
+		Common::String externalCommandName = _vm->getCurStack()->getName(kVariableNames, _arguments[0]);
 		debugN("%s(", externalCommandName.c_str());
 		uint16 varCount = _arguments[1];
 		for (uint16 j = 0; j < varCount; j++) {
@@ -642,7 +643,7 @@ void RivenSimpleCommand::dump(byte tabs) {
 		}
 		debugN(");\n");
 	} else if (_type == 24) { // Use the variable name
-		Common::String varName = _vm->getName(kVariableNames, _arguments[0]);
+		Common::String varName = _vm->getCurStack()->getName(kVariableNames, _arguments[0]);
 		debugN("%s += %d;\n", varName.c_str(), _arguments[1]);
 	} else {
 		debugN("%s(", _opcodes[_type].desc);
@@ -704,7 +705,7 @@ RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm,
 }
 
 void RivenSwitchCommand::dump(byte tabs) {
-	Common::String varName = _vm->getName(kVariableNames, _variableId);
+	Common::String varName = _vm->getCurStack()->getName(kVariableNames, _variableId);
 	printTabs(tabs); debugN("switch (%s) {\n", varName.c_str());
 	for (uint16 j = 0; j < _branches.size(); j++) {
 		printTabs(tabs + 1);
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index 77f7a99..b66ca83 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -30,7 +30,7 @@ namespace Mohawk {
 RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
 		_vm(vm),
 		_id(id) {
-
+	loadResourceNames();
 }
 
 RivenStack::~RivenStack() {
@@ -41,6 +41,48 @@ uint16 RivenStack::getId() const {
 	return _id;
 }
 
+void RivenStack::loadResourceNames() {
+	_varNames = RivenNameList(_vm, kVariableNames);
+	_externalCommandNames = RivenNameList(_vm, kExternalCommandNames);
+	_stackNames = RivenNameList(_vm, kStackNames);
+	_cardNames = RivenNameList(_vm, kCardNames);
+	_hotspotNames = RivenNameList(_vm, kHotspotNames);
+}
+
+Common::String RivenStack::getName(RivenNameResource nameResource, uint16 nameId) const {
+	switch (nameResource) {
+		case kVariableNames:
+			return _varNames.getName(nameId);
+		case kExternalCommandNames:
+			return _externalCommandNames.getName(nameId);
+		case kStackNames:
+			return _stackNames.getName(nameId);
+		case kCardNames:
+			return _cardNames.getName(nameId);
+		case kHotspotNames:
+			return _hotspotNames.getName(nameId);
+		default:
+			error("Unknown name resource %d", nameResource);
+	}
+}
+
+int16 RivenStack::getIdFromName(RivenNameResource nameResource, const Common::String &name) const {
+	switch (nameResource) {
+		case kVariableNames:
+			return _varNames.getNameId(name);
+		case kExternalCommandNames:
+			return _externalCommandNames.getNameId(name);
+		case kStackNames:
+			return _stackNames.getNameId(name);
+		case kCardNames:
+			return _cardNames.getNameId(name);
+		case kHotspotNames:
+			return _hotspotNames.getNameId(name);
+		default:
+			error("Unknown name resource %d", nameResource);
+	}
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 331a082..36d6fb0 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -28,27 +28,14 @@
 namespace Mohawk {
 
 class MohawkEngine_Riven;
-class RivenNameList;
 
-/**
- * A game level
- *
- * The names Card and Stack are legacy from the HyperCard engine used in
- * the original mac version of Myst.
- *
- * Stacks contain behaviors that are specific to a game level.
- */
-class RivenStack {
-public:
-	RivenStack(MohawkEngine_Riven *vm, uint16 id);
-	virtual ~RivenStack();
-
-	/** Get the id of the stack */
-	uint16 getId() const;
-private:
-	MohawkEngine_Riven *_vm;
-
-	uint16 _id;
+// NAME Resource ID's
+enum RivenNameResource {
+	kCardNames = 1,
+	kHotspotNames = 2,
+	kExternalCommandNames = 3,
+	kVariableNames = 4,
+	kStackNames = 5
 };
 
 /**
@@ -71,10 +58,50 @@ public:
 	int16 getNameId(const Common::String &name) const;
 
 private:
+	void loadResource(MohawkEngine_Riven *vm, uint16 id);
+
 	Common::StringArray _names;
 	Common::Array<uint16> _index;
+};
 
-	void loadResource(MohawkEngine_Riven *vm, uint16 id);
+/**
+ * A game level
+ *
+ * The names Card and Stack are legacy from the HyperCard engine used in
+ * the original mac version of Myst.
+ *
+ * Stacks contain behaviors and data that are specific to a game level.
+ */
+class RivenStack {
+public:
+	RivenStack(MohawkEngine_Riven *vm, uint16 id);
+	virtual ~RivenStack();
+
+	/** Get the id of the stack */
+	uint16 getId() const;
+
+	/** Get the name of a resource using its id */
+	Common::String getName(RivenNameResource nameResource, uint16 nameId) const;
+
+	/**
+	 * Get the id of a resource using its name
+	 *
+	 * The search is case insensitive.
+	 */
+	int16 getIdFromName(RivenNameResource nameResource, const Common::String &name) const;
+private:
+	void loadResourceNames();
+
+	MohawkEngine_Riven *_vm;
+
+	uint16 _id;
+
+	// Stack resource names
+	RivenNameList _varNames;
+	RivenNameList _externalCommandNames;
+	RivenNameList _hotspotNames;
+	RivenNameList _cardNames;
+	RivenNameList _stackNames;
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index c45b464..2a19282 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -23,6 +23,7 @@
 #include "common/str.h"
 
 #include "mohawk/riven.h"
+#include "mohawk/riven_stack.h"
 
 namespace Mohawk {
 
@@ -268,7 +269,7 @@ static const char *variableNames[] = {
 };
 
 uint32 &MohawkEngine_Riven::getStackVar(uint32 index) {
-	Common::String name = getName(kVariableNames, index);
+	Common::String name = getCurStack()->getName(kVariableNames, index);
 
 	if (!_vars.contains(name))
 		error("Could not find variable '%s' (stack variable %d)", name.c_str(), index);


Commit: 670a3c4558f3c2c7e7c0d84d95b906e6fe0ce804
    https://github.com/scummvm/scummvm/commit/670a3c4558f3c2c7e7c0d84d95b906e6fe0ce804
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move card id remapping to RivenStack

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index fbf4c23..e28babd 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -630,7 +630,7 @@ bool RivenConsole::Cmd_ListZipCards(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_GetRMAP(int argc, const char **argv) {
-	uint32 rmapCode = _vm->getCurCardRMAP();
+	uint32 rmapCode = _vm->getCurStack()->getCurrentCardGlobalId();
 	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getCurStack()->getId()).c_str(), _vm->getCurCard()->getId(), rmapCode);
 	return true;
 }
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 70f4ab5..9a60384 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -377,9 +377,10 @@ void MohawkEngine_Riven::changeToCard(uint16 dest) {
 
 	if (!(getFeatures() & GF_DEMO)) {
 		for (byte i = 0; i < 13; i++)
-			if (_stack->getId() == rivenSpecialChange[i].startStack && dest == matchRMAPToCard(rivenSpecialChange[i].startCardRMAP)) {
+			if (_stack->getId() == rivenSpecialChange[i].startStack && dest == _stack->getCardStackId(
+					rivenSpecialChange[i].startCardRMAP)) {
 				changeToStack(rivenSpecialChange[i].targetStack);
-				dest = matchRMAPToCard(rivenSpecialChange[i].targetCardRMAP);
+				dest = _stack->getCardStackId(rivenSpecialChange[i].targetCardRMAP);
 			}
 	}
 
@@ -486,32 +487,6 @@ Common::SeekableReadStream *MohawkEngine_Riven::getExtrasResource(uint32 tag, ui
 	return _extrasFile->getResource(tag, id);
 }
 
-uint16 MohawkEngine_Riven::matchRMAPToCard(uint32 rmapCode) {
-	uint16 index = 0;
-	Common::SeekableReadStream *rmapStream = getResource(ID_RMAP, 1);
-
-	for (uint16 i = 1; rmapStream->pos() < rmapStream->size(); i++) {
-		uint32 code = rmapStream->readUint32BE();
-		if (code == rmapCode)
-			index = i;
-	}
-
-	delete rmapStream;
-
-	if (!index)
-		error ("Could not match RMAP code %08x", rmapCode);
-
-	return index - 1;
-}
-
-uint32 MohawkEngine_Riven::getCurCardRMAP() {
-	Common::SeekableReadStream *rmapStream = getResource(ID_RMAP, 1);
-	rmapStream->seek(_card->getId() * 4);
-	uint32 rmapCode = rmapStream->readUint32BE();
-	delete rmapStream;
-	return rmapCode;
-}
-
 void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
 	uint32 startTime = _system->getMillis();
 
@@ -760,7 +735,7 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) {
 }
 
 void MohawkEngine_Riven::installCardTimer() {
-	switch (getCurCardRMAP()) {
+	switch (_stack->getCurrentCardGlobalId()) {
 	case 0x3a85: // Top of elevator on prison island
 		// Handle Catherine hardcoded videos
 		installTimer(&catherineIdleTimer, _rnd->getRandomNumberRng(1, 33) * 1000);
@@ -803,7 +778,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 	if (sunners != 0)
 		return;
 
-	uint32 rmapCode = getCurCardRMAP();
+	uint32 rmapCode = _stack->getCurrentCardGlobalId();
 
 	// This is only for the mid/lower staircase sections
 	if (rmapCode != 0x79bd && rmapCode != 0x7beb)
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 3fd1905..03788b5 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -148,8 +148,6 @@ public:
 	Common::String getStackName(uint16 stack) const;
 	RivenCard *getCurCard() const { return _card; }
 	RivenStack *getCurStack() const { return _stack; }
-	uint16 matchRMAPToCard(uint32);
-	uint32 getCurCardRMAP();
 
 	// Hotspot functions/variables
 	Common::Array<ZipMode> _zipModeData;
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 11f0b96..cf752d2 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -1734,12 +1734,12 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();                      // Update
 	_vm->_video->playMovieBlockingRiven(1);            // Play handle movie
 	_vm->_gfx->scheduleTransition(15);                 // Set pan down transition
-	_vm->changeToCard(_vm->matchRMAPToCard(0x18e77));  // Change to card facing up
+	_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x18e77));  // Change to card facing up
 	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor (again)
 	_vm->_system->updateScreen();                      // Update
 	_vm->_video->playMovieBlockingRiven(4);            // Play carriage beginning to drop
 	_vm->_gfx->scheduleTransition(14);                 // Set pan up transition
-	_vm->changeToCard(_vm->matchRMAPToCard(0x183a9));  // Change to card looking straight again
+	_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x183a9));  // Change to card looking straight again
 	_vm->_video->playMovieBlockingRiven(2);
 
 	if (_vm->_vars["jgallows"] == 1) {
@@ -1774,16 +1774,16 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
 
 	if (gotClick) {
 		_vm->_gfx->scheduleTransition(16);                 // Schedule dissolve transition
-		_vm->changeToCard(_vm->matchRMAPToCard(0x18d4d));  // Move forward
+		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x18d4d));  // Move forward
 		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 		_vm->_system->updateScreen();                      // Update
 		_vm->_system->delayMillis(500);                    // Delay a half second before changing again
 		_vm->_gfx->scheduleTransition(12);                 // Schedule pan left transition
-		_vm->changeToCard(_vm->matchRMAPToCard(0x18ab5));  // Turn right
+		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x18ab5));  // Turn right
 		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 		_vm->_system->updateScreen();                      // Update
 		_vm->_video->playMovieBlockingRiven(1);            // Play carriage ride movie
-		_vm->changeToCard(_vm->matchRMAPToCard(0x17167));  // We have arrived at the top
+		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x17167));  // We have arrived at the top
 	} else
 		_vm->_video->playMovieBlockingRiven(3);            // Too slow!
 }
@@ -1849,7 +1849,7 @@ void RivenExternal::xhandlecontrolup(uint16 argc, uint16 *argv) {
 	if (changeLevel == -1) {
 		_vm->_video->playMovieBlockingRiven(1);
 		_vm->_video->playMovieBlockingRiven(2);
-		_vm->changeToCard(_vm->matchRMAPToCard(0x1e374));
+		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e374));
 	}
 }
 
@@ -1860,7 +1860,7 @@ void RivenExternal::xhandlecontroldown(uint16 argc, uint16 *argv) {
 	if (changeLevel == 1) {
 		_vm->_video->playMovieBlockingRiven(1);
 		_vm->_video->playMovieBlockingRiven(2);
-		_vm->changeToCard(_vm->matchRMAPToCard(0x1e374));
+		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e374));
 	}
 }
 
@@ -1887,10 +1887,10 @@ void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {
 	// Play the elevator video and then change the card
 	if (changeLevel == 1) {
 		_vm->_video->playMovieBlockingRiven(5);
-		_vm->changeToCard(_vm->matchRMAPToCard(0x1e597));
+		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e597));
 	} else {
 		_vm->_video->playMovieBlockingRiven(4);
-		_vm->changeToCard(_vm->matchRMAPToCard(0x1e29c));
+		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e29c));
 	}
 }
 
@@ -2143,7 +2143,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 					_vm->_vars["agehn"] = 4;                            // Set Gehn to the trapped state
 					_vm->_vars["atrapbook"] = 1;                        // We've got the trap book again
 					_vm->_sound->playSound(0);                          // Play the link sound again
-					_vm->changeToCard(_vm->matchRMAPToCard(0x2885));    // Link out!
+					_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x2885));    // Link out!
 					return;
 				}
 				break;
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index cfc97bf..3155c43 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -394,7 +394,8 @@ void RivenSimpleCommand::stopSound(uint16 op, uint16 argc, uint16 *argv) {
 	// would cause all ambient sounds not to play. An alternative
 	// fix would be to stop all scripts on a stack change, but this
 	// does fine for now.
-	if (_vm->getCurStack()->getId() == kStackTspit && (_vm->getCurCardRMAP() == 0x6e9a || _vm->getCurCardRMAP() == 0xfeeb))
+	if (_vm->getCurStack()->getId() == kStackTspit && (_vm->getCurStack()->getCurrentCardGlobalId() == 0x6e9a ||
+			_vm->getCurStack()->getCurrentCardGlobalId() == 0xfeeb))
 		return;
 
 	// The argument is a bitflag for the setting.
@@ -477,7 +478,7 @@ void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
 
 	_vm->changeToStack(index);
 	uint32 rmapCode = (argv[1] << 16) + argv[2];
-	uint16 cardID = _vm->matchRMAPToCard(rmapCode);
+	uint16 cardID = _vm->getCurStack()->getCardStackId(rmapCode);
 	_vm->changeToCard(cardID);
 }
 
@@ -580,7 +581,7 @@ void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
 void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 	// WORKAROUND: Disable the SLST that is played during Riven's intro.
 	// Riven X does this too (spoke this over with Jeff)
-	if (_vm->getCurStack()->getId() == kStackTspit && _vm->getCurCardRMAP() == 0x6e9a && argv[0] == 2)
+	if (_vm->getCurStack()->getId() == kStackTspit && _vm->getCurStack()->getCurrentCardGlobalId() == 0x6e9a && argv[0] == 2)
 		return;
 
 	_vm->_activatedSLST = true;
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index b66ca83..54846f1 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -23,6 +23,7 @@
 #include "mohawk/riven_stack.h"
 
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/resource.h"
 
 namespace Mohawk {
@@ -31,6 +32,7 @@ RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
 		_vm(vm),
 		_id(id) {
 	loadResourceNames();
+	loadCardIdMap();
 }
 
 RivenStack::~RivenStack() {
@@ -83,6 +85,37 @@ int16 RivenStack::getIdFromName(RivenNameResource nameResource, const Common::St
 	}
 }
 
+void RivenStack::loadCardIdMap() {
+	Common::SeekableReadStream *rmapStream = _vm->getResource(ID_RMAP, 1);
+
+	uint count = rmapStream->size() / sizeof(uint32);
+	_cardIdMap.resize(count);
+
+	for (uint i = 0; i < count; i++) {
+		_cardIdMap[i] = rmapStream->readUint32BE();
+	}
+
+	delete rmapStream;
+}
+
+uint16 RivenStack::getCardStackId(uint32 globalId) const {
+	int16 index = -1;
+
+	for (uint16 i = 0; i < _cardIdMap.size(); i++) {
+		if (_cardIdMap[i] == globalId)
+			index = i;
+	}
+
+	if (index < 0)
+		error ("Could not match RMAP code %08x", globalId);
+
+	return index;
+}
+
+uint32 RivenStack::getCurrentCardGlobalId() const {
+	return _cardIdMap[_vm->getCurCard()->getId()];
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 36d6fb0..02fb58c 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -89,8 +89,15 @@ public:
 	 * The search is case insensitive.
 	 */
 	int16 getIdFromName(RivenNameResource nameResource, const Common::String &name) const;
+
+	/** Get the id of a card in the card from its global identifier */
+	uint16 getCardStackId(uint32 globalId) const;
+
+	/** Get the global id of the currently active card */
+	uint32 getCurrentCardGlobalId() const;
 private:
 	void loadResourceNames();
+	void loadCardIdMap();
 
 	MohawkEngine_Riven *_vm;
 
@@ -102,6 +109,8 @@ private:
 	RivenNameList _hotspotNames;
 	RivenNameList _cardNames;
 	RivenNameList _stackNames;
+
+	Common::Array<uint32> _cardIdMap;
 };
 
 } // End of namespace Mohawk


Commit: 05bed84a859589466e12c0a62311ce6959380d2b
    https://github.com/scummvm/scummvm/commit/05bed84a859589466e12c0a62311ce6959380d2b
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Rename the card and stack accessors

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_saveload.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_vars.cpp


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index e28babd..417d9dd 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -412,7 +412,7 @@ bool RivenConsole::Cmd_ChangeCard(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_CurCard(int argc, const char **argv) {
-	debugPrintf("Current Card: %d\n", _vm->getCurCard()->getId());
+	debugPrintf("Current Card: %d\n", _vm->getCard()->getId());
 
 	return true;
 }
@@ -459,7 +459,7 @@ bool RivenConsole::Cmd_PlaySLST(int argc, const char **argv) {
 	_vm->_sound->stopSound();
 	_vm->_sound->stopAllSLST();
 
-	_vm->getCurCard()->playSound((uint16)atoi(argv[1]));
+	_vm->getCard()->playSound((uint16)atoi(argv[1]));
 	return false;
 }
 
@@ -472,7 +472,7 @@ bool RivenConsole::Cmd_StopSound(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_CurStack(int argc, const char **argv) {
-	debugPrintf("Current Stack: %s\n", _vm->getStackName(_vm->getCurStack()->getId()).c_str());
+	debugPrintf("Current Stack: %s\n", _vm->getStackName(_vm->getStack()->getId()).c_str());
 
 	return true;
 }
@@ -511,9 +511,9 @@ bool RivenConsole::Cmd_ChangeStack(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
-	Common::Array<RivenHotspot *> hotspots = _vm->getCurCard()->getHotspots();
+	Common::Array<RivenHotspot *> hotspots = _vm->getCard()->getHotspots();
 
-	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCurCard()->getId(), hotspots.size());
+	debugPrintf("Current card (%d) has %d hotspots:\n", _vm->getCard()->getId(), hotspots.size());
 
 	for (uint16 i = 0; i < hotspots.size(); i++) {
 		RivenHotspot *hotspot = hotspots[i];
@@ -548,7 +548,7 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 		return true;
 	}
 
-	uint16 oldStack = _vm->getCurStack()->getId();
+	uint16 oldStack = _vm->getStack()->getId();
 	uint newStack = kStackUnknown;
 
 	for (uint i = kStackFirst; i <= kStackLast; i++) {
@@ -630,8 +630,8 @@ bool RivenConsole::Cmd_ListZipCards(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_GetRMAP(int argc, const char **argv) {
-	uint32 rmapCode = _vm->getCurStack()->getCurrentCardGlobalId();
-	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getCurStack()->getId()).c_str(), _vm->getCurCard()->getId(), rmapCode);
+	uint32 rmapCode = _vm->getStack()->getCurrentCardGlobalId();
+	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getStack()->getId()).c_str(), _vm->getCard()->getId(), rmapCode);
 	return true;
 }
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 9a60384..8b7ee88 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -588,7 +588,7 @@ static void catherineIdleTimer(MohawkEngine_Riven *vm) {
 		cathState = 1;
 
 	// Play the movie, blocking
-	vm->_video->activateMLST(movie, vm->getCurCard()->getId());
+	vm->_video->activateMLST(movie, vm->getCard()->getId());
 	vm->_cursor->hideCursor();
 	vm->_video->playMovieBlockingRiven(movie);
 	vm->_cursor->showCursor();
@@ -722,7 +722,7 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) {
 			// Unlike the other cards' scripts which automatically
 			// activate the MLST, we have to set it manually here.
 			uint16 mlstID = vm->_rnd->getRandomNumberRng(3, 8);
-			vm->_video->activateMLST(mlstID, vm->getCurCard()->getId());
+			vm->_video->activateMLST(mlstID, vm->getCard()->getId());
 			VideoHandle handle = vm->_video->playMovieRiven(mlstID);
 
 			timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
@@ -797,7 +797,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 }
 
 void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
-	Common::String cardName = getCurStack()->getName(kCardNames, cardNameId);
+	Common::String cardName = getStack()->getName(kCardNames, cardNameId);
 	if (cardName.empty())
 		return;
 	ZipMode zip;
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 03788b5..84b8f97 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -146,8 +146,8 @@ public:
 	void changeToStack(uint16);
 	void refreshCard();
 	Common::String getStackName(uint16 stack) const;
-	RivenCard *getCurCard() const { return _card; }
-	RivenStack *getCurStack() const { return _stack; }
+	RivenCard *getCard() const { return _card; }
+	RivenStack *getStack() const { return _stack; }
 
 	// Hotspot functions/variables
 	Common::Array<ZipMode> _zipModeData;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index a5c64f2..1395100 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -262,7 +262,7 @@ Common::Array<RivenHotspot *> RivenCard::getHotspots() const {
 }
 
 RivenHotspot *RivenCard::getHotspotByName(const Common::String &name) const {
-	int16 nameId = _vm->getCurStack()->getIdFromName(kHotspotNames, name);
+	int16 nameId = _vm->getStack()->getIdFromName(kHotspotNames, name);
 
 	for (uint i = 0; i < _hotspots.size(); i++) {
 		if (_hotspots[i]->getNameId() == nameId) {
@@ -510,7 +510,7 @@ Common::String RivenHotspot::getName() const {
 	if (_nameResource < 0)
 		return Common::String();
 
-	return _vm->getCurStack()->getName(kHotspotNames, _nameResource);
+	return _vm->getStack()->getName(kHotspotNames, _nameResource);
 }
 
 uint16 RivenHotspot::getIndex() const {
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index cf752d2..1a0f1e8 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -199,7 +199,7 @@ void RivenExternal::setupCommands() {
 }
 
 void RivenExternal::runCommand(uint16 argc, uint16 *argv) {
-	Common::String externalCommandName = _vm->getCurStack()->getName(kExternalCommandNames, argv[0]);
+	Common::String externalCommandName = _vm->getStack()->getName(kExternalCommandNames, argv[0]);
 
 	for (uint16 i = 0; i < _externalCommands.size(); i++)
 		if (externalCommandName == _externalCommands[i]->desc) {
@@ -314,8 +314,8 @@ void RivenExternal::resetDomeSliders(uint16 soundId, uint16 startHotspot) {
 }
 
 void RivenExternal::checkDomeSliders() {
-	RivenHotspot *resetSlidersHotspot = _vm->getCurCard()->getHotspotByName("ResetSliders");
-	RivenHotspot *openDomeHotspot = _vm->getCurCard()->getHotspotByName("OpenDome");
+	RivenHotspot *resetSlidersHotspot = _vm->getCard()->getHotspotByName("ResetSliders");
+	RivenHotspot *openDomeHotspot = _vm->getCard()->getHotspotByName("OpenDome");
 
 	// Let's see if we're all matched up...
 	if (_vm->_vars["adomecombo"] == _sliderState) {
@@ -332,7 +332,7 @@ void RivenExternal::checkDomeSliders() {
 void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
 	// Set the cursor based on _sliderState and what hotspot we're over
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + i);
+		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
 		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			if (_sliderState & (1 << (24 - i)))
 				_vm->_cursor->setCursor(kRivenOpenHandCursor);
@@ -348,7 +348,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 startHotspot) {
 	int16 foundSlider = -1;
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + i);
+		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
 		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			// If the slider is not at this hotspot, we can't do anything else
 			if (!(_sliderState & (1 << (24 - i))))
@@ -374,7 +374,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 startHotspot) {
 			switch (event.type) {
 			case Common::EVENT_MOUSEMOVE:
 				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider)))) {
-					RivenHotspot *nextHotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + foundSlider + 1);
+					RivenHotspot *nextHotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + foundSlider + 1);
 					if (nextHotspot->containsPoint(event.mouse)) {
 						// We've moved the slider right one space
 						_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
@@ -386,7 +386,7 @@ void RivenExternal::dragDomeSlider(uint16 soundId, uint16 startHotspot) {
 						drawDomeSliders(startHotspot);
 					}
 				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider)))) {
-					RivenHotspot *previousHotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + foundSlider - 1);
+					RivenHotspot *previousHotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + foundSlider - 1);
 					if (previousHotspot->containsPoint(event.mouse)) {
 						// We've moved the slider left one space
 						_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
@@ -419,14 +419,14 @@ void RivenExternal::drawDomeSliders(uint16 startHotspot) {
 
 	// On pspit, the rect is different by two pixels
 	// (alternatively, we could just use hotspot 3 here, but only on pspit is there a hotspot for this)
-	if (_vm->getCurStack()->getId() == kStackPspit)
+	if (_vm->getStack()->getId() == kStackPspit)
 		dstAreaRect.translate(-2, 0);
 
 	// Find out bitmap id
 	uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(startHotspot + i);
+		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
 
 		Common::Rect srcRect = hotspot->getRect();
 		srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
@@ -463,9 +463,9 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
 	uint32 &page = _vm->_vars["aatruspage"];
 
 	// Set hotspots depending on the page
-	RivenHotspot *openBook = _vm->getCurCard()->getHotspotByName("openBook");
-	RivenHotspot *nextPage = _vm->getCurCard()->getHotspotByName("nextpage");
-	RivenHotspot *prevPage = _vm->getCurCard()->getHotspotByName("prevpage");
+	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->getCard()->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->getCard()->getHotspotByName("prevpage");
 	if (page == 1) {
 		prevPage->enable(false);
 		nextPage->enable(false);
@@ -477,7 +477,7 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
 	}
 
 	// Draw the image of the page
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xaatrusbookback(uint16 argc, uint16 *argv) {
@@ -503,7 +503,7 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
@@ -523,7 +523,7 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
@@ -531,9 +531,9 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
 	uint32 page = _vm->_vars["acathpage"];
 
 	// Set hotspots depending on the page
-	RivenHotspot *openBook = _vm->getCurCard()->getHotspotByName("openBook");
-	RivenHotspot *nextPage = _vm->getCurCard()->getHotspotByName("nextpage");
-	RivenHotspot *prevPage = _vm->getCurCard()->getHotspotByName("prevpage");
+	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->getCard()->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->getCard()->getHotspotByName("prevpage");
 	if (page == 1) {
 		prevPage->enable(false);
 		nextPage->enable(false);
@@ -545,13 +545,13 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
 	}
 
 	// Draw the image of the page
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 
 	// Draw the white page edges
 	if (page > 1 && page < 5)
-		_vm->getCurCard()->drawPicture(50);
+		_vm->getCard()->drawPicture(50);
 	else if (page > 5)
-		_vm->getCurCard()->drawPicture(51);
+		_vm->getCard()->drawPicture(51);
 
 	if (page == 28) {
 		// Draw the telescope combination
@@ -592,7 +592,7 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(3);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
@@ -609,7 +609,7 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(2);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {
@@ -728,7 +728,7 @@ void RivenExternal::xblabopenbook(uint16 argc, uint16 *argv) {
 	uint32 page = _vm->_vars["blabpage"];
 
 	// Draw the image of the page based on the blabbook variable
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 
 	if (page == 14) {
 		// Draw the dome combination
@@ -769,7 +769,7 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
@@ -786,16 +786,16 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
 	if (_vm->_vars["bheat"] != 0)
-		_vm->getCurCard()->playSound(1);
+		_vm->getCard()->playSound(1);
 	else if (_vm->_vars["bcratergg"] != 0)
-		_vm->getCurCard()->playSound(2);
+		_vm->getCard()->playSound(2);
 	else
-		_vm->getCurCard()->playSound(3);
+		_vm->getCard()->playSound(3);
 }
 
 void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
@@ -810,60 +810,60 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
 		// Water is filling/draining from the boiler
 		if (water == 0) {
 			if (platform == 1)
-				_vm->_video->activateMLST(12, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(12, _vm->getCard()->getId());
 			else
-				_vm->_video->activateMLST(10, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(10, _vm->getCard()->getId());
 		} else if (heat == 1) {
 			if (platform == 1)
-				_vm->_video->activateMLST(22, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(22, _vm->getCard()->getId());
 			else
-				_vm->_video->activateMLST(19, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(19, _vm->getCard()->getId());
 		} else {
 			if (platform == 1)
-				_vm->_video->activateMLST(16, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(16, _vm->getCard()->getId());
 			else
-				_vm->_video->activateMLST(13, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(13, _vm->getCard()->getId());
 		}
 	} else if (argv[0] == 2 && water != 0) {
 		if (heat == 1) {
 			// Turning on the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(23, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(23, _vm->getCard()->getId());
 			else
-				_vm->_video->activateMLST(20, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(20, _vm->getCard()->getId());
 		} else {
 			// Turning off the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(18, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(18, _vm->getCard()->getId());
 			else
-				_vm->_video->activateMLST(15, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(15, _vm->getCard()->getId());
 		}
 	} else if (argv[0] == 3) {
 		if (platform == 1) {
 			// Lowering the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(24, _vm->getCurCard()->getId());
+					_vm->_video->activateMLST(24, _vm->getCard()->getId());
 				else
-					_vm->_video->activateMLST(17, _vm->getCurCard()->getId());
+					_vm->_video->activateMLST(17, _vm->getCard()->getId());
 			} else
-				_vm->_video->activateMLST(11, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(11, _vm->getCard()->getId());
 		} else {
 			// Raising the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(21, _vm->getCurCard()->getId());
+					_vm->_video->activateMLST(21, _vm->getCard()->getId());
 				else
-					_vm->_video->activateMLST(14, _vm->getCurCard()->getId());
+					_vm->_video->activateMLST(14, _vm->getCard()->getId());
 			} else
-				_vm->_video->activateMLST(9, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(9, _vm->getCard()->getId());
 		}
 	}
 
 	if (argc > 1)
-		_vm->getCurCard()->playSound(argv[1]);
+		_vm->getCard()->playSound(argv[1]);
 	else if (argv[0] == 2)
-		_vm->getCurCard()->playSound(1);
+		_vm->getCard()->playSound(1);
 
 	_vm->_cursor->setCursor(kRivenHideCursor);
 	_vm->_video->playMovieBlockingRiven(11);
@@ -872,10 +872,10 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
 void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
 	if (_vm->_vars["bheat"] != 0) {
 		if (_vm->_vars["bblrgrt"] == 0) {
-			_vm->_video->activateMLST(8, _vm->getCurCard()->getId());
+			_vm->_video->activateMLST(8, _vm->getCard()->getId());
 			_vm->_video->playMovieRiven(8);
 		} else {
-			_vm->_video->activateMLST(7, _vm->getCurCard()->getId());
+			_vm->_video->activateMLST(7, _vm->getCard()->getId());
 			_vm->_video->playMovieRiven(7);
 		}
 	} else {
@@ -966,13 +966,13 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
 	_vm->_cursor->setCursor(kRivenMainCursor);
 	_vm->_system->updateScreen();
 
-	RivenHotspot *bait = _vm->getCurCard()->getHotspotByBlstId(9);
-	RivenHotspot *baitPlate = _vm->getCurCard()->getHotspotByBlstId(16);
+	RivenHotspot *bait = _vm->getCard()->getHotspotByBlstId(9);
+	RivenHotspot *baitPlate = _vm->getCard()->getHotspotByBlstId(16);
 
 	// Set the bait if we put it on the plate
 	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
-		_vm->getCurCard()->drawPicture(4);
+		_vm->getCard()->drawPicture(4);
 
 		bait->enable(false); // Disable bait hotspot
 		baitPlate->enable(true); // Enable baitplate hotspot
@@ -996,18 +996,18 @@ void RivenExternal::xbfreeytram(uint16 argc, uint16 *argv) {
 	}
 
 	// Activate the MLST and play the video
-	_vm->_video->activateMLST(mlstId, _vm->getCurCard()->getId());
+	_vm->_video->activateMLST(mlstId, _vm->getCard()->getId());
 	_vm->_video->playMovieBlockingRiven(11);
 
 	// Now play the second movie
-	_vm->_video->activateMLST(mlstId + 5, _vm->getCurCard()->getId());
+	_vm->_video->activateMLST(mlstId + 5, _vm->getCard()->getId());
 	_vm->_video->playMovieBlockingRiven(12);
 }
 
 void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	// Remove the pellet from the plate and put it in your hand
 	_vm->_cursor->setCursor(kRivenPelletCursor);
-	_vm->getCurCard()->drawPicture(3);
+	_vm->getCard()->drawPicture(3);
 
 	// Loop until the player lets go (or quits)
 	Common::Event event;
@@ -1029,13 +1029,13 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
 	_vm->_cursor->setCursor(kRivenMainCursor);
 	_vm->_system->updateScreen();
 
-	RivenHotspot *bait = _vm->getCurCard()->getHotspotByBlstId(9);
-	RivenHotspot *baitPlate = _vm->getCurCard()->getHotspotByBlstId(16);
+	RivenHotspot *bait = _vm->getCard()->getHotspotByBlstId(9);
+	RivenHotspot *baitPlate = _vm->getCard()->getHotspotByBlstId(16);
 
 	// Set the bait if we put it on the plate, remove otherwise
 	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 		_vm->_vars["bbait"] = 1;
-		_vm->getCurCard()->drawPicture(4);
+		_vm->getCard()->drawPicture(4);
 		bait->enable(false); // Disable bait hotspot
 		baitPlate->enable(true); // Enable baitplate hotspot
 	} else {
@@ -1219,7 +1219,7 @@ void RivenExternal::xgrotatepins(uint16 argc, uint16 *argv) {
 void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
 	// Handle a click on a section of an island
 
-	RivenHotspot *panel = _vm->getCurCard()->getHotspotByBlstId(13);
+	RivenHotspot *panel = _vm->getCard()->getHotspotByBlstId(13);
 
 	// Get our mouse position and adjust it to the beginning of the hotspot
 	Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
@@ -1369,7 +1369,7 @@ void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) {
 	}
 
 	// Calculate how much we're moving
-	Common::String buttonName = _vm->getCurCard()->getCurHotspot()->getName();
+	Common::String buttonName = _vm->getCard()->getCurHotspot()->getName();
 	uint32 buttonPos = buttonName.lastChar() - '0';
 
 	uint32 &curPos = _vm->_vars["grviewpos"];
@@ -1410,19 +1410,19 @@ void RivenExternal::xgplaywhark(uint16 argc, uint16 *argv) {
 	// Activate the correct video based on the amount of times we've been visited
 	switch (wharkVisits) {
 	case 1:
-		_vm->_video->activateMLST(3, _vm->getCurCard()->getId());
+		_vm->_video->activateMLST(3, _vm->getCard()->getId());
 		break;
 	case 2:
 		// One of two random videos
-		_vm->_video->activateMLST(4 + _vm->_rnd->getRandomBit(), _vm->getCurCard()->getId());
+		_vm->_video->activateMLST(4 + _vm->_rnd->getRandomBit(), _vm->getCard()->getId());
 		break;
 	case 3:
 		// One of two random videos
-		_vm->_video->activateMLST(6 + _vm->_rnd->getRandomBit(), _vm->getCurCard()->getId());
+		_vm->_video->activateMLST(6 + _vm->_rnd->getRandomBit(), _vm->getCard()->getId());
 		break;
 	case 4:
 		// Red alert! Shields online! Brace yourself for impact!
-		_vm->_video->activateMLST(8, _vm->getCurCard()->getId());
+		_vm->_video->activateMLST(8, _vm->getCard()->getId());
 		break;
 	}
 
@@ -1440,7 +1440,7 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	// (It shows the village from the middle of the lake)
 
 	// Calculate how much we're moving
-	Common::String buttonName = _vm->getCurCard()->getCurHotspot()->getName();
+	Common::String buttonName = _vm->getCard()->getCurHotspot()->getName();
 	uint32 buttonPos = buttonName.lastChar() - '0';
 
 	uint32 &curPos = _vm->_vars["glviewpos"];
@@ -1456,19 +1456,19 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	curPos = newPos % 6; // Clip it to 0-5
 
 	// And update the screen with the new image
-	_vm->getCurCard()->drawPicture(curPos + 2);
+	_vm->getCard()->drawPicture(curPos + 2);
 }
 
 void RivenExternal::xglview_villageon(uint16 argc, uint16 *argv) {
 	// Turn on the left viewer to 'village mode'
 	_vm->_vars["glview"] = 2;
-	_vm->getCurCard()->drawPicture(_vm->_vars["glviewpos"] + 2);
+	_vm->getCard()->drawPicture(_vm->_vars["glviewpos"] + 2);
 }
 
 void RivenExternal::xglview_villageoff(uint16 argc, uint16 *argv) {
 	// Turn off the left viewer when in 'village mode' (why is this external?)
 	_vm->_vars["glview"] = 0;
-	_vm->getCurCard()->drawPicture(1);
+	_vm->getCard()->drawPicture(1);
 }
 
 static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
@@ -1496,7 +1496,7 @@ static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
 		cathState = 3;
 
 	// Begin playing the new movie
-	vm->_video->activateMLST(movie, vm->getCurCard()->getId());
+	vm->_video->activateMLST(movie, vm->getCard()->getId());
 	VideoHandle videoHandle = vm->_video->playMovieRiven(30);
 
 	// Reset the timer
@@ -1537,14 +1537,14 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
 
 	// Begin playing a movie immediately if Catherine is already in the viewer
 	if (cathMovie == 8 || (cathMovie >= 13 && cathMovie <= 16)) {
-		_vm->_video->activateMLST(cathMovie, _vm->getCurCard()->getId());
+		_vm->_video->activateMLST(cathMovie, _vm->getCard()->getId());
 		VideoHandle videoHandle = _vm->_video->playMovieRiven(30);
 
 		timeUntilNextMovie = videoHandle->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
 	} else {
 		// Otherwise, just redraw the imager
 		timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000;
-		_vm->getCurCard()->drawPicture(8);
+		_vm->getCard()->drawPicture(8);
 	}
 
 	// Create the timer for the next video
@@ -1567,7 +1567,7 @@ void RivenExternal::xglview_prisonoff(uint16 argc, uint16 *argv) {
 	_vm->_cursor->showCursor();
 
 	// Redraw the viewer
-	_vm->getCurCard()->drawPicture(1);
+	_vm->getCard()->drawPicture(1);
 }
 
 // ------------------------------------------------------------------------------------
@@ -1645,19 +1645,19 @@ void RivenExternal::xjtunnel103_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 0))
-		_vm->getCurCard()->drawPicture(2);
+		_vm->getCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 1))
-		_vm->getCurCard()->drawPicture(3);
+		_vm->getCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 2))
-		_vm->getCurCard()->drawPicture(4);
+		_vm->getCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 3))
-		_vm->getCurCard()->drawPicture(5);
+		_vm->getCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 22))
-		_vm->getCurCard()->drawPicture(6);
+		_vm->getCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 23))
-		_vm->getCurCard()->drawPicture(7);
+		_vm->getCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 24))
-		_vm->getCurCard()->drawPicture(8);
+		_vm->getCard()->drawPicture(8);
 }
 
 void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
@@ -1666,21 +1666,21 @@ void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 9))
-		_vm->getCurCard()->drawPicture(2);
+		_vm->getCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 10))
-		_vm->getCurCard()->drawPicture(3);
+		_vm->getCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 11))
-		_vm->getCurCard()->drawPicture(4);
+		_vm->getCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 12))
-		_vm->getCurCard()->drawPicture(5);
+		_vm->getCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 13))
-		_vm->getCurCard()->drawPicture(6);
+		_vm->getCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 14))
-		_vm->getCurCard()->drawPicture(7);
+		_vm->getCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 15))
-		_vm->getCurCard()->drawPicture(8);
+		_vm->getCard()->drawPicture(8);
 	if (iconsDepressed & (1 << 16))
-		_vm->getCurCard()->drawPicture(9);
+		_vm->getCard()->drawPicture(9);
 }
 
 void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
@@ -1689,19 +1689,19 @@ void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 3))
-		_vm->getCurCard()->drawPicture(2);
+		_vm->getCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 4))
-		_vm->getCurCard()->drawPicture(3);
+		_vm->getCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 5))
-		_vm->getCurCard()->drawPicture(4);
+		_vm->getCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 6))
-		_vm->getCurCard()->drawPicture(5);
+		_vm->getCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 7))
-		_vm->getCurCard()->drawPicture(6);
+		_vm->getCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 8))
-		_vm->getCurCard()->drawPicture(7);
+		_vm->getCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 9))
-		_vm->getCurCard()->drawPicture(8);
+		_vm->getCard()->drawPicture(8);
 }
 
 void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
@@ -1710,21 +1710,21 @@ void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
 
 	// Now, draw which icons are depressed based on the bits of the variable
 	if (iconsDepressed & (1 << 16))
-		_vm->getCurCard()->drawPicture(2);
+		_vm->getCard()->drawPicture(2);
 	if (iconsDepressed & (1 << 17))
-		_vm->getCurCard()->drawPicture(3);
+		_vm->getCard()->drawPicture(3);
 	if (iconsDepressed & (1 << 18))
-		_vm->getCurCard()->drawPicture(4);
+		_vm->getCard()->drawPicture(4);
 	if (iconsDepressed & (1 << 19))
-		_vm->getCurCard()->drawPicture(5);
+		_vm->getCard()->drawPicture(5);
 	if (iconsDepressed & (1 << 20))
-		_vm->getCurCard()->drawPicture(6);
+		_vm->getCard()->drawPicture(6);
 	if (iconsDepressed & (1 << 21))
-		_vm->getCurCard()->drawPicture(7);
+		_vm->getCard()->drawPicture(7);
 	if (iconsDepressed & (1 << 22))
-		_vm->getCurCard()->drawPicture(8);
+		_vm->getCard()->drawPicture(8);
 	if (iconsDepressed & (1 << 23))
-		_vm->getCurCard()->drawPicture(9);
+		_vm->getCard()->drawPicture(9);
 }
 
 void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
@@ -1734,12 +1734,12 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();                      // Update
 	_vm->_video->playMovieBlockingRiven(1);            // Play handle movie
 	_vm->_gfx->scheduleTransition(15);                 // Set pan down transition
-	_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x18e77));  // Change to card facing up
+	_vm->changeToCard(_vm->getStack()->getCardStackId(0x18e77));  // Change to card facing up
 	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor (again)
 	_vm->_system->updateScreen();                      // Update
 	_vm->_video->playMovieBlockingRiven(4);            // Play carriage beginning to drop
 	_vm->_gfx->scheduleTransition(14);                 // Set pan up transition
-	_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x183a9));  // Change to card looking straight again
+	_vm->changeToCard(_vm->getStack()->getCardStackId(0x183a9));  // Change to card looking straight again
 	_vm->_video->playMovieBlockingRiven(2);
 
 	if (_vm->_vars["jgallows"] == 1) {
@@ -1774,16 +1774,16 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
 
 	if (gotClick) {
 		_vm->_gfx->scheduleTransition(16);                 // Schedule dissolve transition
-		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x18d4d));  // Move forward
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18d4d));  // Move forward
 		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 		_vm->_system->updateScreen();                      // Update
 		_vm->_system->delayMillis(500);                    // Delay a half second before changing again
 		_vm->_gfx->scheduleTransition(12);                 // Schedule pan left transition
-		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x18ab5));  // Turn right
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18ab5));  // Turn right
 		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 		_vm->_system->updateScreen();                      // Update
 		_vm->_video->playMovieBlockingRiven(1);            // Play carriage ride movie
-		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x17167));  // We have arrived at the top
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x17167));  // We have arrived at the top
 	} else
 		_vm->_video->playMovieBlockingRiven(3);            // Too slow!
 }
@@ -1849,7 +1849,7 @@ void RivenExternal::xhandlecontrolup(uint16 argc, uint16 *argv) {
 	if (changeLevel == -1) {
 		_vm->_video->playMovieBlockingRiven(1);
 		_vm->_video->playMovieBlockingRiven(2);
-		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e374));
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
 	}
 }
 
@@ -1860,7 +1860,7 @@ void RivenExternal::xhandlecontroldown(uint16 argc, uint16 *argv) {
 	if (changeLevel == 1) {
 		_vm->_video->playMovieBlockingRiven(1);
 		_vm->_video->playMovieBlockingRiven(2);
-		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e374));
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
 	}
 }
 
@@ -1887,10 +1887,10 @@ void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {
 	// Play the elevator video and then change the card
 	if (changeLevel == 1) {
 		_vm->_video->playMovieBlockingRiven(5);
-		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e597));
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e597));
 	} else {
 		_vm->_video->playMovieBlockingRiven(4);
-		_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x1e29c));
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e29c));
 	}
 }
 
@@ -1972,8 +1972,8 @@ void RivenExternal::xjschool280_resetright(uint16 argc, uint16 *argv) {
 void RivenExternal::redrawWharkNumberPuzzle(uint16 overlay, uint16 number) {
 	// Update the screen for the whark number puzzle
 	// We don't update the whole screen here because we don't want to overwrite the video data
-	_vm->getCurCard()->drawPicture(overlay);
-	_vm->getCurCard()->drawPicture(number + 1);
+	_vm->getCard()->drawPicture(overlay);
+	_vm->getCard()->drawPicture(number + 1);
 	_vm->_gfx->updateScreen(Common::Rect(80, 212, 477, 392));
 	_vm->_system->updateScreen();
 }
@@ -2028,8 +2028,8 @@ void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) {
 	}
 
 	// Enable the correct hotspots for the movement now
-	RivenHotspot *rotateLeft = _vm->getCurCard()->getHotspotByName("rotateLeft");
-	RivenHotspot *rotateRight = _vm->getCurCard()->getHotspotByName("rotateRight");
+	RivenHotspot *rotateLeft = _vm->getCard()->getHotspotByName("rotateLeft");
+	RivenHotspot *rotateRight = _vm->getCard()->getHotspotByName("rotateRight");
 	rotateLeft->enable(!rotateLeft->isEnabled());
 	rotateRight->enable(!rotateRight->isEnabled());
 
@@ -2082,7 +2082,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 
 	// Track down our hotspot
 	Common::String hotspotName = Common::String::format("touchBook%d", argv[3]);
-	RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByName(hotspotName);
+	RivenHotspot *hotspot = _vm->getCard()->getHotspotByName(hotspotName);
 	Common::Rect hotspotRect = hotspot->getRect();
 
 	debug(0, "xbookclick:");
@@ -2136,14 +2136,14 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 					_vm->_scriptMan->stopAllScripts();                  // Stop all running scripts (so we don't remain in the cage)
 					_vm->_video->stopVideos();                          // Stop all videos
 					_vm->_cursor->setCursor(kRivenHideCursor);          // Hide the cursor
-					_vm->getCurCard()->drawPicture(3);                  // Black out the screen
+					_vm->getCard()->drawPicture(3);                  // Black out the screen
 					_vm->_sound->playSound(0);                          // Play the link sound
-					_vm->_video->activateMLST(7, _vm->getCurCard()->getId());    // Activate Gehn Link Video
+					_vm->_video->activateMLST(7, _vm->getCard()->getId());    // Activate Gehn Link Video
 					_vm->_video->playMovieBlockingRiven(1);             // Play Gehn Link Video
 					_vm->_vars["agehn"] = 4;                            // Set Gehn to the trapped state
 					_vm->_vars["atrapbook"] = 1;                        // We've got the trap book again
 					_vm->_sound->playSound(0);                          // Play the link sound again
-					_vm->changeToCard(_vm->getCurStack()->getCardStackId(0x2885));    // Link out!
+					_vm->changeToCard(_vm->getStack()->getCardStackId(0x2885));    // Link out!
 					return;
 				}
 				break;
@@ -2192,9 +2192,9 @@ void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {
 	_vm->_video->playMovieBlockingRiven(1);
 
 	// Set the hotspots into their correct states
-	RivenHotspot *closeBook = _vm->getCurCard()->getHotspotByName("closeBook");
-	RivenHotspot *nullHotspot = _vm->getCurCard()->getHotspotByName("null");
-	RivenHotspot *openBook = _vm->getCurCard()->getHotspotByName("openBook");
+	RivenHotspot *closeBook = _vm->getCard()->getHotspotByName("closeBook");
+	RivenHotspot *nullHotspot = _vm->getCard()->getHotspotByName("null");
+	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
 
 	closeBook->enable(false);
 	nullHotspot->enable(false);
@@ -2212,7 +2212,7 @@ void RivenExternal::xobedroom5_closedrawer(uint16 argc, uint16 *argv) {
 }
 
 void RivenExternal::xogehnopenbook(uint16 argc, uint16 *argv) {
-	_vm->getCurCard()->drawPicture(_vm->_vars["ogehnpage"]);
+	_vm->getCard()->drawPicture(_vm->_vars["ogehnpage"]);
 }
 
 void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
@@ -2229,7 +2229,7 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(1);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
@@ -2246,7 +2246,7 @@ void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(0);
-	_vm->getCurCard()->drawPicture(page);
+	_vm->getCard()->drawPicture(page);
 }
 
 uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) {
@@ -2283,7 +2283,7 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
 	}
 
 	// Now play the video for the watch
-	_vm->_video->activateMLST(1, _vm->getCurCard()->getId());
+	_vm->_video->activateMLST(1, _vm->getCard()->getId());
 	_vm->_video->playMovieBlockingRiven(1);
 
 	// And, finally, refresh
@@ -2367,7 +2367,7 @@ void RivenExternal::xrhideinventory(uint16 argc, uint16 *argv) {
 static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) {
 	// Randomize a video out in the middle of Tay
 	uint16 movie = vm->_rnd->getRandomNumberRng(2, 13);
-	vm->_video->activateMLST(movie, vm->getCurCard()->getId());
+	vm->_video->activateMLST(movie, vm->getCard()->getId());
 	VideoHandle handle = vm->_video->playMovieRiven(movie);
 
 	// Ensure the next video starts after this one ends
@@ -2435,25 +2435,25 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
 			if (_vm->_vars["pcage"] == 2) {
 				// The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
 				// And now we fall back to Earth... all the way...
-				_vm->_video->activateMLST(8, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(8, _vm->getCard()->getId());
 				runEndGame(8, 5000);
 			} else if (_vm->_vars["agehn"] == 4) {
 				// The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
 				// Nice going! Catherine and the islanders are all dead now! Just go back to your home...
-				_vm->_video->activateMLST(9, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(9, _vm->getCard()->getId());
 				runEndGame(9, 5000);
 			} else if (_vm->_vars["atrapbook"] == 1) {
 				// The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
 				// And then you get shot by Cho. Nice going! Catherine and the islanders are dead
 				// and you have just set Gehn free from Riven, not to mention you're dead.
-				_vm->_video->activateMLST(10, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(10, _vm->getCard()->getId());
 				runEndGame(10, 5000);
 			} else {
 				// The impossible ending: You don't have Catherine's journal and yet you were somehow
 				// able to open the hatch on the telescope. The game provides an ending for those who
 				// cheat, load a saved game with the combo, or just guess the telescope combo. Atrus
 				// doesn't come and you just fall into the fissure.
-				_vm->_video->activateMLST(11, _vm->getCurCard()->getId());
+				_vm->_video->activateMLST(11, _vm->getCard()->getId());
 				runEndGame(11, 5000);
 			}
 		} else {
@@ -2523,7 +2523,7 @@ void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {
 
 	// If we have hit the correct 5 buttons in a row, activate the hotspot to open up the
 	// telescope cover.
-	RivenHotspot *openCover = _vm->getCurCard()->getHotspotByName("openCover");
+	RivenHotspot *openCover = _vm->getCard()->getHotspotByName("openCover");
 	openCover->enable(correctDigits == 5);
 }
 
@@ -2655,7 +2655,7 @@ void RivenExternal::setMarbleHotspots() {
 	// Set the hotspots
 	for (uint16 i = 0; i < kMarbleCount; i++) {
 		uint32 marblePos = _vm->_vars[s_marbleNames[i]];
-		RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
+		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
 
 		if (marblePos == 0) // In the receptacle
 			marbleHotspot->setRect(_marbleBaseHotspots[i]);
@@ -2668,7 +2668,7 @@ void RivenExternal::xt7800_setup(uint16 argc, uint16 *argv) {
 	// First, let's store the base receptacle hotspots for the marbles
 	if (_marbleBaseHotspots.empty())
 		for (uint16 i = 0; i < kMarbleCount; i++) {
-			RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
+			RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
 			_marbleBaseHotspots.push_back(marbleHotspot->getRect());
 		}
 
@@ -2683,7 +2683,7 @@ void RivenExternal::drawMarbles() {
 		if (_vm->_vars["themarble"] - 1 == i)
 			continue;
 
-		RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
+		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
 
 		Common::Rect rect = marbleHotspot->getRect();
 		// Trim the rect down a bit
@@ -2708,7 +2708,7 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
 	marble = 0;
 
 	for (uint32 i = 0; i < kMarbleCount; i++) {
-		RivenHotspot *marbleHotspot = _vm->getCurCard()->getHotspotByName(s_marbleNames[i]);
+		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
 		if (marbleHotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
 			marble = i + 1;
 			break;
@@ -2719,7 +2719,7 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
 	assert(marble != 0);
 
 	// Redraw the background
-	_vm->getCurCard()->drawPicture(1);
+	_vm->getCard()->drawPicture(1);
 
 	// Loop until the player lets go (or quits)
 	Common::Event event;
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 9346ae9..6931afb 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -264,7 +264,7 @@ void RivenGraphics::showInventory() {
 		drawInventoryImage(101, g_demoExitRect);
 	} else {
 		// We don't want to show the inventory on setup screens or in other journals.
-		if (_vm->getCurStack()->getId() == kStackAspit)
+		if (_vm->getStack()->getId() == kStackAspit)
 			return;
 
 		// There are three books and three vars. We have three different
@@ -432,7 +432,7 @@ void RivenGraphics::applyScreenUpdate(bool force) {
 	if (_screenUpdateNesting <= 0 && !_screenUpdateRunning) {
 		_screenUpdateRunning = true;
 
-		_vm->getCurCard()->runScript(kCardUpdateScript);
+		_vm->getCard()->runScript(kCardUpdateScript);
 		_vm->_sound->triggerDrawSound();
 		updateScreen();
 
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 6be876d..37b5b68 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -404,8 +404,8 @@ Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &desc
 	Common::String filename = buildSaveFilename(slot);
 
 	// Convert class variables to variable numbers
-	_vm->_vars["currentstackid"] = _vm->getCurStack()->getId();
-	_vm->_vars["currentcardid"] = _vm->getCurCard()->getId();
+	_vm->_vars["currentstackid"] = _vm->getStack()->getId();
+	_vm->_vars["currentcardid"] = _vm->getCard()->getId();
 
 	Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
 	if (!saveFile)
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 3155c43..fabb8c6 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -366,7 +366,7 @@ void RivenSimpleCommand::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 9: enable hotspot (blst_id)
 void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
-	RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(argv[0]);
+	RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(argv[0]);
 	if (hotspot) {
 		hotspot->enable(true);
 	}
@@ -377,7 +377,7 @@ void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 10: disable hotspot (blst_id)
 void RivenSimpleCommand::disableHotspot(uint16 op, uint16 argc, uint16 *argv) {
-	RivenHotspot *hotspot = _vm->getCurCard()->getHotspotByBlstId(argv[0]);
+	RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(argv[0]);
 	if (hotspot) {
 		hotspot->enable(false);
 	}
@@ -394,8 +394,8 @@ void RivenSimpleCommand::stopSound(uint16 op, uint16 argc, uint16 *argv) {
 	// would cause all ambient sounds not to play. An alternative
 	// fix would be to stop all scripts on a stack change, but this
 	// does fine for now.
-	if (_vm->getCurStack()->getId() == kStackTspit && (_vm->getCurStack()->getCurrentCardGlobalId() == 0x6e9a ||
-			_vm->getCurStack()->getCurrentCardGlobalId() == 0xfeeb))
+	if (_vm->getStack()->getId() == kStackTspit && (_vm->getStack()->getCurrentCardGlobalId() == 0x6e9a ||
+			_vm->getStack()->getCurrentCardGlobalId() == 0xfeeb))
 		return;
 
 	// The argument is a bitflag for the setting.
@@ -464,7 +464,7 @@ void RivenSimpleCommand::incrementVariable(uint16 op, uint16 argc, uint16 *argv)
 
 // Command 27: go to stack (stack name, code high, code low)
 void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
-	Common::String stackName = _vm->getCurStack()->getName(kStackNames, argv[0]);
+	Common::String stackName = _vm->getStack()->getName(kStackNames, argv[0]);
 	int8 index = -1;
 
 	for (byte i = 0; i < 8; i++)
@@ -478,7 +478,7 @@ void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
 
 	_vm->changeToStack(index);
 	uint32 rmapCode = (argv[1] << 16) + argv[2];
-	uint16 cardID = _vm->getCurStack()->getCardStackId(rmapCode);
+	uint16 cardID = _vm->getStack()->getCardStackId(rmapCode);
 	_vm->changeToCard(cardID);
 }
 
@@ -573,7 +573,7 @@ void RivenSimpleCommand::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv)
 void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_activatedPLST = true;
 
-	RivenCard::Picture picture = _vm->getCurCard()->getPicture(argv[0]);
+	RivenCard::Picture picture = _vm->getCard()->getPicture(argv[0]);
 	_vm->_gfx->copyImageToScreen(picture.id, picture.rect.left, picture.rect.top, picture.rect.right, picture.rect.bottom);
 }
 
@@ -581,23 +581,23 @@ void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
 void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 	// WORKAROUND: Disable the SLST that is played during Riven's intro.
 	// Riven X does this too (spoke this over with Jeff)
-	if (_vm->getCurStack()->getId() == kStackTspit && _vm->getCurStack()->getCurrentCardGlobalId() == 0x6e9a && argv[0] == 2)
+	if (_vm->getStack()->getId() == kStackTspit && _vm->getStack()->getCurrentCardGlobalId() == 0x6e9a && argv[0] == 2)
 		return;
 
 	_vm->_activatedSLST = true;
-	SLSTRecord slstRecord = _vm->getCurCard()->getSound(argv[0]);
+	SLSTRecord slstRecord = _vm->getCard()->getSound(argv[0]);
 	_vm->_sound->playSLST(slstRecord);
 }
 
 // Command 41: activate MLST record and play
 void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(argv[0], _vm->getCurCard()->getId());
+	_vm->_video->activateMLST(argv[0], _vm->getCard()->getId());
 	_vm->_video->playMovieRiven(argv[0]);
 }
 
 // Command 43: activate BLST record (card hotspot enabling lists)
 void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->getCurCard()->activateHotspotEnableRecord(argv[0]);
+	_vm->getCard()->activateHotspotEnableRecord(argv[0]);
 
 	// Recheck our current hotspot because it may have now changed
 	_vm->updateCurrentHotspot();
@@ -605,15 +605,15 @@ void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 44: activate FLST record (information on which SFXE resource this card should use)
 void RivenSimpleCommand::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->getCurCard()->activateWaterEffect(argv[0]);
+	_vm->getCard()->activateWaterEffect(argv[0]);
 }
 
 // Command 45: do zip mode
 void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
-	assert(_vm->getCurCard() && _vm->getCurCard()->getCurHotspot());
+	assert(_vm->getCard() && _vm->getCard()->getCurHotspot());
 
 	// Check the ZIPS records to see if we have a match to the hotspot name
-	Common::String hotspotName = _vm->getCurCard()->getCurHotspot()->getName();
+	Common::String hotspotName = _vm->getCard()->getCurHotspot()->getName();
 
 	for (uint16 i = 0; i < _vm->_zipModeData.size(); i++)
 		if (_vm->_zipModeData[i].name == hotspotName) {
@@ -624,17 +624,17 @@ void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 46: activate MLST record (movie lists)
 void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(argv[0], _vm->getCurCard()->getId());
+	_vm->_video->activateMLST(argv[0], _vm->getCard()->getId());
 }
 
 void RivenSimpleCommand::dump(byte tabs) {
 	printTabs(tabs);
 
 	if (_type == 7) { // Use the variable name
-		Common::String varName = _vm->getCurStack()->getName(kVariableNames, _arguments[0]);
+		Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
 		debugN("%s = %d;\n", varName.c_str(), _arguments[1]);
 	} else if (_type == 17) { // Use the external command name
-		Common::String externalCommandName = _vm->getCurStack()->getName(kVariableNames, _arguments[0]);
+		Common::String externalCommandName = _vm->getStack()->getName(kExternalCommandNames, _arguments[0]);
 		debugN("%s(", externalCommandName.c_str());
 		uint16 varCount = _arguments[1];
 		for (uint16 j = 0; j < varCount; j++) {
@@ -644,7 +644,7 @@ void RivenSimpleCommand::dump(byte tabs) {
 		}
 		debugN(");\n");
 	} else if (_type == 24) { // Use the variable name
-		Common::String varName = _vm->getCurStack()->getName(kVariableNames, _arguments[0]);
+		Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
 		debugN("%s += %d;\n", varName.c_str(), _arguments[1]);
 	} else {
 		debugN("%s(", _opcodes[_type].desc);
@@ -706,7 +706,7 @@ RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm,
 }
 
 void RivenSwitchCommand::dump(byte tabs) {
-	Common::String varName = _vm->getCurStack()->getName(kVariableNames, _variableId);
+	Common::String varName = _vm->getStack()->getName(kVariableNames, _variableId);
 	printTabs(tabs); debugN("switch (%s) {\n", varName.c_str());
 	for (uint16 j = 0; j < _branches.size(); j++) {
 		printTabs(tabs + 1);
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index 54846f1..b3dc693 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -113,7 +113,7 @@ uint16 RivenStack::getCardStackId(uint32 globalId) const {
 }
 
 uint32 RivenStack::getCurrentCardGlobalId() const {
-	return _cardIdMap[_vm->getCurCard()->getId()];
+	return _cardIdMap[_vm->getCard()->getId()];
 }
 
 RivenNameList::RivenNameList() {
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index 2a19282..7d17562 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -269,7 +269,7 @@ static const char *variableNames[] = {
 };
 
 uint32 &MohawkEngine_Riven::getStackVar(uint32 index) {
-	Common::String name = getCurStack()->getName(kVariableNames, index);
+	Common::String name = getStack()->getName(kVariableNames, index);
 
 	if (!_vars.contains(name))
 		error("Could not find variable '%s' (stack variable %d)", name.c_str(), index);


Commit: 9926475937ee5ed6137f747016a72a400e69d48b
    https://github.com/scummvm/scummvm/commit/9926475937ee5ed6137f747016a72a400e69d48b
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Update the card and stack variables when entering new locations

Changed paths:
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_saveload.cpp
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h


diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 1395100..d064a17 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -42,6 +42,7 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	loadCardSoundList(id);
 	loadCardHotspotEnableList(id);
 	loadCardWaterEffectList(id);
+	setCurrentCardVariable();
 }
 
 RivenCard::~RivenCard() {
@@ -432,6 +433,10 @@ void RivenCard::runLeaveScripts() {
 	_vm->_scriptMan->runScript(script, false);
 }
 
+void RivenCard::setCurrentCardVariable() {
+	_vm->_vars["currentcardid"] = _id;
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index f387201..86cfae0 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -124,6 +124,7 @@ private:
 	void loadCardSoundList(uint16 id);
 	void loadCardHotspotEnableList(uint16 id);
 	void loadCardWaterEffectList(uint16 id);
+	void setCurrentCardVariable();
 
 	RivenScriptPtr getScript(uint16 scriptType) const;
 	void defaultLoadScript();
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 37b5b68..0d70123 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -403,10 +403,6 @@ Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &desc
 
 	Common::String filename = buildSaveFilename(slot);
 
-	// Convert class variables to variable numbers
-	_vm->_vars["currentstackid"] = _vm->getStack()->getId();
-	_vm->_vars["currentcardid"] = _vm->getCard()->getId();
-
 	Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
 	if (!saveFile)
 		return Common::kWritingFailed;
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index b3dc693..bbe00fc 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -33,6 +33,7 @@ RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
 		_id(id) {
 	loadResourceNames();
 	loadCardIdMap();
+	setCurrentStackVariable();
 }
 
 RivenStack::~RivenStack() {
@@ -116,6 +117,10 @@ uint32 RivenStack::getCurrentCardGlobalId() const {
 	return _cardIdMap[_vm->getCard()->getId()];
 }
 
+void RivenStack::setCurrentStackVariable() {
+	_vm->_vars["currentstackid"] = _id;
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 02fb58c..6daf365 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -98,6 +98,7 @@ public:
 private:
 	void loadResourceNames();
 	void loadCardIdMap();
+	void setCurrentStackVariable();
 
 	MohawkEngine_Riven *_vm;
 


Commit: ab9b241e50a54384635f3bc35dbb775e0fa3a909
    https://github.com/scummvm/scummvm/commit/ab9b241e50a54384635f3bc35dbb775e0fa3a909
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Card event handlers could be called recursively

Changed paths:
    engines/mohawk/riven_card.cpp


diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index d064a17..0b3d96f 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -349,8 +349,13 @@ void RivenCard::onMouseDown(const Common::Point &mouse) {
 	onMouseMove(mouse);
 
 	_pressedHotspot = _hoveredHotspot;
+
+	RivenScriptPtr script;
 	if (_pressedHotspot) {
-		RivenScriptPtr script = _pressedHotspot->getScript(kMouseDownScript);
+		script = _pressedHotspot->getScript(kMouseDownScript);
+	}
+
+	if (script) {
 		_vm->_scriptMan->runScript(script, false);
 	}
 }
@@ -358,31 +363,39 @@ void RivenCard::onMouseDown(const Common::Point &mouse) {
 void RivenCard::onMouseUp(const Common::Point &mouse) {
 	onMouseMove(mouse);
 
+	RivenScriptPtr script;
 	if (_pressedHotspot && _pressedHotspot == _hoveredHotspot) {
-		RivenScriptPtr script = _pressedHotspot->getScript(kMouseUpScript);
-		_vm->_scriptMan->runScript(script, false);
+		script = _pressedHotspot->getScript(kMouseUpScript);
 	}
 
 	_pressedHotspot = nullptr;
+
+	if (script) {
+		_vm->_scriptMan->runScript(script, false);
+	}
 }
 
 void RivenCard::onMouseMove(const Common::Point &mouse) {
 	RivenHotspot *hotspot = getHotspotContainingPoint(mouse);
 
-	if (hotspot) {
-		if (hotspot != _hoveredHotspot) {
-			if (_hoveredHotspot) {
-				RivenScriptPtr script = _hoveredHotspot->getScript(kMouseLeaveScript);
-				_vm->_scriptMan->runScript(script, false);
-			}
+	RivenScriptPtr script = RivenScriptPtr(new RivenScript());
 
-			_hoveredHotspot = hotspot;
-			RivenScriptPtr script = _hoveredHotspot->getScript(kMouseEnterScript);
-			_vm->_scriptMan->runScript(script, false);
-		}
-	} else {
+	// Detect hotspot exit
+	if (_hoveredHotspot && (!hotspot || hotspot != _hoveredHotspot)) {
+		script += _hoveredHotspot->getScript(kMouseLeaveScript);
+	}
+
+	// Detect hotspot entry
+	if (hotspot && hotspot != _hoveredHotspot) {
+		_hoveredHotspot = hotspot;
+		script += _hoveredHotspot->getScript(kMouseEnterScript);
+	}
+
+	if (!hotspot) {
 		_hoveredHotspot = nullptr;
 	}
+
+	_vm->_scriptMan->runScript(script, false);
 }
 
 void RivenCard::onMouseDragUpdate() {


Commit: 9ab0d53cd3d8008b55d64cea832191f5ef2155ce
    https://github.com/scummvm/scummvm/commit/9ab0d53cd3d8008b55d64cea832191f5ef2155ce
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add console commands to dump Riven cards and stacks to stdout

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/console.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 417d9dd..3f6f241 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -379,11 +379,13 @@ bool MystConsole::Cmd_QuickTest(int argc, const char **argv) {
 RivenConsole::RivenConsole(MohawkEngine_Riven *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("changeCard",		WRAP_METHOD(RivenConsole, Cmd_ChangeCard));
 	registerCmd("curCard",		WRAP_METHOD(RivenConsole, Cmd_CurCard));
+	registerCmd("dumpCard",		WRAP_METHOD(RivenConsole, Cmd_DumpCard));
 	registerCmd("var",			WRAP_METHOD(RivenConsole, Cmd_Var));
 	registerCmd("playSound",		WRAP_METHOD(RivenConsole, Cmd_PlaySound));
 	registerCmd("playSLST",       WRAP_METHOD(RivenConsole, Cmd_PlaySLST));
 	registerCmd("stopSound",		WRAP_METHOD(RivenConsole, Cmd_StopSound));
 	registerCmd("curStack",		WRAP_METHOD(RivenConsole, Cmd_CurStack));
+	registerCmd("dumpStack",		WRAP_METHOD(RivenConsole, Cmd_DumpStack));
 	registerCmd("changeStack",	WRAP_METHOD(RivenConsole, Cmd_ChangeStack));
 	registerCmd("hotspots",		WRAP_METHOD(RivenConsole, Cmd_Hotspots));
 	registerCmd("zipMode",		WRAP_METHOD(RivenConsole, Cmd_ZipMode));
@@ -542,6 +544,32 @@ bool RivenConsole::Cmd_ZipMode(int argc, const char **argv) {
 	return true;
 }
 
+bool RivenConsole::Cmd_DumpCard(int argc, const char **argv) {
+	if (argc != 1) {
+		debugPrintf("Usage: dumpCard\n");
+		return true;
+	}
+
+	_vm->getCard()->dump();
+
+	debugPrintf("Card dump complete.\n");
+
+	return true;
+}
+
+bool RivenConsole::Cmd_DumpStack(int argc, const char **argv) {
+	if (argc != 1) {
+		debugPrintf("Usage: dumpStack\n");
+		return true;
+	}
+
+	_vm->getStack()->dump();
+
+	debugPrintf("Stack dump complete.\n");
+
+	return true;
+}
+
 bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 	if (argc < 4) {
 		debugPrintf("Usage: dumpScript <stack> <CARD or HSPT> <card>\n");
diff --git a/engines/mohawk/console.h b/engines/mohawk/console.h
index dc40049..0cae87d 100644
--- a/engines/mohawk/console.h
+++ b/engines/mohawk/console.h
@@ -82,7 +82,8 @@ private:
 	bool Cmd_ChangeStack(int argc, const char **argv);
 	bool Cmd_Hotspots(int argc, const char **argv);
 	bool Cmd_ZipMode(int argc, const char **argv);
-	bool Cmd_RunAllBlocks(int argc, const char **argv);
+	bool Cmd_DumpCard(int argc, const char **argv);
+	bool Cmd_DumpStack(int argc, const char **argv);
 	bool Cmd_DumpScript(int argc, const char **argv);
 	bool Cmd_ListZipCards(int argc, const char **argv);
 	bool Cmd_GetRMAP(int argc, const char **argv);
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 0b3d96f..e82e00b 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -450,6 +450,69 @@ void RivenCard::setCurrentCardVariable() {
 	_vm->_vars["currentcardid"] = _id;
 }
 
+void RivenCard::dump() const {
+	debug("== Card ==");
+	debug("id: %d", _id);
+	if (_name >= 0) {
+		debug("name: %s", _vm->getStack()->getName(kCardNames, _name).c_str());
+	} else {
+		debug("name: [no name]");
+	}
+	debug("zipModePlace: %d", _zipModePlace);
+	debug("globalId: %x", _vm->getStack()->getCardGlobalId(_id));
+	debugN("\n");
+
+	for (uint i = 0; i < _scripts.size(); i++) {
+		debug("== Script %d ==", i);
+		debug("type: %s", RivenScript::getTypeName(_scripts[i].type));
+		_scripts[i].script->dumpScript(0);
+		debugN("\n");
+	}
+
+	for (uint i = 0; i < _hotspots.size(); i++) {
+		debug("== Hotspot %d ==", i);
+		_hotspots[i]->dump();
+	}
+
+	for (uint i = 0; i < _pictureList.size(); i++) {
+		const Common::Rect &rect = _pictureList[i].rect;
+		debug("== Picture %d ==", _pictureList[i].index);
+		debug("pictureId: %d", _pictureList[i].id);
+		debug("rect: (%d, %d, %d, %d)", rect.left, rect.top, rect.right, rect.bottom);
+		debugN("\n");
+	}
+
+	for (uint i = 0; i < _waterEffectList.size(); i++) {
+		debug("== Effect %d ==", _waterEffectList[i].index);
+		debug("sfxeId: %d", _waterEffectList[i].sfxeId);
+		debug("u0: %d", _waterEffectList[i].u0);
+		debugN("\n");
+	}
+
+	for (uint i = 0; i < _hotspotEnableList.size(); i++) {
+		debug("== Hotspot enable %d ==", _hotspotEnableList[i].index);
+		debug("hotspotId: %d", _hotspotEnableList[i].hotspotId);
+		debug("enabled: %d", _hotspotEnableList[i].enabled);
+		debugN("\n");
+	}
+
+	for (uint i = 0; i < _soundList.size(); i++) {
+		debug("== Ambient sound list %d ==", _soundList[i].index);
+		debug("globalVolume: %d", _soundList[i].globalVolume);
+		debug("fadeFlags: %d", _soundList[i].fadeFlags);
+		debug("loop: %d", _soundList[i].loop);
+		debug("suspend: %d", _soundList[i].suspend);
+		debug("u0: %d", _soundList[i].u0);
+		for (uint j = 0; j < _soundList[i].soundIds.size(); j++) {
+			debug("sound[%d].id: %d", j, _soundList[i].soundIds[j]);
+			debug("sound[%d].volume: %d", j, _soundList[i].volumes[j]);
+			debug("sound[%d].balance: %d", j, _soundList[i].balances[j]);
+			debug("sound[%d].u2: %d", j, _soundList[i].u2[j]);
+		}
+		debugN("\n");
+	}
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
@@ -547,4 +610,23 @@ int16 RivenHotspot::getNameId() const {
 	return _nameResource;
 }
 
+void RivenHotspot::dump() const {
+	debug("index: %d", _index);
+	debug("blstId: %d", _blstID);
+	debug("name: %s", getName().c_str());
+	debug("rect: (%d, %d, %d, %d)", _rect.left, _rect.top, _rect.right, _rect.bottom);
+	debug("flags: %d", _flags);
+	debug("mouseCursor: %d", _mouseCursor);
+	debug("u0: %d", _u0);
+	debug("u1: %d", _u1);
+	debugN("\n");
+
+	for (uint i = 0; i < _scripts.size(); i++) {
+		debug("=== Hotspot script %d ===", i);
+		debug("type: %s", RivenScript::getTypeName(_scripts[i].type));
+		_scripts[i].script->dumpScript(0);
+		debugN("\n");
+	}
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 86cfae0..d20f63d 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -117,6 +117,9 @@ public:
 	/** Frame update handler for mouse dragging */
 	void onMouseDragUpdate();
 
+	/** Write all of the card's data to standard output */
+	void dump() const;
+
 private:
 	void loadCardResource(uint16 id);
 	void loadHotspots(uint16 id);
@@ -209,6 +212,9 @@ public:
 	/** Get the hotspot's enable list id */
 	uint16 getBlstId() const;
 
+	/** Write all of the hotspot's data to standard output */
+	void dump() const;
+
 private:
 	enum {
 		kFlagZip = 1,
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index fabb8c6..794e8db 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -189,6 +189,25 @@ RivenScript &RivenScript::operator+=(const RivenScript &other) {
 	return *this;
 }
 
+const char *RivenScript::getTypeName(uint16 type) {
+	static const char *names[] = {
+		"MouseDown",
+		"MouseDrag",
+		"MouseUp",
+		"MouseEnter",
+		"MouseInside",
+		"MouseLeave",
+		"CardLoad",
+		"CardLeave",
+		"CardUnknown",
+		"CardOpen",
+		"CardUpdate"
+	};
+
+	assert(type < ARRAYSIZE(names));
+	return names[type];
+}
+
 RivenScriptPtr &operator+=(RivenScriptPtr &lhs, const RivenScriptPtr &rhs) {
 	*lhs += *rhs;
 	return lhs;
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 0d575d7..304c2f3 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -90,6 +90,9 @@ public:
 	/** Append the commands of the other script to this script */
 	RivenScript &operator+=(const RivenScript &other);
 
+	/** Get a caption for a script type */
+	static const char *getTypeName(uint16 type);
+
 private:
 	Common::Array<RivenCommand *> _commands;
 	bool _continueRunning;
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index bbe00fc..83ebf57 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -114,13 +114,32 @@ uint16 RivenStack::getCardStackId(uint32 globalId) const {
 }
 
 uint32 RivenStack::getCurrentCardGlobalId() const {
-	return _cardIdMap[_vm->getCard()->getId()];
+	return getCardGlobalId(_vm->getCard()->getId());
 }
 
 void RivenStack::setCurrentStackVariable() {
 	_vm->_vars["currentstackid"] = _id;
 }
 
+uint32 RivenStack::getCardGlobalId(uint16 cardId) const {
+	return _cardIdMap[cardId];
+}
+
+void RivenStack::dump() const {
+	debug("= Stack =");
+	debug("id: %d", _id);
+	debug("name: %s", _vm->getStackName(_id).c_str());
+	debugN("\n");
+
+	for (uint i = 0; i < _cardIdMap.size(); i++) {
+		if (!_vm->hasResource(ID_CARD, i)) continue;
+
+		RivenCard *card = new RivenCard(_vm, i);
+		card->dump();
+		delete card;
+	}
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 6daf365..33c6f48 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -95,6 +95,12 @@ public:
 
 	/** Get the global id of the currently active card */
 	uint32 getCurrentCardGlobalId() const;
+
+	/** Get the global id of a card in the stack */
+	uint32 getCardGlobalId(uint16 cardId) const;
+
+	/** Write all of the stack's data including its cards to standard output */
+	void dump() const;
 private:
 	void loadResourceNames();
 	void loadCardIdMap();


Commit: c623a767676f8422186933c87b569d39185b5496
    https://github.com/scummvm/scummvm/commit/c623a767676f8422186933c87b569d39185b5496
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Ensure constructing and deleting cards does not have side effects

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 8b7ee88..e6d8a16 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -384,7 +384,10 @@ void MohawkEngine_Riven::changeToCard(uint16 dest) {
 			}
 	}
 
-	delete _card;
+	if (_card) {
+		_card->leave();
+		delete _card;
+	}
 	_card = new RivenCard(this, dest);
 
 	refreshCard(); // Handles hotspots and scripts
@@ -394,7 +397,7 @@ void MohawkEngine_Riven::refreshCard() {
 	// Clear any timer still floating around
 	removeTimer();
 
-	_card->open();
+	_card->enter();
 
 	if (_showHotspots)
 		_card->drawHotspotRects();
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index e82e00b..212557b 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -42,12 +42,9 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	loadCardSoundList(id);
 	loadCardHotspotEnableList(id);
 	loadCardWaterEffectList(id);
-	setCurrentCardVariable();
 }
 
 RivenCard::~RivenCard() {
-	runLeaveScripts();
-
 	for (uint i = 0; i < _hotspots.size(); i++) {
 		delete _hotspots[i];
 	}
@@ -66,7 +63,9 @@ void RivenCard::loadCardResource(uint16 id) {
 	delete inStream;
 }
 
-void RivenCard::open() {
+void RivenCard::enter() {
+	setCurrentCardVariable();
+
 	_vm->_activatedPLST = false;
 	_vm->_activatedSLST = false;
 
@@ -77,7 +76,7 @@ void RivenCard::open() {
 	initializeZipMode();
 	_vm->_gfx->applyScreenUpdate(true);
 
-	runScript(kCardOpenScript);
+	runScript(kCardEnterScript);
 }
 
 void RivenCard::initializeZipMode() {
@@ -430,7 +429,7 @@ void RivenCard::updateMouseCursor() {
 	_vm->_system->updateScreen();
 }
 
-void RivenCard::runLeaveScripts() {
+void RivenCard::leave() {
 	RivenScriptPtr script(new RivenScript());
 
 	if (_pressedHotspot) {
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index d20f63d..626c427 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -56,7 +56,10 @@ public:
 	};
 
 	/** Initialization routine used to draw a card for the first time or to refresh it */
-	void open();
+	void enter();
+
+	/** Run the card's leave scripts */
+	void leave();
 
 	/** Run one of the card's scripts */
 	void runScript(uint16 scriptType);
@@ -131,7 +134,6 @@ private:
 
 	RivenScriptPtr getScript(uint16 scriptType) const;
 	void defaultLoadScript();
-	void runLeaveScripts();
 
 	void updateMouseCursor();
 
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 794e8db..c3e9238 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -200,7 +200,7 @@ const char *RivenScript::getTypeName(uint16 type) {
 		"CardLoad",
 		"CardLeave",
 		"CardUnknown",
-		"CardOpen",
+		"CardEnter",
 		"CardUpdate"
 	};
 
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 304c2f3..cfe0f3a 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -46,7 +46,7 @@ enum {
 
 	kCardLoadScript = 6,
 	kCardLeaveScript = 7,
-	kCardOpenScript = 9,
+	kCardEnterScript = 9,
 	kCardUpdateScript = 10
 };
 


Commit: 3f1f407c14f72c48f72ae787af0e1e56a09e9da2
    https://github.com/scummvm/scummvm/commit/3f1f407c14f72c48f72ae787af0e1e56a09e9da2
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Remove VideoHandle usage

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/video.cpp
    engines/mohawk/video.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index e6d8a16..b431b97 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -615,18 +615,18 @@ static void sunnersTopStairsTimer(MohawkEngine_Riven *vm) {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoHandle oldHandle = vm->_video->findVideoHandleRiven(1);
+	VideoEntryPtr oldVideo = vm->_video->findVideoRiven(1);
 	uint32 timerTime = 500;
 
-	if (!oldHandle || oldHandle->endOfVideo()) {
+	if (!oldVideo || oldVideo->endOfVideo()) {
 		uint32 &sunnerTime = vm->_vars["jsunnertime"];
 
 		if (sunnerTime == 0) {
 			timerTime = vm->_rnd->getRandomNumberRng(2, 15) * 1000;
 		} else if (sunnerTime < vm->getTotalPlayTime()) {
-			VideoHandle handle = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(1, 3));
+			VideoEntryPtr video = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(1, 3));
 
-			timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(2, 15) * 1000;
+			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(2, 15) * 1000;
 		}
 
 		sunnerTime = timerTime + vm->getTotalPlayTime();
@@ -645,10 +645,10 @@ static void sunnersMidStairsTimer(MohawkEngine_Riven *vm) {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoHandle oldHandle = vm->_video->findVideoHandleRiven(1);
+	VideoEntryPtr oldVideo = vm->_video->findVideoRiven(1);
 	uint32 timerTime = 500;
 
-	if (!oldHandle || oldHandle->endOfVideo()) {
+	if (!oldVideo || oldVideo->endOfVideo()) {
 		uint32 &sunnerTime = vm->_vars["jsunnertime"];
 
 		if (sunnerTime == 0) {
@@ -662,9 +662,9 @@ static void sunnersMidStairsTimer(MohawkEngine_Riven *vm) {
 			else if (randValue == 5)
 				movie = 3;
 
-			VideoHandle handle = vm->_video->playMovieRiven(movie);
+			VideoEntryPtr video = vm->_video->playMovieRiven(movie);
 
-			timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 10) * 1000;
+			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 10) * 1000;
 		}
 
 		sunnerTime = timerTime + vm->getTotalPlayTime();
@@ -683,18 +683,18 @@ static void sunnersLowerStairsTimer(MohawkEngine_Riven *vm) {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoHandle oldHandle = vm->_video->findVideoHandleRiven(1);
+	VideoEntryPtr oldVideo = vm->_video->findVideoRiven(1);
 	uint32 timerTime = 500;
 
-	if (!oldHandle || oldHandle->endOfVideo()) {
+	if (!oldVideo || oldVideo->endOfVideo()) {
 		uint32 &sunnerTime = vm->_vars["jsunnertime"];
 
 		if (sunnerTime == 0) {
 			timerTime = vm->_rnd->getRandomNumberRng(1, 30) * 1000;
 		} else if (sunnerTime < vm->getTotalPlayTime()) {
-			VideoHandle handle = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(3, 5));
+			VideoEntryPtr video = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(3, 5));
 
-			timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
 		}
 
 		sunnerTime = timerTime + vm->getTotalPlayTime();
@@ -713,10 +713,10 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoHandle oldHandle = vm->_video->findVideoHandleRiven(3);
+	VideoEntryPtr oldvideo = vm->_video->findVideoRiven(3);
 	uint32 timerTime = 500;
 
-	if (!oldHandle || oldHandle->endOfVideo()) {
+	if (!oldvideo || oldvideo->endOfVideo()) {
 		uint32 &sunnerTime = vm->_vars["jsunnertime"];
 
 		if (sunnerTime == 0) {
@@ -726,9 +726,9 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) {
 			// activate the MLST, we have to set it manually here.
 			uint16 mlstID = vm->_rnd->getRandomNumberRng(3, 8);
 			vm->_video->activateMLST(mlstID, vm->getCard()->getId());
-			VideoHandle handle = vm->_video->playMovieRiven(mlstID);
+			VideoEntryPtr video = vm->_video->playMovieRiven(mlstID);
 
-			timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
 		}
 
 		sunnerTime = timerTime + vm->getTotalPlayTime();
@@ -763,7 +763,7 @@ void MohawkEngine_Riven::doVideoTimer(VideoHandle handle, bool force) {
 
 	uint16 id = _scriptMan->getStoredMovieOpcodeID();
 
-	if (handle != _video->findVideoHandleRiven(id)) // Check if we've got a video match
+	if (handle != _video->findVideoRiven(id)) // Check if we've got a video match
 		return;
 
 	// Run the opcode if we can at this point
@@ -792,8 +792,8 @@ void MohawkEngine_Riven::checkSunnerAlertClick() {
 		return;
 
 	// If the alert video is no longer playing, we have nothing left to do
-	VideoHandle handle = _video->findVideoHandleRiven(1);
-	if (!handle || handle->endOfVideo())
+	VideoEntryPtr video = _video->findVideoRiven(1);
+	if (!video || video->endOfVideo())
 		return;
 
 	sunners = 1;
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 1a0f1e8..525721b 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -229,10 +229,10 @@ void RivenExternal::runCredits(uint16 video, uint32 delay) {
 	_vm->_gfx->beginCredits();
 	uint nextCreditsFrameStart = 0;
 
-	VideoHandle videoHandle = _vm->_video->findVideoHandleRiven(video);
+	VideoEntryPtr videoPtr = _vm->_video->findVideoRiven(video);
 
 	while (!_vm->shouldQuit() && _vm->_gfx->getCurCreditsImage() <= 320) {
-		if (videoHandle->getCurFrame() >= (int32)videoHandle->getFrameCount() - 1) {
+		if (videoPtr->getCurFrame() >= (int32)videoPtr->getFrameCount() - 1) {
 			if (nextCreditsFrameStart == 0) {
 				// Set us up to start after delay ms
 				nextCreditsFrameStart = _vm->_system->getMillis() + delay;
@@ -267,7 +267,7 @@ void RivenExternal::runDomeButtonMovie() {
 void RivenExternal::runDomeCheck() {
 	// Check if we clicked while the golden frame was showing
 
-	VideoHandle video = _vm->_video->findVideoHandleRiven(1);
+	VideoEntryPtr video = _vm->_video->findVideoRiven(1);
 	assert(video);
 
 	int32 curFrame = video->getCurFrame();
@@ -879,12 +879,12 @@ void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
 			_vm->_video->playMovieRiven(7);
 		}
 	} else {
-		VideoHandle handle = _vm->_video->findVideoHandleRiven(7);
-		if (handle)
-			handle->setEnabled(false);
-		handle = _vm->_video->findVideoHandleRiven(8);
-		if (handle)
-			handle->setEnabled(false);
+		VideoEntryPtr video = _vm->_video->findVideoRiven(7);
+		if (video)
+			video->setEnabled(false);
+		video = _vm->_video->findVideoRiven(8);
+		if (video)
+			video->setEnabled(false);
 	}
 }
 
@@ -1178,7 +1178,7 @@ void RivenExternal::lowerPins() {
 	uint32 &upMovie = _vm->_vars["gupmoov"];
 
 	// Play the video of the pins going down
-	VideoHandle handle = _vm->_video->playMovieRiven(upMovie);
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(upMovie);
 	assert(handle);
 	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
 	_vm->_video->waitUntilMovieEnds(handle);
@@ -1210,7 +1210,7 @@ void RivenExternal::xgrotatepins(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(12);
 
 	// Play the video of the pins rotating
-	VideoHandle handle = _vm->_video->playMovieRiven(_vm->_vars["gupmoov"]);
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(_vm->_vars["gupmoov"]);
 	assert(handle);
 	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 1215, 600));
 	_vm->_video->waitUntilMovieEnds(handle);
@@ -1296,7 +1296,7 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(14);
 
 	// Actually play the movie
-	VideoHandle handle = _vm->_video->playMovieRiven(pinMovieCodes[imagePos - 1]);
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(pinMovieCodes[imagePos - 1]);
 	assert(handle);
 	uint32 startTime = 9630 - pinPos * 600;
 	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
@@ -1376,7 +1376,7 @@ void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) {
 	uint32 newPos = curPos + buttonPos;
 
 	// Now play the movie
-	VideoHandle handle = _vm->_video->playMovieRiven(1);
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
 	assert(handle);
 	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
 	_vm->_video->waitUntilMovieEnds(handle);
@@ -1447,7 +1447,7 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
 	uint32 newPos = curPos + buttonPos;
 
 	// Now play the movie
-	VideoHandle handle = _vm->_video->playMovieRiven(1);
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
 	assert(handle);
 	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
 	_vm->_video->waitUntilMovieEnds(handle);
@@ -1497,10 +1497,10 @@ static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
 
 	// Begin playing the new movie
 	vm->_video->activateMLST(movie, vm->getCard()->getId());
-	VideoHandle videoHandle = vm->_video->playMovieRiven(30);
+	VideoEntryPtr video = vm->_video->playMovieRiven(30);
 
 	// Reset the timer
-	vm->installTimer(&catherineViewerIdleTimer, videoHandle->getDuration().msecs() + vm->_rnd->getRandomNumber(60) * 1000);
+	vm->installTimer(&catherineViewerIdleTimer, video->getDuration().msecs() + vm->_rnd->getRandomNumber(60) * 1000);
 }
 
 void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
@@ -1538,9 +1538,9 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
 	// Begin playing a movie immediately if Catherine is already in the viewer
 	if (cathMovie == 8 || (cathMovie >= 13 && cathMovie <= 16)) {
 		_vm->_video->activateMLST(cathMovie, _vm->getCard()->getId());
-		VideoHandle videoHandle = _vm->_video->playMovieRiven(30);
+		VideoEntryPtr video = _vm->_video->playMovieRiven(30);
 
-		timeUntilNextMovie = videoHandle->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
+		timeUntilNextMovie = video->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
 	} else {
 		// Otherwise, just redraw the imager
 		timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000;
@@ -2013,7 +2013,7 @@ void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) {
 	// Handle movement
 	// (11560/600)s is the length of each of the two movies. We divide it into 19 parts
 	// (one for each of the possible positions the villager can have).
-	VideoHandle handle = _vm->_video->playMovieRiven(doomMLST);
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(doomMLST);
 	Audio::Timestamp startTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
 	*posVar += number; // Adjust to the end
 	Audio::Timestamp endTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
@@ -2072,7 +2072,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();
 
 	// Let's hook onto our video
-	VideoHandle video = _vm->_video->findVideoHandleRiven(argv[0]);
+	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
 
 	// Convert from the standard QuickTime base time to milliseconds
 	// The values are in terms of 1/600 of a second.
@@ -2368,7 +2368,7 @@ static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) {
 	// Randomize a video out in the middle of Tay
 	uint16 movie = vm->_rnd->getRandomNumberRng(2, 13);
 	vm->_video->activateMLST(movie, vm->getCard()->getId());
-	VideoHandle handle = vm->_video->playMovieRiven(movie);
+	VideoEntryPtr handle = vm->_video->playMovieRiven(movie);
 
 	// Ensure the next video starts after this one ends
 	uint32 timeUntilNextVideo = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(38, 58) * 1000;
@@ -2469,7 +2469,7 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
 		// Play a piece of the moving down movie
 		static const uint32 timeIntervals[] = { 4320, 3440, 2560, 1760, 880, 0 };
 		uint16 movieCode = telescopeCover ? 1 : 2;
-		VideoHandle handle = _vm->_video->playMovieRiven(movieCode);
+		VideoEntryPtr handle = _vm->_video->playMovieRiven(movieCode);
 		handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos], 600), Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600));
 		_vm->_sound->playSound(14); // Play the moving sound
 		_vm->_video->waitUntilMovieEnds(handle);
@@ -2502,7 +2502,7 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
 	// Play a piece of the moving up movie
 	static const uint32 timeIntervals[] = { 0, 800, 1680, 2560, 3440, 4320 };
 	uint16 movieCode = _vm->_vars["ttelecover"] ? 4 : 5;
-	VideoHandle handle = _vm->_video->playMovieRiven(movieCode);
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(movieCode);
 	handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600), Audio::Timestamp(0, timeIntervals[telescopePos], 600));
 	_vm->_sound->playSound(14); // Play the moving sound
 	_vm->_video->waitUntilMovieEnds(handle);
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index c3e9238..1e4a193 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -503,9 +503,9 @@ void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 28: disable a movie
 void RivenSimpleCommand::disableMovie(uint16 op, uint16 argc, uint16 *argv) {
-	VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]);
-	if (handle)
-		handle->setEnabled(false);
+	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
+	if (video)
+		video->setEnabled(false);
 }
 
 // Command 29: disable all movies
@@ -515,9 +515,9 @@ void RivenSimpleCommand::disableAllMovies(uint16 op, uint16 argc, uint16 *argv)
 
 // Command 31: enable a movie
 void RivenSimpleCommand::enableMovie(uint16 op, uint16 argc, uint16 *argv) {
-	VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]);
-	if (handle)
-		handle->setEnabled(true);
+	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
+	if (video)
+		video->setEnabled(true);
 }
 
 // Command 32: play foreground movie - blocking (movie_id)
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index eec5432..56da654 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -203,6 +203,10 @@ void VideoManager::playMovieBlockingCentered(const Common::String &fileName, boo
 	waitUntilMovieEnds(VideoHandle(ptr));
 }
 
+void VideoManager::waitUntilMovieEnds(const VideoEntryPtr &video) {
+	waitUntilMovieEnds(VideoHandle(video));
+}
+
 void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
 	if (!videoHandle)
 		return;
@@ -448,7 +452,7 @@ void VideoManager::clearMLST() {
 	_mlstRecords.clear();
 }
 
-VideoHandle VideoManager::playMovieRiven(uint16 id) {
+VideoEntryPtr VideoManager::playMovieRiven(uint16 id) {
 	for (uint16 i = 0; i < _mlstRecords.size(); i++) {
 		if (_mlstRecords[i].code == id) {
 			debug(1, "Play tMOV %d (non-blocking) at (%d, %d) %s, Volume = %d", _mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].loop != 0 ? "looping" : "non-looping", _mlstRecords[i].volume);
@@ -461,11 +465,11 @@ VideoHandle VideoManager::playMovieRiven(uint16 id) {
 				ptr->start();
 			}
 
-			return VideoHandle(ptr);
+			return ptr;
 		}
 	}
 
-	return VideoHandle();
+	return VideoEntryPtr();
 }
 
 void VideoManager::playMovieBlockingRiven(uint16 id) {
@@ -484,9 +488,9 @@ void VideoManager::playMovieBlockingRiven(uint16 id) {
 
 void VideoManager::stopMovieRiven(uint16 id) {
 	debug(2, "Stopping movie %d", id);
-	VideoHandle handle = findVideoHandleRiven(id);
-	if (handle)
-		removeEntry(handle._ptr);
+	VideoEntryPtr video = findVideoRiven(id);
+	if (video)
+		removeEntry(video);
 }
 
 void VideoManager::disableAllMovies() {
@@ -548,14 +552,14 @@ VideoEntryPtr VideoManager::open(const Common::String &fileName) {
 	return entry;
 }
 
-VideoHandle VideoManager::findVideoHandleRiven(uint16 id) {
+VideoEntryPtr VideoManager::findVideoRiven(uint16 id) {
 	for (uint16 i = 0; i < _mlstRecords.size(); i++)
 		if (_mlstRecords[i].code == id)
 			for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++)
 				if ((*it)->getID() == _mlstRecords[i].movieID)
-					return VideoHandle(*it);
+					return *it;
 
-	return VideoHandle();
+	return VideoEntryPtr();
 }
 
 VideoHandle VideoManager::findVideoHandle(uint16 id) {
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index e2b09b6..dba448d 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -321,10 +321,11 @@ public:
 	void activateMLST(uint16 mlstId, uint16 card);
 	void clearMLST();
 	void disableAllMovies();
-	VideoHandle playMovieRiven(uint16 id);
+	VideoEntryPtr playMovieRiven(uint16 id);
 	void playMovieBlockingRiven(uint16 id);
-	VideoHandle findVideoHandleRiven(uint16 id);
+	VideoEntryPtr findVideoRiven(uint16 id);
 	void stopMovieRiven(uint16 id);
+	void waitUntilMovieEnds(const VideoEntryPtr &video);
 
 	// Handle functions
 	VideoHandle findVideoHandle(uint16 id);


Commit: 1a5b2a1e50cbb9067829810d31726a5447b72791
    https://github.com/scummvm/scummvm/commit/1a5b2a1e50cbb9067829810d31726a5447b72791
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move MLST loading to RivenCard

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/video.cpp
    engines/mohawk/video.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index b431b97..2536b02 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -591,7 +591,7 @@ static void catherineIdleTimer(MohawkEngine_Riven *vm) {
 		cathState = 1;
 
 	// Play the movie, blocking
-	vm->_video->activateMLST(movie, vm->getCard()->getId());
+	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
 	vm->_cursor->hideCursor();
 	vm->_video->playMovieBlockingRiven(movie);
 	vm->_cursor->showCursor();
@@ -725,7 +725,7 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) {
 			// Unlike the other cards' scripts which automatically
 			// activate the MLST, we have to set it manually here.
 			uint16 mlstID = vm->_rnd->getRandomNumberRng(3, 8);
-			vm->_video->activateMLST(mlstID, vm->getCard()->getId());
+			vm->_video->activateMLST(vm->getCard()->getMovie(mlstID));
 			VideoEntryPtr video = vm->_video->playMovieRiven(mlstID);
 
 			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 212557b..4b6feae 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -40,6 +40,7 @@ RivenCard::RivenCard(MohawkEngine_Riven *vm, uint16 id) :
 	loadHotspots(id);
 	loadCardPictureList(id);
 	loadCardSoundList(id);
+	loadCardMovieList(id);
 	loadCardHotspotEnableList(id);
 	loadCardWaterEffectList(id);
 }
@@ -510,6 +511,63 @@ void RivenCard::dump() const {
 		}
 		debugN("\n");
 	}
+
+	for (uint i = 0; i < _movieList.size(); i++) {
+		debug("== Movie %d ==", _movieList[i].index);
+		debug("movieID: %d", _movieList[i].movieID);
+		debug("code: %d", _movieList[i].code);
+		debug("left: %d", _movieList[i].left);
+		debug("top: %d", _movieList[i].top);
+		debug("u0[0]: %d", _movieList[i].u0[0]);
+		debug("u0[1]: %d", _movieList[i].u0[1]);
+		debug("u0[2]: %d", _movieList[i].u0[2]);
+		debug("loop: %d", _movieList[i].loop);
+		debug("volume: %d", _movieList[i].volume);
+		debug("u1: %d", _movieList[i].u1);
+		debugN("\n");
+	}
+}
+
+void RivenCard::loadCardMovieList(uint16 id) {
+	Common::SeekableReadStream *mlstStream = _vm->getResource(ID_MLST, id);
+
+	uint16 recordCount = mlstStream->readUint16BE();
+	_movieList.resize(recordCount);
+
+	for (uint16 i = 0; i < recordCount; i++) {
+		MLSTRecord &mlstRecord = _movieList[i];
+		mlstRecord.index = mlstStream->readUint16BE();
+		mlstRecord.movieID = mlstStream->readUint16BE();
+		mlstRecord.code = mlstStream->readUint16BE();
+		mlstRecord.left = mlstStream->readUint16BE();
+		mlstRecord.top = mlstStream->readUint16BE();
+
+		for (byte j = 0; j < 2; j++)
+			if (mlstStream->readUint16BE() != 0)
+				warning("u0[%d] in MLST non-zero", j);
+
+		if (mlstStream->readUint16BE() != 0xFFFF)
+			warning("u0[2] in MLST not 0xFFFF");
+
+		mlstRecord.loop = mlstStream->readUint16BE();
+		mlstRecord.volume = mlstStream->readUint16BE();
+		mlstRecord.u1 = mlstStream->readUint16BE();
+
+		if (mlstRecord.u1 != 1)
+			warning("mlstRecord.u1 not 1");
+	}
+
+	delete mlstStream;
+}
+
+MLSTRecord RivenCard::getMovie(uint16 index) const {
+	for (uint16 i = 0; i < _movieList.size(); i++) {
+		if (_movieList[i].index == index) {
+			return _movieList[i];
+		}
+	}
+
+	error("Could not find movie %d in card %d", index, _id);
 }
 
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 626c427..b02f5f7 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -25,6 +25,7 @@
 
 #include "mohawk/riven_scripts.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/video.h"
 
 #include "common/rect.h"
 #include "common/system.h"
@@ -79,6 +80,9 @@ public:
 	/** Get the card's sound description with the specified index */
 	SLSTRecord getSound(uint16 index) const;
 
+	/** Get the card's movie description with the specified index */
+	MLSTRecord getMovie(uint16 index) const;
+
 	/** Draw borders for all the hotspots in the card */
 	void drawHotspotRects();
 
@@ -128,6 +132,7 @@ private:
 	void loadHotspots(uint16 id);
 	void loadCardPictureList(uint16 id);
 	void loadCardSoundList(uint16 id);
+	void loadCardMovieList(uint16 id);
 	void loadCardHotspotEnableList(uint16 id);
 	void loadCardWaterEffectList(uint16 id);
 	void setCurrentCardVariable();
@@ -164,6 +169,7 @@ private:
 	// Resource lists
 	Common::Array<Picture> _pictureList;
 	Common::Array<SLSTRecord> _soundList;
+	Common::Array<MLSTRecord> _movieList;
 	Common::Array<HotspotEnableRecord> _hotspotEnableList;
 	Common::Array<WaterEffectRecord> _waterEffectList;
 };
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 525721b..c5426dc 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -810,53 +810,53 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
 		// Water is filling/draining from the boiler
 		if (water == 0) {
 			if (platform == 1)
-				_vm->_video->activateMLST(12, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(12));
 			else
-				_vm->_video->activateMLST(10, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
 		} else if (heat == 1) {
 			if (platform == 1)
-				_vm->_video->activateMLST(22, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(22));
 			else
-				_vm->_video->activateMLST(19, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(19));
 		} else {
 			if (platform == 1)
-				_vm->_video->activateMLST(16, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(16));
 			else
-				_vm->_video->activateMLST(13, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(13));
 		}
 	} else if (argv[0] == 2 && water != 0) {
 		if (heat == 1) {
 			// Turning on the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(23, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(23));
 			else
-				_vm->_video->activateMLST(20, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(20));
 		} else {
 			// Turning off the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(18, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(18));
 			else
-				_vm->_video->activateMLST(15, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(15));
 		}
 	} else if (argv[0] == 3) {
 		if (platform == 1) {
 			// Lowering the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(24, _vm->getCard()->getId());
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(24));
 				else
-					_vm->_video->activateMLST(17, _vm->getCard()->getId());
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(17));
 			} else
-				_vm->_video->activateMLST(11, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
 		} else {
 			// Raising the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(21, _vm->getCard()->getId());
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(21));
 				else
-					_vm->_video->activateMLST(14, _vm->getCard()->getId());
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(14));
 			} else
-				_vm->_video->activateMLST(9, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
 		}
 	}
 
@@ -872,10 +872,10 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
 void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
 	if (_vm->_vars["bheat"] != 0) {
 		if (_vm->_vars["bblrgrt"] == 0) {
-			_vm->_video->activateMLST(8, _vm->getCard()->getId());
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
 			_vm->_video->playMovieRiven(8);
 		} else {
-			_vm->_video->activateMLST(7, _vm->getCard()->getId());
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(7));
 			_vm->_video->playMovieRiven(7);
 		}
 	} else {
@@ -996,11 +996,11 @@ void RivenExternal::xbfreeytram(uint16 argc, uint16 *argv) {
 	}
 
 	// Activate the MLST and play the video
-	_vm->_video->activateMLST(mlstId, _vm->getCard()->getId());
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId));
 	_vm->_video->playMovieBlockingRiven(11);
 
 	// Now play the second movie
-	_vm->_video->activateMLST(mlstId + 5, _vm->getCard()->getId());
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId + 5));
 	_vm->_video->playMovieBlockingRiven(12);
 }
 
@@ -1410,19 +1410,19 @@ void RivenExternal::xgplaywhark(uint16 argc, uint16 *argv) {
 	// Activate the correct video based on the amount of times we've been visited
 	switch (wharkVisits) {
 	case 1:
-		_vm->_video->activateMLST(3, _vm->getCard()->getId());
+		_vm->_video->activateMLST(_vm->getCard()->getMovie(3));
 		break;
 	case 2:
 		// One of two random videos
-		_vm->_video->activateMLST(4 + _vm->_rnd->getRandomBit(), _vm->getCard()->getId());
+		_vm->_video->activateMLST(_vm->getCard()->getMovie(4 + _vm->_rnd->getRandomBit()));
 		break;
 	case 3:
 		// One of two random videos
-		_vm->_video->activateMLST(6 + _vm->_rnd->getRandomBit(), _vm->getCard()->getId());
+		_vm->_video->activateMLST(_vm->getCard()->getMovie(6 + _vm->_rnd->getRandomBit()));
 		break;
 	case 4:
 		// Red alert! Shields online! Brace yourself for impact!
-		_vm->_video->activateMLST(8, _vm->getCard()->getId());
+		_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
 		break;
 	}
 
@@ -1496,7 +1496,7 @@ static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
 		cathState = 3;
 
 	// Begin playing the new movie
-	vm->_video->activateMLST(movie, vm->getCard()->getId());
+	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
 	VideoEntryPtr video = vm->_video->playMovieRiven(30);
 
 	// Reset the timer
@@ -1537,7 +1537,7 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
 
 	// Begin playing a movie immediately if Catherine is already in the viewer
 	if (cathMovie == 8 || (cathMovie >= 13 && cathMovie <= 16)) {
-		_vm->_video->activateMLST(cathMovie, _vm->getCard()->getId());
+		_vm->_video->activateMLST(_vm->getCard()->getMovie(cathMovie));
 		VideoEntryPtr video = _vm->_video->playMovieRiven(30);
 
 		timeUntilNextMovie = video->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
@@ -2138,7 +2138,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
 					_vm->_cursor->setCursor(kRivenHideCursor);          // Hide the cursor
 					_vm->getCard()->drawPicture(3);                  // Black out the screen
 					_vm->_sound->playSound(0);                          // Play the link sound
-					_vm->_video->activateMLST(7, _vm->getCard()->getId());    // Activate Gehn Link Video
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(7));    // Activate Gehn Link Video
 					_vm->_video->playMovieBlockingRiven(1);             // Play Gehn Link Video
 					_vm->_vars["agehn"] = 4;                            // Set Gehn to the trapped state
 					_vm->_vars["atrapbook"] = 1;                        // We've got the trap book again
@@ -2283,7 +2283,7 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
 	}
 
 	// Now play the video for the watch
-	_vm->_video->activateMLST(1, _vm->getCard()->getId());
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(1));
 	_vm->_video->playMovieBlockingRiven(1);
 
 	// And, finally, refresh
@@ -2367,7 +2367,7 @@ void RivenExternal::xrhideinventory(uint16 argc, uint16 *argv) {
 static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) {
 	// Randomize a video out in the middle of Tay
 	uint16 movie = vm->_rnd->getRandomNumberRng(2, 13);
-	vm->_video->activateMLST(movie, vm->getCard()->getId());
+	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
 	VideoEntryPtr handle = vm->_video->playMovieRiven(movie);
 
 	// Ensure the next video starts after this one ends
@@ -2435,25 +2435,25 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
 			if (_vm->_vars["pcage"] == 2) {
 				// The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
 				// And now we fall back to Earth... all the way...
-				_vm->_video->activateMLST(8, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
 				runEndGame(8, 5000);
 			} else if (_vm->_vars["agehn"] == 4) {
 				// The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
 				// Nice going! Catherine and the islanders are all dead now! Just go back to your home...
-				_vm->_video->activateMLST(9, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
 				runEndGame(9, 5000);
 			} else if (_vm->_vars["atrapbook"] == 1) {
 				// The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
 				// And then you get shot by Cho. Nice going! Catherine and the islanders are dead
 				// and you have just set Gehn free from Riven, not to mention you're dead.
-				_vm->_video->activateMLST(10, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
 				runEndGame(10, 5000);
 			} else {
 				// The impossible ending: You don't have Catherine's journal and yet you were somehow
 				// able to open the hatch on the telescope. The game provides an ending for those who
 				// cheat, load a saved game with the combo, or just guess the telescope combo. Atrus
 				// doesn't come and you just fall into the fissure.
-				_vm->_video->activateMLST(11, _vm->getCard()->getId());
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
 				runEndGame(11, 5000);
 			}
 		} else {
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 1e4a193..2985f15 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -610,7 +610,7 @@ void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 41: activate MLST record and play
 void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(argv[0], _vm->getCard()->getId());
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(argv[0]));
 	_vm->_video->playMovieRiven(argv[0]);
 }
 
@@ -643,7 +643,7 @@ void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 46: activate MLST record (movie lists)
 void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(argv[0], _vm->getCard()->getId());
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(argv[0]));
 }
 
 void RivenSimpleCommand::dump(byte tabs) {
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index 56da654..cfc6015 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -405,47 +405,15 @@ bool VideoManager::drawNextFrame(VideoEntryPtr videoEntry) {
 	return true;
 }
 
-void VideoManager::activateMLST(uint16 mlstId, uint16 card) {
-	Common::SeekableReadStream *mlstStream = _vm->getResource(ID_MLST, card);
-	uint16 recordCount = mlstStream->readUint16BE();
-
-	for (uint16 i = 0; i < recordCount; i++) {
-		MLSTRecord mlstRecord;
-		mlstRecord.index = mlstStream->readUint16BE();
-		mlstRecord.movieID = mlstStream->readUint16BE();
-		mlstRecord.code = mlstStream->readUint16BE();
-		mlstRecord.left = mlstStream->readUint16BE();
-		mlstRecord.top = mlstStream->readUint16BE();
-
-		for (byte j = 0; j < 2; j++)
-			if (mlstStream->readUint16BE() != 0)
-				warning("u0[%d] in MLST non-zero", j);
-
-		if (mlstStream->readUint16BE() != 0xFFFF)
-			warning("u0[2] in MLST not 0xFFFF");
-
-		mlstRecord.loop = mlstStream->readUint16BE();
-		mlstRecord.volume = mlstStream->readUint16BE();
-		mlstRecord.u1 = mlstStream->readUint16BE();
-
-		if (mlstRecord.u1 != 1)
-			warning("mlstRecord.u1 not 1");
-
-		// We've found a match, add it
-		if (mlstRecord.index == mlstId) {
-			// Make sure we don't have any duplicates
-			for (uint32 j = 0; j < _mlstRecords.size(); j++)
-				if (_mlstRecords[j].index == mlstRecord.index || _mlstRecords[j].code == mlstRecord.code) {
-					_mlstRecords.remove_at(j);
-					j--;
-				}
-
-			_mlstRecords.push_back(mlstRecord);
-			break;
+void VideoManager::activateMLST(const MLSTRecord &mlst) {
+	// Make sure we don't have any duplicates
+	for (uint32 j = 0; j < _mlstRecords.size(); j++)
+		if (_mlstRecords[j].index == mlst.index || _mlstRecords[j].code == mlst.code) {
+			_mlstRecords.remove_at(j);
+			j--;
 		}
-	}
 
-	delete mlstStream;
+	_mlstRecords.push_back(mlst);
 }
 
 void VideoManager::clearMLST() {
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index dba448d..c3d04ea 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -318,7 +318,7 @@ public:
 	bool isVideoPlaying();
 
 	// Riven-related functions
-	void activateMLST(uint16 mlstId, uint16 card);
+	void activateMLST(const MLSTRecord &mlst);
 	void clearMLST();
 	void disableAllMovies();
 	VideoEntryPtr playMovieRiven(uint16 id);


Commit: 8fcebc12c6f653dbfbd06af50a03074a33ac8810
    https://github.com/scummvm/scummvm/commit/8fcebc12c6f653dbfbd06af50a03074a33ac8810
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Fix dumping Riven external commands' arguments

Changed paths:
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 2985f15..44ecca1 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -657,7 +657,7 @@ void RivenSimpleCommand::dump(byte tabs) {
 		debugN("%s(", externalCommandName.c_str());
 		uint16 varCount = _arguments[1];
 		for (uint16 j = 0; j < varCount; j++) {
-			debugN("%d", _arguments[1 + j]);
+			debugN("%d", _arguments[2 + j]);
 			if (j != varCount - 1)
 				debugN(", ");
 		}


Commit: e9b67081c36653bfae0d842a261f313fc9f32231
    https://github.com/scummvm/scummvm/commit/e9b67081c36653bfae0d842a261f313fc9f32231
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Introduce the effects intermediary screen

Changed paths:
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h


diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 6931afb..b5fce9c 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -47,6 +47,9 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 	_mainScreen = new Graphics::Surface();
 	_mainScreen->create(608, 392, _pixelFormat);
 
+	_effectScreen = new Graphics::Surface();
+	_effectScreen->create(608, 392, _pixelFormat);
+
 	_screenUpdateNesting = 0;
 	_screenUpdateRunning = false;
 	_scheduledTransition = -1;	// no transition
@@ -60,6 +63,8 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 }
 
 RivenGraphics::~RivenGraphics() {
+	_effectScreen->free();
+	delete _effectScreen;
 	_mainScreen->free();
 	delete _mainScreen;
 	delete _bitmapDecoder;
@@ -90,10 +95,13 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
 void RivenGraphics::updateScreen(Common::Rect updateRect) {
 	if (_dirtyScreen) {
 		// Copy to screen if there's no transition. Otherwise transition. ;)
-		if (_scheduledTransition < 0)
-			_vm->_system->copyRectToScreen(_mainScreen->getBasePtr(updateRect.left, updateRect.top), _mainScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
-		else
+		if (_scheduledTransition < 0) {
+			// mainScreen -> effectScreen -> systemScreen
+			_effectScreen->copyRectToSurface(*_mainScreen, updateRect.left, updateRect.top, updateRect);
+			_vm->_system->copyRectToScreen(_effectScreen->getBasePtr(updateRect.left, updateRect.top), _effectScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
+		} else {
 			runScheduledTransition();
+		}
 
 		// Finally, update the screen.
 		_vm->_system->updateScreen();
@@ -230,7 +238,8 @@ void RivenGraphics::runScheduledTransition() {
 	}
 
 	// For now, just copy the image to screen without doing any transition.
-	_vm->_system->copyRectToScreen(_mainScreen->getPixels(), _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h);
+	_effectScreen->copyRectToSurface(*_mainScreen, 0, 0, Common::Rect(_mainScreen->w, _mainScreen->h));
+	_vm->_system->copyRectToScreen(_effectScreen->getBasePtr(0, 0), _effectScreen->pitch, 0, 0, _effectScreen->w, _effectScreen->h);
 	_vm->_system->updateScreen();
 
 	_scheduledTransition = -1; // Clear scheduled transition
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 614f9ab..2802c84 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -100,6 +100,7 @@ private:
 
 	// Screen Related
 	Graphics::Surface *_mainScreen;
+	Graphics::Surface *_effectScreen;
 	bool _dirtyScreen;
 	Graphics::PixelFormat _pixelFormat;
 	void clearMainScreen();


Commit: 85712e56c8e2cf8a6e04110646f4345c153211fd
    https://github.com/scummvm/scummvm/commit/85712e56c8e2cf8a6e04110646f4345c153211fd
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Only allow a single pixel format in Riven to simplify the implementation

Changed paths:
    engines/mohawk/riven_graphics.cpp


diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index b5fce9c..d5cb353 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -35,12 +35,9 @@ namespace Mohawk {
 RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm) {
 	_bitmapDecoder = new MohawkBitmap();
 
-	// Give me the best you've got!
-	initGraphics(608, 436, true, NULL);
-	_pixelFormat = _vm->_system->getScreenFormat();
-
-	if (_pixelFormat.bytesPerPixel == 1)
-		error("Riven requires greater than 256 colors to run");
+	// Restrict ourselves to a single pixel format to simplify the effects implementation
+	_pixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+	initGraphics(608, 436, true, &_pixelFormat);
 
 	// The actual game graphics only take up the first 392 rows. The inventory
 	// occupies the rest of the screen and we don't use the buffer to hold that.


Commit: ab2d151541f5d4ae12aeeba6ec5e928109be84f5
    https://github.com/scummvm/scummvm/commit/ab2d151541f5d4ae12aeeba6ec5e928109be84f5
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Implement the (fire)flies effect mainly used in jungle island

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_external.cpp
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 2536b02..485d4bb 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -204,6 +204,7 @@ void MohawkEngine_Riven::handleEvents() {
 	// Update background running things
 	checkTimer();
 	_sound->updateSLST();
+	_gfx->runFliesEffect();
 	bool needsUpdate = _gfx->runScheduledWaterEffects();
 	needsUpdate |= _video->updateMovies();
 
@@ -495,6 +496,7 @@ void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
 
 	while (_system->getMillis() < startTime + ms && !shouldQuit()) {
 		_sound->updateSLST();
+		_gfx->runFliesEffect();
 		bool needsUpdate = _gfx->runScheduledWaterEffects();
 		needsUpdate |= _video->updateMovies();
 
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 4b6feae..894cb4c 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -51,6 +51,7 @@ RivenCard::~RivenCard() {
 	}
 
 	_vm->_gfx->clearWaterEffects();
+	_vm->_gfx->clearFliesEffect();
 	_vm->_video->stopVideos();
 }
 
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index c5426dc..3855123 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -2806,8 +2806,7 @@ void RivenExternal::xtatboundary(uint16 argc, uint16 *argv) {
 // ------------------------------------------------------------------------------------
 
 void RivenExternal::xflies(uint16 argc, uint16 *argv) {
-	// TODO: Activate the "flies" effect
-	debug(1, "STUB: xflies(): create %d %s fl%s", argv[1], (argv[0] == 0) ? "black" : "glowing", (argv[1] == 1) ? "y" : "ies");
+	_vm->_gfx->setFliesEffect(argv[1], argv[0] == 1);
 }
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index d5cb353..573eddf 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -57,6 +57,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 	_creditsPos = 0;
 
 	_transitionSpeed = 0;
+	_fliesEffect = nullptr;
 }
 
 RivenGraphics::~RivenGraphics() {
@@ -65,6 +66,7 @@ RivenGraphics::~RivenGraphics() {
 	_mainScreen->free();
 	delete _mainScreen;
 	delete _bitmapDecoder;
+	delete _fliesEffect;
 }
 
 MohawkSurface *RivenGraphics::decodeImage(uint16 id) {
@@ -447,4 +449,465 @@ void RivenGraphics::applyScreenUpdate(bool force) {
 	}
 }
 
+void RivenGraphics::setFliesEffect(uint16 count, bool fireflies) {
+	delete _fliesEffect;
+	_fliesEffect = new FliesEffect(_vm, count, fireflies);
+}
+
+void RivenGraphics::clearFliesEffect() {
+	delete _fliesEffect;
+	_fliesEffect = nullptr;
+}
+
+void RivenGraphics::runFliesEffect() {
+	if (_fliesEffect) {
+		_fliesEffect->update();
+	}
+}
+
+Graphics::Surface *RivenGraphics::getBackScreen() {
+	return _mainScreen;
+}
+
+Graphics::Surface *RivenGraphics::getEffectScreen() {
+	return _effectScreen;
+}
+
+const FliesEffect::FliesEffectData FliesEffect::_firefliesParameters = {
+		true,
+		true,
+		true,
+		true,
+		3.0,
+		0.7,
+		40,
+		2.0,
+		1.0,
+		8447718,
+		30,
+		10
+};
+
+const FliesEffect::FliesEffectData FliesEffect::_fliesParameters = {
+		false,
+		false,
+		false,
+		true,
+		8.0,
+		3.0,
+		80,
+		3.0,
+		1.0,
+		661528,
+		30,
+		10
+};
+
+FliesEffect::FliesEffect(MohawkEngine_Riven *vm, uint16 count, bool fireflies) :
+		_vm(vm) {
+
+	_effectSurface = _vm->_gfx->getEffectScreen();
+	_backSurface = _vm->_gfx->getBackScreen();
+	_gameRect = Common::Rect(608, 392);
+
+	if (fireflies) {
+		_parameters = &_firefliesParameters;
+	} else {
+		_parameters = &_fliesParameters;
+	}
+
+	_updatePeriodMs = 66;
+	_nextUpdateTime = _vm->_system->getMillis();
+
+	initFlies(count);
+}
+
+FliesEffect::~FliesEffect() {
+
+}
+
+void FliesEffect::initFlies(uint16 count) {
+	_fly.resize(count);
+	for (uint16 i = 0; i < _fly.size(); i++) {
+		initFlyRandomPosition(i);
+	}
+}
+
+void FliesEffect::initFlyRandomPosition(uint index) {
+	int posX = _vm->_rnd->getRandomNumber(_gameRect.right - 3);
+	int posY = _vm->_rnd->getRandomNumber(_gameRect.bottom - 3);
+
+	if (posY < 100) {
+		posY = 100;
+	}
+
+	initFlyAtPosition(index, posX, posY, 15);
+}
+
+int FliesEffect::randomBetween(int min, int max) {
+	return _vm->_rnd->getRandomNumber(max - min) + min;
+}
+
+void FliesEffect::initFlyAtPosition(uint index, int posX, int posY, int posZ) {
+	FliesEffectEntry &fly = _fly[index];
+
+	fly.posX = posX;
+	fly.posXFloat = posX;
+	fly.posY = posY;
+	fly.posYFloat = posY;
+	fly.posZ = posZ;
+	fly.light = true;
+
+	fly.framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration);
+
+	fly.hasBlur = false;
+	fly.directionAngleRad = randomBetween(0, 300) / 100.0f;
+	fly.directionAngleRadZ = randomBetween(0, 300) / 100.0f;
+	fly.speed = randomBetween(0, 100) / 100.0f;
+}
+
+void FliesEffect::update() {
+	if (_nextUpdateTime <= _vm->_system->getMillis()) {
+		_nextUpdateTime = _updatePeriodMs + _vm->_system->getMillis();
+
+		updateFlies();
+		draw();
+		updateScreen();
+	}
+}
+
+void FliesEffect::updateFlies() {
+	for (uint i = 0; i < _fly.size(); i++) {
+		updateFlyPosition(i);
+
+		if (_fly[i].posX < 1 || _fly[i].posX > _gameRect.right - 4 || _fly[i].posY > _gameRect.bottom - 4) {
+			initFlyRandomPosition(i);
+		}
+
+		if (_parameters->lightable) {
+			_fly[i].framesTillLightSwitch--;
+
+			if (_fly[i].framesTillLightSwitch <= 0) {
+				_fly[i].light = !_fly[i].light;
+				_fly[i].framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration);
+				_fly[i].hasBlur = false;
+			}
+		}
+	}
+}
+
+void FliesEffect::updateFlyPosition(uint index) {
+	FliesEffectEntry &fly = _fly[index];
+
+	if (fly.directionAngleRad > 2.0f * M_PI) {
+		fly.directionAngleRad = fly.directionAngleRad - 2.0f * M_PI;
+	}
+	if (fly.directionAngleRad < 0.0f) {
+		fly.directionAngleRad = fly.directionAngleRad + 2.0f * M_PI;
+	}
+	if (fly.directionAngleRadZ > 2.0f * M_PI) {
+		fly.directionAngleRadZ = fly.directionAngleRadZ - 2.0f * M_PI;
+	}
+	if (fly.directionAngleRadZ < 0.0f) {
+		fly.directionAngleRadZ = fly.directionAngleRadZ + 2.0f * M_PI;
+	}
+	fly.posXFloat += cos(fly.directionAngleRad) * fly.speed;
+	fly.posYFloat += sin(fly.directionAngleRad) * fly.speed;
+	fly.posX = fly.posXFloat;
+	fly.posY = fly.posYFloat;
+	selectAlphaMap(
+			fly.posXFloat - fly.posX >= 0.5,
+			fly.posYFloat - fly.posY >= 0.5,
+			&fly.alphaMap,
+			&fly.width,
+			&fly.height);
+	fly.posZFloat += cos(fly.directionAngleRadZ) * (fly.speed / 2.0f);
+	fly.posZ = fly.posZFloat;
+	if (_parameters->canBlur && fly.speed > _parameters->blurSpeedTreshold) {
+		fly.hasBlur = true;
+		float blurPosXFloat = cos(fly.directionAngleRad + M_PI) * _parameters->blurDistance + fly.posXFloat;
+		float blurPosYFloat = sin(fly.directionAngleRad + M_PI) * _parameters->blurDistance + fly.posYFloat;
+
+		fly.blurPosX = blurPosXFloat;
+		fly.blurPosY = blurPosYFloat;
+		selectAlphaMap(
+				blurPosXFloat - fly.blurPosX >= 0.5,
+				blurPosYFloat - fly.blurPosY >= 0.5,
+				&fly.blurAlphaMap,
+				&fly.blurWidth,
+				&fly.blurHeight);
+	}
+	if (fly.posY >= 100) {
+		int maxAngularSpeed = _parameters->maxAcceleration;
+		if (fly.posZ > 15) {
+			maxAngularSpeed /= 2;
+		}
+		int angularSpeed = randomBetween(-maxAngularSpeed, maxAngularSpeed);
+		fly.directionAngleRad += angularSpeed / 100.0f;
+	} else {
+		// Make the flies go down if they are too high in the screen
+		int angularSpeed = randomBetween(0, 50);
+		if (fly.directionAngleRad >= M_PI / 2.0f && fly.directionAngleRad <= 3.0f * M_PI / 2.0f) {
+			// Going down
+			fly.directionAngleRad -= angularSpeed / 100.0f;
+		} else {
+			// Going up
+			fly.directionAngleRad += angularSpeed / 100.0f;
+		}
+		if (fly.posY < 1) {
+			initFlyRandomPosition(index);
+		}
+	}
+	if (fly.posZ >= 0) {
+		int distanceToScreenEdge;
+		if (fly.posX / 10 >= (_gameRect.right - fly.posX) / 10) {
+			distanceToScreenEdge = (_gameRect.right - fly.posX) / 10;
+		} else {
+			distanceToScreenEdge = fly.posX / 10;
+		}
+		if (distanceToScreenEdge > (_gameRect.bottom - fly.posY) / 10) {
+			distanceToScreenEdge = (_gameRect.bottom - fly.posY) / 10;
+		}
+		if (distanceToScreenEdge > 30) {
+			distanceToScreenEdge = 30;
+		}
+		if (fly.posZ <= distanceToScreenEdge) {
+			fly.directionAngleRadZ += randomBetween(-_parameters->maxAcceleration, _parameters->maxAcceleration) / 100.0f;
+		} else {
+			fly.posZ = distanceToScreenEdge;
+			fly.directionAngleRadZ += M_PI;
+		}
+	} else {
+		fly.posZ = 0;
+		fly.directionAngleRadZ += M_PI;
+	}
+	float minSpeed = _parameters->minSpeed - fly.posZ / 40.0f;
+	float maxSpeed = _parameters->maxSpeed - fly.posZ / 20.0f;
+	fly.speed += randomBetween(-_parameters->maxAcceleration, _parameters->maxAcceleration) / 100.0f;
+	if (fly.speed > maxSpeed) {
+		fly.speed -= randomBetween(0, 50) / 100.0f;
+	}
+	if (fly.speed < minSpeed) {
+		fly.speed += randomBetween(0, 50) / 100.0f;
+	}
+}
+
+void FliesEffect::selectAlphaMap(bool horGridOffset, bool vertGridoffset, const uint16 **alphaMap, uint *width, uint *height) {
+	static const uint16 alpha1[12] = {
+			 8, 16,  8,
+			16, 32, 16,
+			 8, 16,  8,
+			 0,  0,  0
+	};
+
+	static const uint16 alpha2[12] = {
+			4, 12, 12, 4,
+			8, 24, 24, 8,
+			4, 12, 12, 4
+	};
+
+	static const uint16 alpha3[12] = {
+			 4,  8,  4,
+			12, 24, 12,
+			12, 24, 12,
+			 4,  8,  4
+	};
+
+	static const uint16 alpha4[16] = {
+			2,  6,  6, 2,
+			6, 18, 18, 6,
+			6, 18, 18, 6,
+			2,  6,  6, 2
+	};
+
+	static const uint16 alpha5[12] = {
+			4,  8, 4,
+			8, 32, 8,
+			4,  8, 4,
+			0,  0, 0
+	};
+
+	static const uint16 alpha6[12] = {
+			2,  6,  6, 2,
+			4, 24, 24, 4,
+			2,  6,  6, 2
+	};
+
+	static const uint16 alpha7[12] = {
+			2,  4, 2,
+			6, 24, 6,
+			6, 24, 6,
+			2,  4, 2
+	};
+
+	static const uint16 alpha8[16] = {
+			1,  3,  3, 1,
+			3, 18, 18, 3,
+			3, 18, 18, 3,
+			1,  3,  3, 1
+	};
+
+	struct AlphaMap {
+		bool horizontalGridOffset;
+		bool verticalGridOffset;
+		bool isLarge;
+		uint16 width;
+		uint16 height;
+		const uint16 *pixels;
+	};
+
+	static const AlphaMap alphaSelector[] = {
+			{ true,  true,  true,  4, 4, alpha4 },
+			{ true,  true,  false, 4, 4, alpha8 },
+			{ true,  false, true,  4, 3, alpha2 },
+			{ true,  false, false, 4, 3, alpha6 },
+			{ false, true,  true,  3, 4, alpha3 },
+			{ false, true,  false, 3, 4, alpha7 },
+			{ false, false, true,  3, 3, alpha1 },
+			{ false, false, false, 3, 3, alpha5 }
+	};
+
+	for (uint i = 0; i < ARRAYSIZE(alphaSelector); i++) {
+		if (alphaSelector[i].horizontalGridOffset == horGridOffset
+		    && alphaSelector[i].verticalGridOffset == vertGridoffset
+		    && alphaSelector[i].isLarge == _parameters->isLarge) {
+			*alphaMap = alphaSelector[i].pixels;
+			*width = alphaSelector[i].width;
+			*height = alphaSelector[i].height;
+			return;
+		}
+	}
+
+	error("Unknown flies alpha map case");
+}
+
+void FliesEffect::draw() {
+	const Graphics::PixelFormat format = _effectSurface->format;
+
+	for (uint i = 0; i < _fly.size(); i++) {
+		FliesEffectEntry &fly = _fly[i];
+		uint32 color = _parameters->color32;
+		if (!fly.light) {
+			color = _fliesParameters.color32;
+		}
+
+		bool hoveringBrightBackground = false;
+		for (uint y = 0; y < fly.height; y++) {
+			uint16 *pixel = (uint16 *) _effectSurface->getBasePtr(fly.posX, fly.posY + y);
+
+			for (uint x = 0; x < fly.width; x++) {
+				byte r, g, b;
+				format.colorToRGB(*pixel, r, g, b);
+
+				if (_parameters->unlightIfTooBright) {
+					if (r >= 192 || g >= 192 || b >= 192) {
+						hoveringBrightBackground = true;
+					}
+				}
+				colorBlending(color, r, g, b, fly.alphaMap[fly.width * y + x] - fly.posZ);
+
+				*pixel = format.RGBToColor(r, g, b);
+				++pixel;
+			}
+		}
+
+		Common::Rect drawRect = Common::Rect(fly.width, fly.height);
+		drawRect.translate(fly.posX, fly.posY);
+		addToScreenDirtyRects(drawRect);
+		addToEffectsDirtyRects(drawRect);
+
+		if (fly.hasBlur) {
+			for (uint y = 0; y < fly.blurHeight; y++) {
+				uint16 *pixel = (uint16 *) _effectSurface->getBasePtr(fly.blurPosX, fly.blurPosY + y);
+				for (uint x = 0; x < fly.blurWidth; x++) {
+					byte r, g, b;
+					format.colorToRGB(*pixel, r, g, b);
+
+					colorBlending(color, r, g, b, fly.blurAlphaMap[fly.blurWidth * y + x] - fly.posZ);
+
+					*pixel = format.RGBToColor(r, g, b);
+					++pixel;
+				}
+			}
+
+			Common::Rect drawRect2 = Common::Rect(fly.blurWidth, fly.blurHeight);
+			drawRect2.translate(fly.blurPosX, fly.blurPosY);
+			addToScreenDirtyRects(drawRect2);
+			addToEffectsDirtyRects(drawRect2);
+
+			fly.hasBlur = false;
+		}
+
+		if (hoveringBrightBackground) {
+			fly.hasBlur = false;
+			if (_parameters->lightable) {
+				fly.light = false;
+				fly.framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration);
+			}
+
+			if (_vm->_rnd->getRandomBit()) {
+				fly.directionAngleRad += M_PI / 2.0;
+			} else {
+				fly.directionAngleRad -= M_PI / 2.0;
+			}
+		}
+	}
+}
+
+void FliesEffect::colorBlending(uint32 flyColor, byte &r, byte &g, byte &b, int alpha) {
+	alpha = CLIP(alpha, 0, 32);
+	byte flyR = (flyColor & 0x000000FF) >> 0;
+	byte flyG = (flyColor & 0x0000FF00) >> 8;
+	byte flyB = (flyColor & 0x00FF0000) >> 16;
+
+	r = (32 * r + alpha * (flyR - r)) / 32;
+	g = (32 * g + alpha * (flyG - g)) / 32;
+	b = (32 * b + alpha * (flyB - b)) / 32;
+}
+
+void FliesEffect::updateScreen() {
+	for (uint i = 0; i < _screenSurfaceDirtyRects.size(); i++) {
+		const Common::Rect &rect = _screenSurfaceDirtyRects[i];
+		_vm->_system->copyRectToScreen(_effectSurface->getBasePtr(rect.left, rect.top),
+		                               _effectSurface->pitch, rect.left, rect.top,
+		                               rect.width(), rect.height()
+		);
+	}
+	_screenSurfaceDirtyRects.clear();
+
+	restoreEffectsSurface();
+}
+
+void FliesEffect::addToScreenDirtyRects(const Common::Rect &rect) {
+	for (uint i = 0; i < _screenSurfaceDirtyRects.size(); i++) {
+		if (rect.intersects(_screenSurfaceDirtyRects[i])) {
+			_screenSurfaceDirtyRects[i].extend(rect);
+			return;
+		}
+	}
+
+	_screenSurfaceDirtyRects.push_back(rect);
+}
+
+void FliesEffect::addToEffectsDirtyRects(const Common::Rect &rect) {
+	for (uint i = 0; i < _effectsSurfaceDirtyRects.size(); i++) {
+		if (rect.intersects(_effectsSurfaceDirtyRects[i])) {
+			_effectsSurfaceDirtyRects[i].extend(rect);
+			return;
+		}
+	}
+
+	_effectsSurfaceDirtyRects.push_back(rect);
+}
+
+void FliesEffect::restoreEffectsSurface() {
+	for (uint i = 0; i < _effectsSurfaceDirtyRects.size(); i++) {
+		const Common::Rect &rect = _effectsSurfaceDirtyRects[i];
+		_effectSurface->copyRectToSurface(*_backSurface, rect.left, rect.top, rect);
+		addToScreenDirtyRects(rect);
+	}
+
+	_effectsSurfaceDirtyRects.clear();
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 2802c84..8120879 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -28,6 +28,7 @@
 namespace Mohawk {
 
 class MohawkEngine_Riven;
+class FliesEffect;
 
 class RivenGraphics : public GraphicsManager {
 public:
@@ -44,11 +45,19 @@ public:
 	void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect);
 	void drawExtrasImage(uint16 id, Common::Rect dstRect);
 
+	Graphics::Surface *getEffectScreen();
+	Graphics::Surface *getBackScreen();
+
 	// Water Effect
 	void scheduleWaterEffect(uint16);
 	void clearWaterEffects();
 	bool runScheduledWaterEffects();
 
+	// Flies Effect
+	void setFliesEffect(uint16 count, bool fireflies);
+	void clearFliesEffect();
+	void runFliesEffect();
+
 	// Transitions
 	void scheduleTransition(uint16 id, Common::Rect rect = Common::Rect(0, 0, 608, 392));
 	void runScheduledTransition();
@@ -88,6 +97,9 @@ private:
 	};
 	Common::Array<SFXERecord> _waterEffects;
 
+	// Flies Effect
+	FliesEffect *_fliesEffect;
+
 	// Transitions
 	int16 _scheduledTransition;
 	Common::Rect _transitionRect;
@@ -102,6 +114,7 @@ private:
 	Graphics::Surface *_mainScreen;
 	Graphics::Surface *_effectScreen;
 	bool _dirtyScreen;
+
 	Graphics::PixelFormat _pixelFormat;
 	void clearMainScreen();
 
@@ -109,6 +122,96 @@ private:
 	uint _creditsImage, _creditsPos;
 };
 
+/**
+ * The flies effect draws flies in the scene
+ *
+ * It can draw either regular flies or fireflies.
+ * The flies' movement is simulated in 3 dimensions.
+ */
+class FliesEffect {
+public:
+	FliesEffect(MohawkEngine_Riven *vm, uint16 count, bool fireflies);
+	~FliesEffect();
+
+	/** Simulate the flies' movement and draw them to the screen */
+	void update();
+
+private:
+	struct FliesEffectEntry	{
+		bool light;
+		int posX;
+		int posY;
+		int posZ;
+		const uint16 *alphaMap;
+		uint width;
+		uint height;
+		int framesTillLightSwitch;
+		bool hasBlur;
+		int blurPosX;
+		int blurPosY;
+		const uint16 *blurAlphaMap;
+		uint blurWidth;
+		uint blurHeight;
+		float posXFloat;
+		float posYFloat;
+		float posZFloat;
+		float directionAngleRad;
+		float directionAngleRadZ;
+		float speed;
+	};
+
+	struct FliesEffectData {
+		bool lightable;
+		bool unlightIfTooBright;
+		bool isLarge;
+		bool canBlur;
+		float maxSpeed;
+		float minSpeed;
+		int maxAcceleration;
+		float blurSpeedTreshold;
+		float blurDistance;
+		uint32 color32;
+		int minFramesLit;
+		int maxLightDuration;
+	};
+
+	MohawkEngine_Riven *_vm;
+
+	uint _nextUpdateTime;
+	int _updatePeriodMs;
+
+	Common::Rect _gameRect;
+	Graphics::Surface *_effectSurface;
+	Graphics::Surface *_backSurface;
+	Common::Array<Common::Rect> _screenSurfaceDirtyRects;
+	Common::Array<Common::Rect> _effectsSurfaceDirtyRects;
+
+	const FliesEffectData *_parameters;
+	static const FliesEffectData _firefliesParameters;
+	static const FliesEffectData _fliesParameters;
+
+	Common::Array<FliesEffectEntry> _fly;
+
+	void initFlies(uint16 count);
+	void initFlyRandomPosition(uint index);
+	void initFlyAtPosition(uint index, int posX, int posY, int posZ);
+
+	void updateFlies();
+	void updateFlyPosition(uint index);
+
+	void draw();
+	void updateScreen();
+
+	void selectAlphaMap(bool horGridOffset, bool vertGridoffset, const uint16 **alphaMap, uint *width, uint *height);
+	void colorBlending(uint32 flyColor, byte &r, byte &g, byte &b, int alpha);
+
+	void addToScreenDirtyRects(const Common::Rect &rect);
+	void addToEffectsDirtyRects(const Common::Rect &rect);
+	void restoreEffectsSurface();
+
+	int randomBetween(int min, int max);
+};
+
 } // End of namespace Mohawk
 
 #endif


Commit: e2c5609e81d3a54e0d3c63427288f3c261b86ade
    https://github.com/scummvm/scummvm/commit/e2c5609e81d3a54e0d3c63427288f3c261b86ade
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Prepare empty classes for the Riven stacks

Changed paths:
  A engines/mohawk/riven_stacks/aspit.cpp
  A engines/mohawk/riven_stacks/aspit.h
  A engines/mohawk/riven_stacks/bspit.cpp
  A engines/mohawk/riven_stacks/bspit.h
  A engines/mohawk/riven_stacks/domespit.cpp
  A engines/mohawk/riven_stacks/domespit.h
  A engines/mohawk/riven_stacks/gspit.cpp
  A engines/mohawk/riven_stacks/gspit.h
  A engines/mohawk/riven_stacks/jspit.cpp
  A engines/mohawk/riven_stacks/jspit.h
  A engines/mohawk/riven_stacks/ospit.cpp
  A engines/mohawk/riven_stacks/ospit.h
  A engines/mohawk/riven_stacks/pspit.cpp
  A engines/mohawk/riven_stacks/pspit.h
  A engines/mohawk/riven_stacks/rspit.cpp
  A engines/mohawk/riven_stacks/rspit.h
  A engines/mohawk/riven_stacks/tspit.cpp
  A engines/mohawk/riven_stacks/tspit.h
    engines/mohawk/module.mk
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h


diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 7de2a16..2f6d216 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -60,7 +60,16 @@ MODULE_OBJS += \
 	riven_scripts.o \
 	riven_sound.o \
 	riven_stack.o \
-	riven_vars.o
+	riven_vars.o \
+	riven_stacks/aspit.o \
+	riven_stacks/bspit.o \
+	riven_stacks/domespit.o \
+	riven_stacks/gspit.o \
+	riven_stacks/jspit.o \
+	riven_stacks/ospit.o \
+	riven_stacks/pspit.o \
+	riven_stacks/rspit.o \
+	riven_stacks/tspit.o
 endif
 
 # This module can be built as a plugin
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 485d4bb..d6c6754 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -37,6 +37,14 @@
 #include "mohawk/riven_saveload.h"
 #include "mohawk/riven_sound.h"
 #include "mohawk/riven_stack.h"
+#include "mohawk/riven_stacks/aspit.h"
+#include "mohawk/riven_stacks/bspit.h"
+#include "mohawk/riven_stacks/gspit.h"
+#include "mohawk/riven_stacks/jspit.h"
+#include "mohawk/riven_stacks/ospit.h"
+#include "mohawk/riven_stacks/pspit.h"
+#include "mohawk/riven_stacks/rspit.h"
+#include "mohawk/riven_stacks/tspit.h"
 #include "mohawk/dialogs.h"
 #include "mohawk/video.h"
 #include "mohawk/console.h"
@@ -341,7 +349,30 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 	_sound->stopAllSLST();
 
 	delete _stack;
-	_stack = new RivenStack(this, n);
+	_stack = constructStackById(n);
+}
+
+RivenStack *MohawkEngine_Riven::constructStackById(uint16 id) {
+	switch (id) {
+		case kStackAspit:
+			return new RivenStacks::ASpit(this);
+		case kStackBspit:
+			return new RivenStacks::BSpit(this);
+		case kStackGspit:
+			return new RivenStacks::GSpit(this);
+		case kStackJspit:
+			return new RivenStacks::JSpit(this);
+		case kStackOspit:
+			return new RivenStacks::OSpit(this);
+		case kStackPspit:
+			return new RivenStacks::PSpit(this);
+		case kStackRspit:
+			return new RivenStacks::RSpit(this);
+		case kStackTspit:
+			return new RivenStacks::TSpit(this);
+		default:
+			error("Unknown stack id '%d'", id);
+	}
 }
 
 // Riven uses some hacks to change stacks for linking books
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 84b8f97..6d268d9 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -172,6 +172,8 @@ public:
 	void installCardTimer();
 	void checkTimer();
 	void removeTimer();
+
+	RivenStack *constructStackById(uint16 id);
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp
new file mode 100644
index 0000000..f8aa53a
--- /dev/null
+++ b/engines/mohawk/riven_stacks/aspit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/aspit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+ASpit::ASpit(MohawkEngine_Riven *vm) :
+		RivenStack(vm, kStackAspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/aspit.h b/engines/mohawk/riven_stacks/aspit.h
new file mode 100644
index 0000000..3a55714
--- /dev/null
+++ b/engines/mohawk/riven_stacks/aspit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_ASPIT_H
+#define RIVEN_STACKS_ASPIT_H
+
+#include "mohawk/riven_stack.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class ASpit : public RivenStack {
+public:
+	ASpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/bspit.cpp b/engines/mohawk/riven_stacks/bspit.cpp
new file mode 100644
index 0000000..791317c
--- /dev/null
+++ b/engines/mohawk/riven_stacks/bspit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/bspit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+BSpit::BSpit(MohawkEngine_Riven *vm) :
+		DomeSpit(vm, kStackBspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/bspit.h b/engines/mohawk/riven_stacks/bspit.h
new file mode 100644
index 0000000..be3c052
--- /dev/null
+++ b/engines/mohawk/riven_stacks/bspit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_BSPIT_H
+#define RIVEN_STACKS_BSPIT_H
+
+#include "mohawk/riven_stacks/domespit.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class BSpit : public DomeSpit {
+public:
+	BSpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/domespit.cpp b/engines/mohawk/riven_stacks/domespit.cpp
new file mode 100644
index 0000000..4674a24
--- /dev/null
+++ b/engines/mohawk/riven_stacks/domespit.cpp
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/domespit.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+DomeSpit::DomeSpit(MohawkEngine_Riven *vm, uint16 id) :
+		RivenStack(vm, id) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/domespit.h b/engines/mohawk/riven_stacks/domespit.h
new file mode 100644
index 0000000..776736d
--- /dev/null
+++ b/engines/mohawk/riven_stacks/domespit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_DOMESPIT_H
+#define RIVEN_STACKS_DOMESPIT_H
+
+#include "mohawk/riven_stack.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class DomeSpit : public RivenStack {
+public:
+	DomeSpit(MohawkEngine_Riven *vm, uint16 id);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/gspit.cpp b/engines/mohawk/riven_stacks/gspit.cpp
new file mode 100644
index 0000000..8617b28
--- /dev/null
+++ b/engines/mohawk/riven_stacks/gspit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/gspit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+GSpit::GSpit(MohawkEngine_Riven *vm) :
+		DomeSpit(vm, kStackGspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/gspit.h b/engines/mohawk/riven_stacks/gspit.h
new file mode 100644
index 0000000..794a95b
--- /dev/null
+++ b/engines/mohawk/riven_stacks/gspit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_GSPIT_H
+#define RIVEN_STACKS_GSPIT_H
+
+#include "mohawk/riven_stacks/domespit.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class GSpit : public DomeSpit {
+public:
+	GSpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
new file mode 100644
index 0000000..e19d289
--- /dev/null
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/jspit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+JSpit::JSpit(MohawkEngine_Riven *vm) :
+		DomeSpit(vm, kStackJspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/jspit.h b/engines/mohawk/riven_stacks/jspit.h
new file mode 100644
index 0000000..9e82a13
--- /dev/null
+++ b/engines/mohawk/riven_stacks/jspit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_JSPIT_H
+#define RIVEN_STACKS_JSPIT_H
+
+#include "mohawk/riven_stacks/domespit.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class JSpit : public DomeSpit {
+public:
+	JSpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/ospit.cpp b/engines/mohawk/riven_stacks/ospit.cpp
new file mode 100644
index 0000000..f36be94
--- /dev/null
+++ b/engines/mohawk/riven_stacks/ospit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/ospit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+OSpit::OSpit(MohawkEngine_Riven *vm) :
+		RivenStack(vm, kStackOspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/ospit.h b/engines/mohawk/riven_stacks/ospit.h
new file mode 100644
index 0000000..0792571
--- /dev/null
+++ b/engines/mohawk/riven_stacks/ospit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_OSPIT_H
+#define RIVEN_STACKS_OSPIT_H
+
+#include "mohawk/riven_stack.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class OSpit : public RivenStack {
+public:
+	OSpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/pspit.cpp b/engines/mohawk/riven_stacks/pspit.cpp
new file mode 100644
index 0000000..c6b1b97
--- /dev/null
+++ b/engines/mohawk/riven_stacks/pspit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/pspit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+PSpit::PSpit(MohawkEngine_Riven *vm) :
+		DomeSpit(vm, kStackPspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/pspit.h b/engines/mohawk/riven_stacks/pspit.h
new file mode 100644
index 0000000..797eb29
--- /dev/null
+++ b/engines/mohawk/riven_stacks/pspit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_PSPIT_H
+#define RIVEN_STACKS_PSPIT_H
+
+#include "mohawk/riven_stacks/domespit.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class PSpit : public DomeSpit {
+public:
+	PSpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/rspit.cpp b/engines/mohawk/riven_stacks/rspit.cpp
new file mode 100644
index 0000000..c099c43
--- /dev/null
+++ b/engines/mohawk/riven_stacks/rspit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/rspit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+RSpit::RSpit(MohawkEngine_Riven *vm) :
+		RivenStack(vm, kStackRspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/rspit.h b/engines/mohawk/riven_stacks/rspit.h
new file mode 100644
index 0000000..67384cb
--- /dev/null
+++ b/engines/mohawk/riven_stacks/rspit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_RSPIT_H
+#define RIVEN_STACKS_RSPIT_H
+
+#include "mohawk/riven_stack.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class RSpit : public RivenStack {
+public:
+	RSpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stacks/tspit.cpp b/engines/mohawk/riven_stacks/tspit.cpp
new file mode 100644
index 0000000..aa4634a
--- /dev/null
+++ b/engines/mohawk/riven_stacks/tspit.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_stacks/tspit.h"
+
+#include "engines/mohawk/riven.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+TSpit::TSpit(MohawkEngine_Riven *vm) :
+		DomeSpit(vm, kStackTspit) {
+
+}
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/tspit.h b/engines/mohawk/riven_stacks/tspit.h
new file mode 100644
index 0000000..90c62a0
--- /dev/null
+++ b/engines/mohawk/riven_stacks/tspit.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_STACKS_TSPIT_H
+#define RIVEN_STACKS_TSPIT_H
+
+#include "mohawk/riven_stacks/domespit.h"
+
+namespace Mohawk {
+namespace RivenStacks {
+
+class TSpit : public DomeSpit {
+public:
+	TSpit(MohawkEngine_Riven *vm);
+
+};
+
+} // End of namespace RivenStacks
+} // End of namespace Mohawk
+
+#endif


Commit: 14bbf8aab4ed187ec1e6da9e61a1acf6601d0332
    https://github.com/scummvm/scummvm/commit/14bbf8aab4ed187ec1e6da9e61a1acf6601d0332
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the external commands to their respective stacks

Changed paths:
  R engines/mohawk/riven_external.cpp
  R engines/mohawk/riven_external.h
    engines/mohawk/POTFILES
    engines/mohawk/console.cpp
    engines/mohawk/module.mk
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h
    engines/mohawk/riven_stacks/aspit.cpp
    engines/mohawk/riven_stacks/aspit.h
    engines/mohawk/riven_stacks/bspit.cpp
    engines/mohawk/riven_stacks/bspit.h
    engines/mohawk/riven_stacks/domespit.cpp
    engines/mohawk/riven_stacks/domespit.h
    engines/mohawk/riven_stacks/gspit.cpp
    engines/mohawk/riven_stacks/gspit.h
    engines/mohawk/riven_stacks/jspit.cpp
    engines/mohawk/riven_stacks/jspit.h
    engines/mohawk/riven_stacks/ospit.cpp
    engines/mohawk/riven_stacks/ospit.h
    engines/mohawk/riven_stacks/pspit.cpp
    engines/mohawk/riven_stacks/pspit.h
    engines/mohawk/riven_stacks/rspit.cpp
    engines/mohawk/riven_stacks/rspit.h
    engines/mohawk/riven_stacks/tspit.cpp
    engines/mohawk/riven_stacks/tspit.h


diff --git a/engines/mohawk/POTFILES b/engines/mohawk/POTFILES
index 42d1d08..5181975 100644
--- a/engines/mohawk/POTFILES
+++ b/engines/mohawk/POTFILES
@@ -2,5 +2,5 @@ engines/mohawk/detection.cpp
 engines/mohawk/dialogs.cpp
 engines/mohawk/myst.cpp
 engines/mohawk/riven.cpp
-engines/mohawk/riven_external.cpp
+engines/mohawk/riven_stacks/aspit.cpp
 engines/mohawk/mohawk.cpp
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 3f6f241..64d3a00 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -42,9 +42,9 @@
 #ifdef ENABLE_RIVEN
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
-#include "mohawk/riven_external.h"
 #include "mohawk/riven_sound.h"
 #include "mohawk/riven_stack.h"
+#include "mohawk/riven_stacks/domespit.h"
 #endif
 
 namespace Mohawk {
@@ -675,11 +675,11 @@ bool RivenConsole::Cmd_Combos(int argc, const char **argv) {
 
 	debugPrintf("Telescope Combo:\n  ");
 	for (int i = 0; i < 5; i++)
-		debugPrintf("%d ", _vm->_externalScriptHandler->getComboDigit(teleCombo, i));
+		debugPrintf("%d ", _vm->getStack()->getComboDigit(teleCombo, i));
 
 	debugPrintf("\nPrison Combo:\n  ");
 	for (int i = 0; i < 5; i++)
-		debugPrintf("%d ", _vm->_externalScriptHandler->getComboDigit(prisonCombo, i));
+		debugPrintf("%d ", _vm->getStack()->getComboDigit(prisonCombo, i));
 
 	debugPrintf("\nDome Combo:\n  ");
 	for (int i = 1; i <= 25; i++)
@@ -691,10 +691,16 @@ bool RivenConsole::Cmd_Combos(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_SliderState(int argc, const char **argv) {
+	RivenStacks::DomeSpit *domeSpit = dynamic_cast<RivenStacks::DomeSpit *>(_vm->getStack());
+	if (!domeSpit) {
+		debugPrintf("No dome in this stack\n");
+		return true;
+	}
+
 	if (argc > 1)
-		_vm->_externalScriptHandler->setDomeSliderState((uint32)atoi(argv[1]));
+		domeSpit->setDomeSliderState((uint32)atoi(argv[1]));
 
-	debugPrintf("Dome Slider State = %08x\n", _vm->_externalScriptHandler->getDomeSliderState());
+	debugPrintf("Dome Slider State = %08x\n", domeSpit->getDomeSliderState());
 	return true;
 }
 
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 2f6d216..292310e 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -54,7 +54,6 @@ ifdef ENABLE_RIVEN
 MODULE_OBJS += \
 	riven.o \
 	riven_card.o \
-	riven_external.o \
 	riven_graphics.o \
 	riven_saveload.o \
 	riven_scripts.o \
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index d6c6754..03af714 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -32,7 +32,6 @@
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
-#include "mohawk/riven_external.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_saveload.h"
 #include "mohawk/riven_sound.h"
@@ -69,7 +68,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_stack = nullptr;
 	_gfx = nullptr;
 	_sound = nullptr;
-	_externalScriptHandler = nullptr;
 	_rnd = nullptr;
 	_scriptMan = nullptr;
 	_console = nullptr;
@@ -106,7 +104,6 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
 	delete _sound;
 	delete _gfx;
 	delete _console;
-	delete _externalScriptHandler;
 	delete _extrasFile;
 	delete _saveLoad;
 	delete _scriptMan;
@@ -137,7 +134,6 @@ Common::Error MohawkEngine_Riven::run() {
 	_sound = new RivenSoundManager(this);
 	_console = new RivenConsole(this);
 	_saveLoad = new RivenSaveLoad(this, _saveFileMan);
-	_externalScriptHandler = new RivenExternal(this);
 	_optionsDialog = new RivenOptionsDialog(this);
 	_scriptMan = new RivenScriptManager(this);
 
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 6d268d9..b2e9188 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -37,7 +37,6 @@ namespace Mohawk {
 struct MohawkGameDescription;
 class MohawkArchive;
 class RivenGraphics;
-class RivenExternal;
 class RivenConsole;
 class RivenSaveLoad;
 class RivenOptionsDialog;
@@ -97,7 +96,6 @@ public:
 
 	RivenSoundManager *_sound;
 	RivenGraphics *_gfx;
-	RivenExternal *_externalScriptHandler;
 	Common::RandomSource *_rnd;
 	RivenScriptManager *_scriptMan;
 
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
deleted file mode 100644
index 3855123..0000000
--- a/engines/mohawk/riven_external.cpp
+++ /dev/null
@@ -1,2812 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "mohawk/cursors.h"
-#include "mohawk/riven.h"
-#include "mohawk/riven_card.h"
-#include "mohawk/riven_external.h"
-#include "mohawk/riven_graphics.h"
-#include "mohawk/riven_sound.h"
-#include "mohawk/riven_stack.h"
-#include "mohawk/video.h"
-
-#include "gui/message.h"
-#include "common/events.h"
-#include "common/system.h"
-#include "common/translation.h"
-
-namespace Mohawk {
-
-static const uint32 kDomeSliderDefaultState = 0x01F00000;
-static const uint32 kDomeSliderSlotCount = 25;
-
-RivenExternal::RivenExternal(MohawkEngine_Riven *vm) : _vm(vm) {
-	setupCommands();
-	_sliderState = kDomeSliderDefaultState;
-}
-
-RivenExternal::~RivenExternal() {
-	for (uint32 i = 0; i < _externalCommands.size(); i++)
-		delete _externalCommands[i];
-
-	_externalCommands.clear();
-}
-
-void RivenExternal::setupCommands() {
-	// aspit (Main Menu, Books, Setup) external commands
-	COMMAND(xastartupbtnhide);
-	COMMAND(xasetupcomplete);
-	COMMAND(xaatrusopenbook);
-	COMMAND(xaatrusbookback);
-	COMMAND(xaatrusbookprevpage);
-	COMMAND(xaatrusbooknextpage);
-	COMMAND(xacathopenbook);
-	COMMAND(xacathbookback);
-	COMMAND(xacathbookprevpage);
-	COMMAND(xacathbooknextpage);
-	COMMAND(xtrapbookback);
-	COMMAND(xatrapbookclose);
-	COMMAND(xatrapbookopen);
-	COMMAND(xarestoregame);
-	COMMAND(xadisablemenureturn);
-	COMMAND(xaenablemenureturn);
-	COMMAND(xalaunchbrowser);
-	COMMAND(xadisablemenuintro);
-	COMMAND(xaenablemenuintro);
-	COMMAND(xademoquit);
-	COMMAND(xaexittomain);
-
-	// bspit (Bookmaking Island) external commands
-	COMMAND(xblabopenbook);
-	COMMAND(xblabbookprevpage);
-	COMMAND(xblabbooknextpage);
-	COMMAND(xsoundplug);
-	COMMAND(xbchangeboiler);
-	COMMAND(xbupdateboiler);
-	COMMAND(xbsettrap);
-	COMMAND(xbcheckcatch);
-	COMMAND(xbait);
-	COMMAND(xbfreeytram);
-	COMMAND(xbaitplate);
-	COMMAND(xbisland190_opencard);
-	COMMAND(xbisland190_resetsliders);
-	COMMAND(xbisland190_slidermd);
-	COMMAND(xbisland190_slidermw);
-	COMMAND(xbscpbtn);
-	COMMAND(xbisland_domecheck);
-	COMMAND(xvalvecontrol);
-	COMMAND(xbchipper);
-
-	// gspit (Garden Island) external commands
-	COMMAND(xgresetpins);
-	COMMAND(xgrotatepins);
-	COMMAND(xgpincontrols);
-	COMMAND(xgisland25_opencard);
-	COMMAND(xgisland25_resetsliders);
-	COMMAND(xgisland25_slidermd);
-	COMMAND(xgisland25_slidermw);
-	COMMAND(xgscpbtn);
-	COMMAND(xgisland1490_domecheck);
-	COMMAND(xgplateau3160_dopools);
-	COMMAND(xgwt200_scribetime);
-	COMMAND(xgwt900_scribe);
-	COMMAND(xgplaywhark);
-	COMMAND(xgrviewer);
-	COMMAND(xgwharksnd);
-	COMMAND(xglview_prisonoff);
-	COMMAND(xglview_villageoff);
-	COMMAND(xglviewer);
-	COMMAND(xglview_prisonon);
-	COMMAND(xglview_villageon);
-
-	// jspit (Jungle Island) external commands
-	COMMAND(xreseticons);
-	COMMAND(xicon);
-	COMMAND(xcheckicons);
-	COMMAND(xtoggleicon);
-	COMMAND(xjtunnel103_pictfix);
-	COMMAND(xjtunnel104_pictfix);
-	COMMAND(xjtunnel105_pictfix);
-	COMMAND(xjtunnel106_pictfix);
-	COMMAND(xvga1300_carriage);
-	COMMAND(xjdome25_resetsliders);
-	COMMAND(xjdome25_slidermd);
-	COMMAND(xjdome25_slidermw);
-	COMMAND(xjscpbtn);
-	COMMAND(xjisland3500_domecheck);
-	COMMAND(xhandlecontroldown);
-	COMMAND(xhandlecontrolmid);
-	COMMAND(xhandlecontrolup);
-	COMMAND(xjplaybeetle_550);
-	COMMAND(xjplaybeetle_600);
-	COMMAND(xjplaybeetle_950);
-	COMMAND(xjplaybeetle_1050);
-	COMMAND(xjplaybeetle_1450);
-	COMMAND(xjlagoon700_alert);
-	COMMAND(xjlagoon800_alert);
-	COMMAND(xjlagoon1500_alert);
-	COMMAND(xschool280_playwhark);
-	COMMAND(xjschool280_resetleft);
-	COMMAND(xjschool280_resetright);
-	COMMAND(xjatboundary);
-
-	// ospit (Gehn's Office) external commands
-	COMMAND(xorollcredittime);
-	COMMAND(xbookclick);
-	COMMAND(xooffice30_closebook);
-	COMMAND(xobedroom5_closedrawer);
-	COMMAND(xogehnopenbook);
-	COMMAND(xogehnbookprevpage);
-	COMMAND(xogehnbooknextpage);
-	COMMAND(xgwatch);
-
-	// pspit (Prison Island) external commands
-	COMMAND(xpisland990_elevcombo);
-	COMMAND(xpscpbtn);
-	COMMAND(xpisland290_domecheck);
-	COMMAND(xpisland25_opencard);
-	COMMAND(xpisland25_resetsliders);
-	COMMAND(xpisland25_slidermd);
-	COMMAND(xpisland25_slidermw);
-
-	// rspit (Rebel Age) external commands
-	COMMAND(xrshowinventory);
-	COMMAND(xrhideinventory);
-	COMMAND(xrcredittime);
-	COMMAND(xrwindowsetup);
-
-	// tspit (Temple Island) external commands
-	COMMAND(xtexterior300_telescopedown);
-	COMMAND(xtexterior300_telescopeup);
-	COMMAND(xtisland390_covercombo);
-	COMMAND(xtatrusgivesbooks);
-	COMMAND(xtchotakesbook);
-	COMMAND(xthideinventory);
-	COMMAND(xt7500_checkmarbles);
-	COMMAND(xt7600_setupmarbles);
-	COMMAND(xt7800_setup);
-	COMMAND(xdrawmarbles);
-	COMMAND(xtakeit);
-	COMMAND(xtscpbtn);
-	COMMAND(xtisland4990_domecheck);
-	COMMAND(xtisland5056_opencard);
-	COMMAND(xtisland5056_resetsliders);
-	COMMAND(xtisland5056_slidermd);
-	COMMAND(xtisland5056_slidermw);
-	COMMAND(xtatboundary);
-
-	// Common external commands
-	COMMAND(xflies);
-}
-
-void RivenExternal::runCommand(uint16 argc, uint16 *argv) {
-	Common::String externalCommandName = _vm->getStack()->getName(kExternalCommandNames, argv[0]);
-
-	for (uint16 i = 0; i < _externalCommands.size(); i++)
-		if (externalCommandName == _externalCommands[i]->desc) {
-			debug(0, "Running Riven External Command \'%s\'", externalCommandName.c_str());
-			(this->*(_externalCommands[i]->proc)) (argv[1], argv[1] ? argv + 2 : NULL);
-			return;
-		}
-
-	error("Unknown external command \'%s\'", externalCommandName.c_str());
-}
-
-void RivenExternal::runDemoBoundaryDialog() {
-	GUI::MessageDialog dialog(_("Exploration beyond this point available only within the full version of\n"
-							  "the game."));
-	dialog.runModal();
-}
-
-void RivenExternal::runEndGame(uint16 video, uint32 delay) {
-	_vm->_sound->stopAllSLST();
-	_vm->_video->playMovieRiven(video);
-	runCredits(video, delay);
-}
-
-void RivenExternal::runCredits(uint16 video, uint32 delay) {
-	// Initialize our credits state
-	_vm->_cursor->hideCursor();
-	_vm->_gfx->beginCredits();
-	uint nextCreditsFrameStart = 0;
-
-	VideoEntryPtr videoPtr = _vm->_video->findVideoRiven(video);
-
-	while (!_vm->shouldQuit() && _vm->_gfx->getCurCreditsImage() <= 320) {
-		if (videoPtr->getCurFrame() >= (int32)videoPtr->getFrameCount() - 1) {
-			if (nextCreditsFrameStart == 0) {
-				// Set us up to start after delay ms
-				nextCreditsFrameStart = _vm->_system->getMillis() + delay;
-			} else if (_vm->_system->getMillis() >= nextCreditsFrameStart) {
-				// the first two frames stay on for 4 seconds
-				// the rest of the scroll updates happen at 30Hz
-				if (_vm->_gfx->getCurCreditsImage() < 304)
-					nextCreditsFrameStart = _vm->_system->getMillis() + 4000;
-				else
-					nextCreditsFrameStart = _vm->_system->getMillis() + 1000 / 30;
-
-				_vm->_gfx->updateCredits();
-			}
-		} else if (_vm->_video->updateMovies())
-			_vm->_system->updateScreen();
-
-		Common::Event event;
-		while (_vm->_system->getEventManager()->pollEvent(event))
-			;
-
-		_vm->_system->delayMillis(10);
-	}
-
-	_vm->setGameOver();
-}
-
-void RivenExternal::runDomeButtonMovie() {
-	// This command just plays the video of the button moving down and up.
-	_vm->_video->playMovieBlockingRiven(2);
-}
-
-void RivenExternal::runDomeCheck() {
-	// Check if we clicked while the golden frame was showing
-
-	VideoEntryPtr video = _vm->_video->findVideoRiven(1);
-	assert(video);
-
-	int32 curFrame = video->getCurFrame();
-	int32 frameCount = video->getFrameCount();
-
-	// The final frame of the video is the 'golden' frame (double meaning: the
-	// frame that is the magic one is the one with the golden symbol) but we
-	// give a 3 frame leeway in either direction.
-	if (frameCount - curFrame < 3 || curFrame < 3)
-		_vm->_vars["domecheck"] = 1;
-}
-
-void RivenExternal::resetDomeSliders(uint16 soundId, uint16 startHotspot) {
-	// The rightmost slider should move left until it finds the next slider,
-	// then those two continue until they find the third slider. This continues
-	// until all five sliders have returned their starting slots.
-	byte slidersFound = 0;
-	for (uint32 i = 0; i < kDomeSliderSlotCount; i++) {
-		if (_sliderState & (1 << i)) {
-			// A slider occupies this spot. Increase the number of sliders we
-			// have found, but we're not doing any moving this iteration.
-			slidersFound++;
-		} else {
-			// Move all the sliders we have found over one slot
-			for (byte j = 0; j < slidersFound; j++) {
-				_sliderState &= ~(1 << (i - j - 1));
-				_sliderState |= 1 << (i - j);
-			}
-
-			// If we have at least one found slider, it has now moved
-			// so we should redraw and play a tick sound
-			if (slidersFound) {
-				_vm->_sound->playSound(soundId);
-				drawDomeSliders(startHotspot);
-				_vm->_system->delayMillis(100);
-			}
-		}
-	}
-
-	// Sanity checks - the slider count should always be 5 and we should end up at
-	// the default state after moving them all over.
-	assert(slidersFound == 5);
-	assert(_sliderState == kDomeSliderDefaultState);
-}
-
-void RivenExternal::checkDomeSliders() {
-	RivenHotspot *resetSlidersHotspot = _vm->getCard()->getHotspotByName("ResetSliders");
-	RivenHotspot *openDomeHotspot = _vm->getCard()->getHotspotByName("OpenDome");
-
-	// Let's see if we're all matched up...
-	if (_vm->_vars["adomecombo"] == _sliderState) {
-		// Set the button hotspot to the open dome hotspot
-		resetSlidersHotspot->enable(false);
-		openDomeHotspot->enable(true);
-	} else {
-		// Set the button hotspot to the reset sliders hotspot
-		resetSlidersHotspot->enable(true);
-		openDomeHotspot->enable(false);
-	}
-}
-
-void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
-	// Set the cursor based on _sliderState and what hotspot we're over
-	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
-		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
-			if (_sliderState & (1 << (24 - i)))
-				_vm->_cursor->setCursor(kRivenOpenHandCursor);
-			else
-				_vm->_cursor->setCursor(kRivenMainCursor);
-			_vm->_system->updateScreen();
-			break;
-		}
-	}
-}
-
-void RivenExternal::dragDomeSlider(uint16 soundId, uint16 startHotspot) {
-	int16 foundSlider = -1;
-
-	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
-		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
-			// If the slider is not at this hotspot, we can't do anything else
-			if (!(_sliderState & (1 << (24 - i))))
-				return;
-
-			foundSlider = i;
-			break;
-		}
-	}
-
-	// We're not over any slider
-	if (foundSlider < 0)
-		return;
-
-	// We've clicked down, so show the closed hand cursor
-	_vm->_cursor->setCursor(kRivenClosedHandCursor);
-	_vm->_system->updateScreen();
-
-	bool done = false;
-	while (!done) {
-		Common::Event event;
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_MOUSEMOVE:
-				if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider)))) {
-					RivenHotspot *nextHotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + foundSlider + 1);
-					if (nextHotspot->containsPoint(event.mouse)) {
-						// We've moved the slider right one space
-						_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
-						foundSlider++;
-						_sliderState |= 1 << (24 - foundSlider);
-
-						// Now play a click sound and redraw
-						_vm->_sound->playSound(soundId);
-						drawDomeSliders(startHotspot);
-					}
-				} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider)))) {
-					RivenHotspot *previousHotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + foundSlider - 1);
-					if (previousHotspot->containsPoint(event.mouse)) {
-						// We've moved the slider left one space
-						_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
-						foundSlider--;
-						_sliderState |= 1 << (24 - foundSlider);
-
-						// Now play a click sound and redraw
-						_vm->_sound->playSound(soundId);
-						drawDomeSliders(startHotspot);
-					}
-				} else
-					_vm->_system->updateScreen(); // A normal update for the cursor
-				break;
-			case Common::EVENT_LBUTTONUP:
-				done = true;
-				break;
-			default:
-				break;
-			}
-		}
-		_vm->_system->delayMillis(10);
-	}
-
-	// Check to see if we have the right combination
-	checkDomeSliders();
-}
-
-void RivenExternal::drawDomeSliders(uint16 startHotspot) {
-	Common::Rect dstAreaRect = Common::Rect(200, 250, 420, 319);
-
-	// On pspit, the rect is different by two pixels
-	// (alternatively, we could just use hotspot 3 here, but only on pspit is there a hotspot for this)
-	if (_vm->getStack()->getId() == kStackPspit)
-		dstAreaRect.translate(-2, 0);
-
-	// Find out bitmap id
-	uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
-
-	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
-		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
-
-		Common::Rect srcRect = hotspot->getRect();
-		srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
-
-		Common::Rect dstRect = hotspot->getRect();
-
-		if (_sliderState & (1 << (24 - i)))
-			_vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect);
-		else
-			_vm->_gfx->drawImageRect(bitmapId + 1, srcRect, dstRect);
-	}
-
-	_vm->_gfx->updateScreen();
-}
-
-// ------------------------------------------------------------------------------------
-// aspit (Main Menu, Books, Setup) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xastartupbtnhide(uint16 argc, uint16 *argv) {
-	// The original game hides the start/setup buttons depending on an ini entry.
-	// It's safe to ignore this command.
-}
-
-void RivenExternal::xasetupcomplete(uint16 argc, uint16 *argv) {
-	// The original game sets an ini entry to disable the setup button and use the
-	// start button only. It's safe to ignore this part of the command.
-	_vm->_sound->stopSound();
-	_vm->changeToCard(1);
-}
-
-void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
-	// Get the variable
-	uint32 &page = _vm->_vars["aatruspage"];
-
-	// Set hotspots depending on the page
-	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
-	RivenHotspot *nextPage = _vm->getCard()->getHotspotByName("nextpage");
-	RivenHotspot *prevPage = _vm->getCard()->getHotspotByName("prevpage");
-	if (page == 1) {
-		prevPage->enable(false);
-		nextPage->enable(false);
-		openBook->enable(true);
-	} else {
-		prevPage->enable(true);
-		nextPage->enable(true);
-		openBook->enable(false);
-	}
-
-	// Draw the image of the page
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xaatrusbookback(uint16 argc, uint16 *argv) {
-	// Return to where we were before entering the book
-	_vm->changeToStack(_vm->_vars["returnstackid"]);
-	_vm->changeToCard(_vm->_vars["returncardid"]);
-}
-
-void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
-	// Get the page variable
-	uint32 &page = _vm->_vars["aatruspage"];
-
-	// Decrement the page if it's not the first page
-	if (page == 1)
-		return;
-	page--;
-
-	// Play the page turning sound
-	if (_vm->getFeatures() & GF_DEMO)
-		_vm->_sound->playSound(4);
-	else
-		_vm->_sound->playSound(3);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(1);
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
-	// Get the page variable
-	uint32 &page = _vm->_vars["aatruspage"];
-
-	// Increment the page if it's not the last page
-	if (((_vm->getFeatures() & GF_DEMO) && page == 6) || page == 10)
-		return;
-	page++;
-
-	// Play the page turning sound
-	if (_vm->getFeatures() & GF_DEMO)
-		_vm->_sound->playSound(5);
-	else
-		_vm->_sound->playSound(4);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(0);
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
-	// Get the variable
-	uint32 page = _vm->_vars["acathpage"];
-
-	// Set hotspots depending on the page
-	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
-	RivenHotspot *nextPage = _vm->getCard()->getHotspotByName("nextpage");
-	RivenHotspot *prevPage = _vm->getCard()->getHotspotByName("prevpage");
-	if (page == 1) {
-		prevPage->enable(false);
-		nextPage->enable(false);
-		openBook->enable(true);
-	} else {
-		prevPage->enable(true);
-		nextPage->enable(true);
-		openBook->enable(false);
-	}
-
-	// Draw the image of the page
-	_vm->getCard()->drawPicture(page);
-
-	// Draw the white page edges
-	if (page > 1 && page < 5)
-		_vm->getCard()->drawPicture(50);
-	else if (page > 5)
-		_vm->getCard()->drawPicture(51);
-
-	if (page == 28) {
-		// Draw the telescope combination
-		// The images for the numbers are tBMP's 13 through 17.
-		// The start point is at (156, 247)
-		uint32 teleCombo = _vm->_vars["tcorrectorder"];
-		static const uint16 kNumberWidth = 32;
-		static const uint16 kNumberHeight = 25;
-		static const uint16 kDstX = 156;
-		static const uint16 kDstY = 247;
-
-		for (byte i = 0; i < 5; i++) {
-			uint16 offset = (getComboDigit(teleCombo, i) - 1) * kNumberWidth;
-			Common::Rect srcRect = Common::Rect(offset, 0, offset + kNumberWidth, kNumberHeight);
-			Common::Rect dstRect = Common::Rect(i * kNumberWidth + kDstX, kDstY, (i + 1) * kNumberWidth + kDstX, kDstY + kNumberHeight);
-			_vm->_gfx->drawImageRect(i + 13, srcRect, dstRect);
-		}
-	}
-}
-
-void RivenExternal::xacathbookback(uint16 argc, uint16 *argv) {
-	// Return to where we were before entering the book
-	_vm->changeToStack(_vm->_vars["returnstackid"]);
-	_vm->changeToCard(_vm->_vars["returncardid"]);
-}
-
-void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
-	// Get the variable
-	uint32 &page = _vm->_vars["acathpage"];
-
-	// Increment the page if it's not the first page
-	if (page == 1)
-		return;
-	page--;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(5);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(3);
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
-	// Get the variable
-	uint32 &page = _vm->_vars["acathpage"];
-
-	// Increment the page if it's not the last page
-	if (page == 49)
-		return;
-	page++;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(6);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(2);
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {
-	// Return to where we were before entering the book
-	_vm->_vars["atrap"] = 0;
-	_vm->changeToStack(_vm->_vars["returnstackid"]);
-	_vm->changeToCard(_vm->_vars["returncardid"]);
-}
-
-void RivenExternal::xatrapbookclose(uint16 argc, uint16 *argv) {
-	// Close the trap book
-	_vm->_vars["atrap"] = 0;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(8);
-
-	_vm->refreshCard();
-}
-
-void RivenExternal::xatrapbookopen(uint16 argc, uint16 *argv) {
-	// Open the trap book
-	_vm->_vars["atrap"] = 1;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(9);
-
-	_vm->refreshCard();
-}
-
-void RivenExternal::xarestoregame(uint16 argc, uint16 *argv) {
-	// Launch the load game dialog
-	_vm->runLoadDialog();
-}
-
-void RivenExternal::xadisablemenureturn(uint16 argc, uint16 *argv) {
-	// This function would normally enable the Windows menu item for
-	// returning to the main menu. Ctrl+r will do this instead.
-	// The original also had this shortcut.
-}
-
-void RivenExternal::xaenablemenureturn(uint16 argc, uint16 *argv) {
-	// This function would normally enable the Windows menu item for
-	// returning to the main menu. Ctrl+r will do this instead.
-	// The original also had this shortcut.
-}
-
-void RivenExternal::xalaunchbrowser(uint16 argc, uint16 *argv) {
-	// Well, we can't launch a browser for obvious reasons ;)
-	// The original text is as follows (for reference):
-
-	// If you have an auto-dial configured connection to the Internet,
-	// please select YES below.
-	//
-	// America Online and CompuServe users may experience difficulty. If
-	// you find that you are unable to connect, please quit the Riven
-	// Demo, launch your browser and type in the following URL:
-	//
-	//     www.redorb.com/buyriven
-	//
-	// Would you like to attempt to make the connection?
-	//
-	// [YES] [NO]
-
-	GUI::MessageDialog dialog(_("At this point, the Riven Demo would\n"
-							  "ask if you would like to open a web browser\n"
-							  "to bring you to the Red Orb store to buy\n"
-							  "the game. ScummVM cannot do that and\n"
-							  "the site no longer exists."));
-	dialog.runModal();
-}
-
-void RivenExternal::xadisablemenuintro(uint16 argc, uint16 *argv) {
-	// This function would normally enable the Windows menu item for
-	// playing the intro. Ctrl+p will play the intro movies instead.
-	// The original also had this shortcut.
-
-	// Hide the "exit" button here
-	_vm->_gfx->hideInventory();
-}
-
-void RivenExternal::xaenablemenuintro(uint16 argc, uint16 *argv) {
-	// This function would normally enable the Windows menu item for
-	// playing the intro. Ctrl+p will play the intro movies instead.
-	// The original also had this shortcut.
-
-	// Show the "exit" button here
-	_vm->_gfx->showInventory();
-}
-
-void RivenExternal::xademoquit(uint16 argc, uint16 *argv) {
-	// Exactly as it says on the tin. In the demo, this function quits.
-	_vm->setGameOver();
-}
-
-void RivenExternal::xaexittomain(uint16 argc, uint16 *argv) {
-	// One could potentially implement this function, but there would be no
-	// point. This function is only used in the demo's aspit card 9 update
-	// screen script. However, card 9 is not accessible from the game without
-	// jumping to the card and there's nothing going on in the card so it
-	// never gets called. There's also no card 9 in the full game, so the
-	// functionality of this card was likely removed before release. The
-	// demo executable references some other external commands relating to
-	// setting and getting the volume, as well as drawing the volume. I'd
-	// venture to guess that this would have been some sort of options card
-	// replaced with the Windows/Mac API in the final product.
-	//
-	// Yeah, this function is just dummied and holds a big comment ;)
-}
-
-// ------------------------------------------------------------------------------------
-// bspit (Bookmaking Island) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xblabopenbook(uint16 argc, uint16 *argv) {
-	// Get the variable
-	uint32 page = _vm->_vars["blabpage"];
-
-	// Draw the image of the page based on the blabbook variable
-	_vm->getCard()->drawPicture(page);
-
-	if (page == 14) {
-		// Draw the dome combination
-		// The images for the numbers are tBMP's 364 through 368
-		// The start point is at (240, 82)
-		uint32 domeCombo = _vm->_vars["adomecombo"];
-		static const uint16 kNumberWidth = 32;
-		static const uint16 kNumberHeight = 24;
-		static const uint16 kDstX = 240;
-		static const uint16 kDstY = 82;
-		byte numCount = 0;
-
-		for (int bitPos = 24; bitPos >= 0; bitPos--) {
-			if (domeCombo & (1 << bitPos)) {
-				uint16 offset = (24 - bitPos) * kNumberWidth;
-				Common::Rect srcRect = Common::Rect(offset, 0, offset + kNumberWidth, kNumberHeight);
-				Common::Rect dstRect = Common::Rect(numCount * kNumberWidth + kDstX, kDstY, (numCount + 1) * kNumberWidth + kDstX, kDstY + kNumberHeight);
-				_vm->_gfx->drawImageRect(numCount + 364, srcRect, dstRect);
-				numCount++;
-			}
-		}
-
-		assert(numCount == 5); // Sanity check
-	}
-}
-
-void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
-	// Get the page variable
-	uint32 &page = _vm->_vars["blabpage"];
-
-	// Decrement the page if it's not the first page
-	if (page == 1)
-		return;
-	page--;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(22);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(1);
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
-	// Get the page variable
-	uint32 &page = _vm->_vars["blabpage"];
-
-	// Increment the page if it's not the last page
-	if (page == 22)
-		return;
-	page++;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(23);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(0);
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
-	if (_vm->_vars["bheat"] != 0)
-		_vm->getCard()->playSound(1);
-	else if (_vm->_vars["bcratergg"] != 0)
-		_vm->getCard()->playSound(2);
-	else
-		_vm->getCard()->playSound(3);
-}
-
-void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
-	uint32 heat = _vm->_vars["bheat"];
-	uint32 water = _vm->_vars["bblrwtr"];
-	uint32 platform = _vm->_vars["bblrgrt"];
-
-	// Stop any background videos
-	_vm->_video->stopVideos();
-
-	if (argv[0] == 1) {
-		// Water is filling/draining from the boiler
-		if (water == 0) {
-			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(12));
-			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
-		} else if (heat == 1) {
-			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(22));
-			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(19));
-		} else {
-			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(16));
-			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(13));
-		}
-	} else if (argv[0] == 2 && water != 0) {
-		if (heat == 1) {
-			// Turning on the heat
-			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(23));
-			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(20));
-		} else {
-			// Turning off the heat
-			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(18));
-			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(15));
-		}
-	} else if (argv[0] == 3) {
-		if (platform == 1) {
-			// Lowering the platform
-			if (water == 1) {
-				if (heat == 1)
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(24));
-				else
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(17));
-			} else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
-		} else {
-			// Raising the platform
-			if (water == 1) {
-				if (heat == 1)
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(21));
-				else
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(14));
-			} else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
-		}
-	}
-
-	if (argc > 1)
-		_vm->getCard()->playSound(argv[1]);
-	else if (argv[0] == 2)
-		_vm->getCard()->playSound(1);
-
-	_vm->_cursor->setCursor(kRivenHideCursor);
-	_vm->_video->playMovieBlockingRiven(11);
-}
-
-void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
-	if (_vm->_vars["bheat"] != 0) {
-		if (_vm->_vars["bblrgrt"] == 0) {
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
-			_vm->_video->playMovieRiven(8);
-		} else {
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(7));
-			_vm->_video->playMovieRiven(7);
-		}
-	} else {
-		VideoEntryPtr video = _vm->_video->findVideoRiven(7);
-		if (video)
-			video->setEnabled(false);
-		video = _vm->_video->findVideoRiven(8);
-		if (video)
-			video->setEnabled(false);
-	}
-}
-
-static void ytramTrapTimer(MohawkEngine_Riven *vm) {
-	// Remove this timer
-	vm->removeTimer();
-
-	// Check if we've caught a Ytram
-	vm->_externalScriptHandler->checkYtramCatch(true);
-}
-
-void RivenExternal::xbsettrap(uint16 argc, uint16 *argv) {
-	// Set the Ytram trap
-
-	// We can catch the Ytram between 10 seconds and 3 minutes from now
-	uint32 timeUntilCatch = _vm->_rnd->getRandomNumberRng(10, 60 * 3) * 1000;
-	_vm->_vars["bytramtime"] = timeUntilCatch + _vm->getTotalPlayTime();
-
-	// And set the timer too
-	_vm->installTimer(&ytramTrapTimer, timeUntilCatch);
-}
-
-void RivenExternal::checkYtramCatch(bool playSound) {
-	// Check if we've caught a Ytram
-
-	uint32 &ytramTime = _vm->_vars["bytramtime"];
-
-	// If the trap still has not gone off, reinstall our timer
-	// This is in case you set the trap, walked away, and returned
-	if (_vm->getTotalPlayTime() < ytramTime) {
-		_vm->installTimer(&ytramTrapTimer, ytramTime - _vm->getTotalPlayTime());
-		return;
-	}
-
-	// Increment the movie per catch (max = 3)
-	uint32 &ytramMovie = _vm->_vars["bytram"];
-	ytramMovie++;
-	if (ytramMovie > 3)
-		ytramMovie = 3;
-
-	// Reset variables
-	_vm->_vars["bytrapped"] = 1;
-	_vm->_vars["bbait"] = 0;
-	_vm->_vars["bytrap"] = 0;
-	ytramTime = 0;
-
-	// Play the capture sound, if requested
-	if (playSound)
-		_vm->_sound->playSound(33);
-}
-
-void RivenExternal::xbcheckcatch(uint16 argc, uint16 *argv) {
-	// Just pass our parameter along...
-	checkYtramCatch(argv[0] != 0);
-}
-
-void RivenExternal::xbait(uint16 argc, uint16 *argv) {
-	// Set the cursor to the pellet
-	_vm->_cursor->setCursor(kRivenPelletCursor);
-	_vm->_system->updateScreen();
-
-	// Loop until the player lets go (or quits)
-	Common::Event event;
-	bool mouseDown = true;
-	while (mouseDown) {
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONUP)
-				mouseDown = false;
-			else if (event.type == Common::EVENT_MOUSEMOVE)
-				_vm->_system->updateScreen();
-			else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
-				return;
-		}
-
-		_vm->_system->delayMillis(10); // Take it easy on the CPU
-	}
-
-	// Set back the cursor
-	_vm->_cursor->setCursor(kRivenMainCursor);
-	_vm->_system->updateScreen();
-
-	RivenHotspot *bait = _vm->getCard()->getHotspotByBlstId(9);
-	RivenHotspot *baitPlate = _vm->getCard()->getHotspotByBlstId(16);
-
-	// Set the bait if we put it on the plate
-	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
-		_vm->_vars["bbait"] = 1;
-		_vm->getCard()->drawPicture(4);
-
-		bait->enable(false); // Disable bait hotspot
-		baitPlate->enable(true); // Enable baitplate hotspot
-	}
-}
-
-void RivenExternal::xbfreeytram(uint16 argc, uint16 *argv) {
-	// Play a random Ytram movie after freeing it
-	uint16 mlstId;
-
-	switch (_vm->_vars["bytram"]) {
-	case 1:
-		mlstId = 11;
-		break;
-	case 2:
-		mlstId = 12;
-		break;
-	default:
-		mlstId = _vm->_rnd->getRandomNumberRng(13, 15);
-		break;
-	}
-
-	// Activate the MLST and play the video
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId));
-	_vm->_video->playMovieBlockingRiven(11);
-
-	// Now play the second movie
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId + 5));
-	_vm->_video->playMovieBlockingRiven(12);
-}
-
-void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
-	// Remove the pellet from the plate and put it in your hand
-	_vm->_cursor->setCursor(kRivenPelletCursor);
-	_vm->getCard()->drawPicture(3);
-
-	// Loop until the player lets go (or quits)
-	Common::Event event;
-	bool mouseDown = true;
-	while (mouseDown) {
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONUP)
-				mouseDown = false;
-			else if (event.type == Common::EVENT_MOUSEMOVE)
-				_vm->_system->updateScreen();
-			else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
-				return;
-		}
-
-		_vm->_system->delayMillis(10); // Take it easy on the CPU
-	}
-
-	// Set back the cursor
-	_vm->_cursor->setCursor(kRivenMainCursor);
-	_vm->_system->updateScreen();
-
-	RivenHotspot *bait = _vm->getCard()->getHotspotByBlstId(9);
-	RivenHotspot *baitPlate = _vm->getCard()->getHotspotByBlstId(16);
-
-	// Set the bait if we put it on the plate, remove otherwise
-	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
-		_vm->_vars["bbait"] = 1;
-		_vm->getCard()->drawPicture(4);
-		bait->enable(false); // Disable bait hotspot
-		baitPlate->enable(true); // Enable baitplate hotspot
-	} else {
-		_vm->_vars["bbait"] = 0;
-		bait->enable(true); // Enable bait hotspot
-		baitPlate->enable(false); // Disable baitplate hotspot
-	}
-}
-
-void RivenExternal::xbisland190_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders();
-}
-
-void RivenExternal::xbisland190_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(41, 9);
-}
-
-void RivenExternal::xbisland190_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(41, 9);
-}
-
-void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(9);
-}
-
-void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) {
-	runDomeButtonMovie();
-}
-
-void RivenExternal::xbisland_domecheck(uint16 argc, uint16 *argv) {
-	runDomeCheck();
-}
-
-void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
-	Common::Point startPos = _vm->_system->getEventManager()->getMousePos();
-
-	// Get the variable for the valve
-	uint32 &valve = _vm->_vars["bvalve"];
-
-	int changeX = 0;
-	int changeY = 0;
-	bool done = false;
-
-	// Set the cursor to the closed position
-	_vm->_cursor->setCursor(kRivenClosedHandCursor);
-	_vm->_system->updateScreen();
-
-	while (!done) {
-		Common::Event event;
-
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_MOUSEMOVE:
-				changeX = event.mouse.x - startPos.x;
-				changeY = startPos.y - event.mouse.y;
-				_vm->_system->updateScreen();
-				break;
-			case Common::EVENT_LBUTTONUP:
-				// FIXME: These values for changes in x/y could be tweaked.
-				if (valve == 0 && changeY <= -10) {
-					valve = 1;
-					_vm->_cursor->setCursor(kRivenHideCursor);
-					_vm->_system->updateScreen();
-					_vm->_video->playMovieBlockingRiven(2);
-					_vm->refreshCard();
-				} else if (valve == 1) {
-					if (changeX >= 0 && changeY >= 10) {
-						valve = 0;
-						_vm->_cursor->setCursor(kRivenHideCursor);
-						_vm->_system->updateScreen();
-						_vm->_video->playMovieBlockingRiven(3);
-						_vm->refreshCard();
-					} else if (changeX <= -10 && changeY <= 10) {
-						valve = 2;
-						_vm->_cursor->setCursor(kRivenHideCursor);
-						_vm->_system->updateScreen();
-						_vm->_video->playMovieBlockingRiven(1);
-						_vm->refreshCard();
-					}
-				} else if (valve == 2 && changeX >= 10) {
-					valve = 1;
-					_vm->_cursor->setCursor(kRivenHideCursor);
-					_vm->_system->updateScreen();
-					_vm->_video->playMovieBlockingRiven(4);
-					_vm->refreshCard();
-				}
-				done = true;
-			default:
-				break;
-			}
-		}
-		_vm->_system->delayMillis(10);
-	}
-
-	// If we changed state and the new state is that the valve is flowing to
-	// the boiler, we need to update the boiler state.
-	if (valve == 1) {
-		if (_vm->_vars["bidvlv"] == 1) { // Check which way the water is going at the boiler
-			if (_vm->_vars["bblrarm"] == 1) {
-				// If the pipe is open, make sure the water is drained out
-				_vm->_vars["bheat"] = 0;
-				_vm->_vars["bblrwtr"] = 0;
-			} else {
-				// If the pipe is closed, fill the boiler again
-				_vm->_vars["bheat"] = _vm->_vars["bblrvalve"];
-				_vm->_vars["bblrwtr"] = 1;
-			}
-		} else {
-			// Have the grating inside the boiler match the switch outside
-			_vm->_vars["bblrgrt"] = (_vm->_vars["bblrsw"] == 1) ? 0 : 1;
-		}
-	}
-}
-
-void RivenExternal::xbchipper(uint16 argc, uint16 *argv) {
-	// Why is this an external command....?
-	if (_vm->_vars["bvalve"] == 2)
-		_vm->_video->playMovieBlockingRiven(2);
-}
-
-// ------------------------------------------------------------------------------------
-// gspit (Garden Island) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::lowerPins() {
-	// Lower the pins
-
-	uint32 &pinUp = _vm->_vars["gpinup"];
-
-	if (pinUp == 0)
-		return;
-
-	uint32 &pinPos = _vm->_vars["gpinpos"];
-	uint32 startTime = (pinPos - 1) * 600 + 4830;
-	pinUp = 0;
-
-	// Play the down sound
-	_vm->_sound->playSound(13);
-
-	uint32 &upMovie = _vm->_vars["gupmoov"];
-
-	// Play the video of the pins going down
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(upMovie);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
-	_vm->_video->waitUntilMovieEnds(handle);
-
-	upMovie = 0;
-}
-
-void RivenExternal::xgresetpins(uint16 argc, uint16 *argv) {
-	// As the function name suggests, this resets the pins
-	lowerPins();
-	_vm->_vars["gupmoov"] = 0;
-}
-
-void RivenExternal::xgrotatepins(uint16 argc, uint16 *argv) {
-	// Rotate the pins, if necessary
-
-	if (_vm->_vars["gpinup"] == 0)
-		return;
-
-	uint32 &pinPos = _vm->_vars["gpinpos"];
-	uint32 startTime = (pinPos - 1) * 1200;
-
-	if (pinPos == 4)
-		pinPos = 1;
-	else
-		pinPos++;
-
-	// Play the rotating sound
-	_vm->_sound->playSound(12);
-
-	// Play the video of the pins rotating
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(_vm->_vars["gupmoov"]);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 1215, 600));
-	_vm->_video->waitUntilMovieEnds(handle);
-}
-
-void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
-	// Handle a click on a section of an island
-
-	RivenHotspot *panel = _vm->getCard()->getHotspotByBlstId(13);
-
-	// Get our mouse position and adjust it to the beginning of the hotspot
-	Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
-	mousePos.x -= panel->getRect().left;
-	mousePos.y -= panel->getRect().top;
-
-	// And now adjust it to which box we hit
-	mousePos.x /= 10;
-	mousePos.y /= 11;
-
-	// Lastly, adjust it based on the rotational position
-	uint32 &pinPos = _vm->_vars["gpinpos"];
-	switch (pinPos) {
-	case 1:
-		mousePos.x = 5 - mousePos.x;
-		mousePos.y = (4 - mousePos.y) * 5;
-		break;
-	case 2:
-		mousePos.x = (4 - mousePos.x) * 5;
-		mousePos.y = 1 + mousePos.y;
-		break;
-	case 3:
-		mousePos.x = 1 + mousePos.x;
-		mousePos.y = mousePos.y * 5;
-		break;
-	case 4:
-		mousePos.x = mousePos.x * 5;
-		mousePos.y = 5 - mousePos.y;
-		break;
-	default:
-		// (Should never happen)
-		error("Bad pin pos");
-	}
-
-	// Now check to see if this section of the island exists
-	uint32 islandIndex = _vm->_vars["glkbtns"] - 1;
-	uint16 imagePos = mousePos.x + mousePos.y;
-
-	static const uint16 islandImages[5][11] = {
-		{ 1, 2, 6, 7 },
-		{ 11, 16, 21, 22 },
-		{ 12, 13, 14, 15, 17, 18, 19, 20, 23, 24, 25 },
-		{ 5 },
-		{ 3, 4, 8, 9, 10 }
-	};
-
-	// The scripts set gimagemax to hold the max pin array length in islandPins above
-	uint32 imageCount = _vm->_vars["gimagemax"];
-	uint32 image = 0;
-	for (; image < imageCount; image++)
-		if (islandImages[islandIndex][image] == imagePos)
-			break;
-
-	// If we past it, we don't have a valid map coordinate
-	if (image == imageCount)
-		return;
-
-	uint32 &pinUp = _vm->_vars["gpinup"];
-	uint32 &curImage = _vm->_vars["gimagecurr"];
-
-	// Lower the pins if they are currently raised
-	if (pinUp == 1) {
-		lowerPins();
-
-		// If we just lowered the selected section, don't raise it up again
-		if (curImage == image)
-			return;
-	}
-
-	// Raise the pins by translating the position to the movie code
-	static const uint16 pinMovieCodes[] = { 1, 2, 1, 2, 1, 3, 4, 3, 4, 5, 1, 1, 2, 3, 4, 2, 5, 6, 7, 8, 3, 4, 9, 10, 11 };
-
-	// Play the up sound
-	_vm->_sound->playSound(14);
-
-	// Actually play the movie
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(pinMovieCodes[imagePos - 1]);
-	assert(handle);
-	uint32 startTime = 9630 - pinPos * 600;
-	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
-	_vm->_video->waitUntilMovieEnds(handle);
-
-	// Update the relevant variables
-	_vm->_vars["gupmoov"] = pinMovieCodes[imagePos - 1];
-	pinUp = 1;
-	curImage = image;
-}
-
-void RivenExternal::xgisland25_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders();
-}
-
-void RivenExternal::xgisland25_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(16, 11);
-}
-
-void RivenExternal::xgisland25_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(16, 11);
-}
-
-void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(11);
-}
-
-void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) {
-	runDomeButtonMovie();
-}
-
-void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) {
-	runDomeCheck();
-}
-
-void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) {
-	// Play the deactivation of a pool if one is active and a different one is activated
-	_vm->_cursor->setCursor(kRivenHideCursor);
-	_vm->_system->updateScreen();
-	_vm->_video->playMovieBlockingRiven(_vm->_vars["glkbtns"] * 2);
-}
-
-void RivenExternal::xgwt200_scribetime(uint16 argc, uint16 *argv) {
-	// Get the current time
-	_vm->_vars["gscribetime"] = _vm->_system->getMillis();
-}
-
-void RivenExternal::xgwt900_scribe(uint16 argc, uint16 *argv) {
-	uint32 &scribeVar = _vm->_vars["gscribe"];
-
-	if (scribeVar == 1 && _vm->_system->getMillis() > _vm->_vars["gscribetime"] + 40000)
-		scribeVar = 2;
-}
-
-static const uint16 s_viewerTimeIntervals[] = { 0, 816, 1617, 2416, 3216, 4016, 4816, 5616, 6416, 7216, 8016, 8816 };
-
-void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) {
-	// This controls the viewer on the right side of the 'throne' on Garden Island
-	// (It shows the colors of the marbles)
-
-	// If the light is on, turn it off
-	uint32 &viewerLight = _vm->_vars["grview"];
-	if (viewerLight == 1) {
-		viewerLight = 0;
-		_vm->_sound->playSound(27);
-		_vm->refreshCard();
-
-		// Delay a bit before turning
-		_vm->_system->delayMillis(200);
-	}
-
-	// Calculate how much we're moving
-	Common::String buttonName = _vm->getCard()->getCurHotspot()->getName();
-	uint32 buttonPos = buttonName.lastChar() - '0';
-
-	uint32 &curPos = _vm->_vars["grviewpos"];
-	uint32 newPos = curPos + buttonPos;
-
-	// Now play the movie
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
-	_vm->_video->waitUntilMovieEnds(handle);
-
-	// Set the new position and let the card's scripts take over again
-	curPos = newPos % 6; // Clip it to 0-5
-	_vm->refreshCard();
-}
-
-void RivenExternal::xgplaywhark(uint16 argc, uint16 *argv) {
-	// The whark response to using the lights
-
-	// If we've gotten a visit already since we turned out the light, bail out
-	uint32 &wharkState = _vm->_vars["gwharktime"];
-
-	if (wharkState != 1)
-		return;
-
-	wharkState = 0;
-
-	// Increase the amount of times the whark has visited
-	uint32 &wharkVisits = _vm->_vars["gwhark"];
-	wharkVisits++;
-
-	// If we're at 5 or more, the whark will no longer visit us :(
-	if (wharkVisits >= 5) {
-		wharkVisits = 5;
-		return;
-	}
-
-	// Activate the correct video based on the amount of times we've been visited
-	switch (wharkVisits) {
-	case 1:
-		_vm->_video->activateMLST(_vm->getCard()->getMovie(3));
-		break;
-	case 2:
-		// One of two random videos
-		_vm->_video->activateMLST(_vm->getCard()->getMovie(4 + _vm->_rnd->getRandomBit()));
-		break;
-	case 3:
-		// One of two random videos
-		_vm->_video->activateMLST(_vm->getCard()->getMovie(6 + _vm->_rnd->getRandomBit()));
-		break;
-	case 4:
-		// Red alert! Shields online! Brace yourself for impact!
-		_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
-		break;
-	}
-
-	// For whatever reason the devs felt fit, code 31 is used for all of the videos
-	_vm->_video->playMovieBlockingRiven(31);
-	_vm->refreshCard();
-}
-
-void RivenExternal::xgwharksnd(uint16 argc, uint16 *argv) {
-	// TODO: Random background whark videos
-}
-
-void RivenExternal::xglviewer(uint16 argc, uint16 *argv) {
-	// This controls the viewer on the left side of the 'throne' on Garden Island
-	// (It shows the village from the middle of the lake)
-
-	// Calculate how much we're moving
-	Common::String buttonName = _vm->getCard()->getCurHotspot()->getName();
-	uint32 buttonPos = buttonName.lastChar() - '0';
-
-	uint32 &curPos = _vm->_vars["glviewpos"];
-	uint32 newPos = curPos + buttonPos;
-
-	// Now play the movie
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
-	_vm->_video->waitUntilMovieEnds(handle);
-
-	// Set the new position to the variable
-	curPos = newPos % 6; // Clip it to 0-5
-
-	// And update the screen with the new image
-	_vm->getCard()->drawPicture(curPos + 2);
-}
-
-void RivenExternal::xglview_villageon(uint16 argc, uint16 *argv) {
-	// Turn on the left viewer to 'village mode'
-	_vm->_vars["glview"] = 2;
-	_vm->getCard()->drawPicture(_vm->_vars["glviewpos"] + 2);
-}
-
-void RivenExternal::xglview_villageoff(uint16 argc, uint16 *argv) {
-	// Turn off the left viewer when in 'village mode' (why is this external?)
-	_vm->_vars["glview"] = 0;
-	_vm->getCard()->drawPicture(1);
-}
-
-static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
-	uint32 &cathState = vm->_vars["gcathstate"];
-	uint16 movie;
-
-	// Choose a new movie
-	if (cathState == 1) {
-		static const int movieList[] = { 9, 10, 19, 19, 21, 21 };
-		movie = movieList[vm->_rnd->getRandomNumber(5)];
-	} else if (cathState == 2) {
-		static const int movieList[] = { 18, 20, 22 };
-		movie = movieList[vm->_rnd->getRandomNumber(2)];
-	} else {
-		static const int movieList[] = { 11, 11, 12, 17, 17, 17, 17, 23 };
-		movie = movieList[vm->_rnd->getRandomNumber(7)];
-	}
-
-	// Update Catherine's state
-	if (movie == 10 || movie == 17 || movie == 18 || movie == 20)
-		cathState = 1;
-	else if (movie == 19 || movie == 21 || movie == 23)
-		cathState = 2;
-	else
-		cathState = 3;
-
-	// Begin playing the new movie
-	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
-	VideoEntryPtr video = vm->_video->playMovieRiven(30);
-
-	// Reset the timer
-	vm->installTimer(&catherineViewerIdleTimer, video->getDuration().msecs() + vm->_rnd->getRandomNumber(60) * 1000);
-}
-
-void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) {
-	// Activate random background Catherine videos
-
-	// Turn on the left viewer to 'prison mode'
-	_vm->_vars["glview"] = 1;
-
-	// Get basic starting states
-	uint16 cathMovie = _vm->_rnd->getRandomNumberRng(8, 23);
-	uint16 turnOnMovie = 4;
-	uint32 &cathState = _vm->_vars["gcathstate"];
-
-	// Adjust the turn on movie
-	if (cathMovie == 14)
-		turnOnMovie = 6;
-	else if (cathMovie == 15)
-		turnOnMovie = 7;
-
-	// Adjust Catherine's state
-	if (cathMovie == 9 || cathMovie == 11 || cathMovie == 12 || cathMovie == 22)
-		cathState = 3;
-	else if (cathMovie == 19 || cathMovie == 21 || cathMovie == 23 || cathMovie == 14)
-		cathState = 2;
-	else
-		cathState = 1;
-
-	// Turn on the viewer
-	_vm->_cursor->hideCursor();
-	_vm->_video->playMovieBlockingRiven(turnOnMovie);
-	_vm->_cursor->showCursor();
-
-	uint32 timeUntilNextMovie;
-
-	// Begin playing a movie immediately if Catherine is already in the viewer
-	if (cathMovie == 8 || (cathMovie >= 13 && cathMovie <= 16)) {
-		_vm->_video->activateMLST(_vm->getCard()->getMovie(cathMovie));
-		VideoEntryPtr video = _vm->_video->playMovieRiven(30);
-
-		timeUntilNextMovie = video->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
-	} else {
-		// Otherwise, just redraw the imager
-		timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000;
-		_vm->getCard()->drawPicture(8);
-	}
-
-	// Create the timer for the next video
-	_vm->installTimer(&catherineViewerIdleTimer, timeUntilNextMovie);
-}
-
-void RivenExternal::xglview_prisonoff(uint16 argc, uint16 *argv) {
-	// Deactivate random background Catherine videos
-
-	// Update the viewer state (now off)
-	_vm->_vars["glview"] = 0;
-
-	// Remove the timer we set in xglview_prisonon()
-	_vm->removeTimer();
-
-	// Play the 'turn off' movie after stopping any videos still playing
-	_vm->_video->stopVideos();
-	_vm->_cursor->hideCursor();
-	_vm->_video->playMovieBlockingRiven(5);
-	_vm->_cursor->showCursor();
-
-	// Redraw the viewer
-	_vm->getCard()->drawPicture(1);
-}
-
-// ------------------------------------------------------------------------------------
-// jspit (Jungle Island) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xreseticons(uint16 argc, uint16 *argv) {
-	// Reset the icons when going to Tay (rspit)
-	_vm->_vars["jicons"] = 0;
-	_vm->_vars["jiconorder"] = 0;
-	_vm->_vars["jrbook"] = 0;
-}
-
-// Count up how many icons are pressed
-static byte countDepressedIcons(uint32 iconOrderVar) {
-	if (iconOrderVar >= (1 << 20))
-		return 5;
-	else if (iconOrderVar >= (1 << 15))
-		return 4;
-	else if (iconOrderVar >= (1 << 10))
-		return 3;
-	else if (iconOrderVar >= (1 << 5))
-		return 2;
-	else if (iconOrderVar >= (1 << 1))
-		return 1;
-	else
-		return 0;
-}
-
-void RivenExternal::xicon(uint16 argc, uint16 *argv) {
-	// Set atemp as the status of whether or not the icon can be depressed.
-	if (_vm->_vars["jicons"] & (1 << (argv[0] - 1))) {
-		// This icon is depressed. Allow depression only if the last depressed icon was this one.
-		if ((_vm->_vars["jiconorder"] & 0x1f) == argv[0])
-			_vm->_vars["atemp"] = 1;
-		else
-			_vm->_vars["atemp"] = 2;
-	} else
-		_vm->_vars["atemp"] = 0;
-}
-
-void RivenExternal::xcheckicons(uint16 argc, uint16 *argv) {
-	// Reset the icons if this is the sixth icon
-	uint32 &iconOrderVar = _vm->_vars["jiconorder"];
-	if (countDepressedIcons(iconOrderVar) == 5) {
-		iconOrderVar = 0;
-		_vm->_vars["jicons"] = 0;
-		_vm->_sound->playSound(46);
-	}
-}
-
-void RivenExternal::xtoggleicon(uint16 argc, uint16 *argv) {
-	// Get the variables
-	uint32 &iconsDepressed = _vm->_vars["jicons"];
-	uint32 &iconOrderVar = _vm->_vars["jiconorder"];
-
-	if (iconsDepressed & (1 << (argv[0] - 1))) {
-		// The icon is depressed, now unpress it
-		iconsDepressed &= ~(1 << (argv[0] - 1));
-		iconOrderVar >>= 5;
-	} else {
-		// The icon is not depressed, now depress it
-		iconsDepressed |= 1 << (argv[0] - 1);
-		iconOrderVar = (iconOrderVar << 5) + argv[0];
-	}
-
-	// Check if the puzzle is complete now and assign 1 to jrbook if the puzzle is complete.
-	if (iconOrderVar == _vm->_vars["jiconcorrectorder"])
-		_vm->_vars["jrbook"] = 1;
-}
-
-void RivenExternal::xjtunnel103_pictfix(uint16 argc, uint16 *argv) {
-	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
-	uint32 iconsDepressed = _vm->_vars["jicons"];
-
-	// Now, draw which icons are depressed based on the bits of the variable
-	if (iconsDepressed & (1 << 0))
-		_vm->getCard()->drawPicture(2);
-	if (iconsDepressed & (1 << 1))
-		_vm->getCard()->drawPicture(3);
-	if (iconsDepressed & (1 << 2))
-		_vm->getCard()->drawPicture(4);
-	if (iconsDepressed & (1 << 3))
-		_vm->getCard()->drawPicture(5);
-	if (iconsDepressed & (1 << 22))
-		_vm->getCard()->drawPicture(6);
-	if (iconsDepressed & (1 << 23))
-		_vm->getCard()->drawPicture(7);
-	if (iconsDepressed & (1 << 24))
-		_vm->getCard()->drawPicture(8);
-}
-
-void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
-	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
-	uint32 iconsDepressed = _vm->_vars["jicons"];
-
-	// Now, draw which icons are depressed based on the bits of the variable
-	if (iconsDepressed & (1 << 9))
-		_vm->getCard()->drawPicture(2);
-	if (iconsDepressed & (1 << 10))
-		_vm->getCard()->drawPicture(3);
-	if (iconsDepressed & (1 << 11))
-		_vm->getCard()->drawPicture(4);
-	if (iconsDepressed & (1 << 12))
-		_vm->getCard()->drawPicture(5);
-	if (iconsDepressed & (1 << 13))
-		_vm->getCard()->drawPicture(6);
-	if (iconsDepressed & (1 << 14))
-		_vm->getCard()->drawPicture(7);
-	if (iconsDepressed & (1 << 15))
-		_vm->getCard()->drawPicture(8);
-	if (iconsDepressed & (1 << 16))
-		_vm->getCard()->drawPicture(9);
-}
-
-void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
-	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
-	uint32 iconsDepressed = _vm->_vars["jicons"];
-
-	// Now, draw which icons are depressed based on the bits of the variable
-	if (iconsDepressed & (1 << 3))
-		_vm->getCard()->drawPicture(2);
-	if (iconsDepressed & (1 << 4))
-		_vm->getCard()->drawPicture(3);
-	if (iconsDepressed & (1 << 5))
-		_vm->getCard()->drawPicture(4);
-	if (iconsDepressed & (1 << 6))
-		_vm->getCard()->drawPicture(5);
-	if (iconsDepressed & (1 << 7))
-		_vm->getCard()->drawPicture(6);
-	if (iconsDepressed & (1 << 8))
-		_vm->getCard()->drawPicture(7);
-	if (iconsDepressed & (1 << 9))
-		_vm->getCard()->drawPicture(8);
-}
-
-void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
-	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
-	uint32 iconsDepressed = _vm->_vars["jicons"];
-
-	// Now, draw which icons are depressed based on the bits of the variable
-	if (iconsDepressed & (1 << 16))
-		_vm->getCard()->drawPicture(2);
-	if (iconsDepressed & (1 << 17))
-		_vm->getCard()->drawPicture(3);
-	if (iconsDepressed & (1 << 18))
-		_vm->getCard()->drawPicture(4);
-	if (iconsDepressed & (1 << 19))
-		_vm->getCard()->drawPicture(5);
-	if (iconsDepressed & (1 << 20))
-		_vm->getCard()->drawPicture(6);
-	if (iconsDepressed & (1 << 21))
-		_vm->getCard()->drawPicture(7);
-	if (iconsDepressed & (1 << 22))
-		_vm->getCard()->drawPicture(8);
-	if (iconsDepressed & (1 << 23))
-		_vm->getCard()->drawPicture(9);
-}
-
-void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
-	// Run the gallows's carriage
-
-	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
-	_vm->_system->updateScreen();                      // Update
-	_vm->_video->playMovieBlockingRiven(1);            // Play handle movie
-	_vm->_gfx->scheduleTransition(15);                 // Set pan down transition
-	_vm->changeToCard(_vm->getStack()->getCardStackId(0x18e77));  // Change to card facing up
-	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor (again)
-	_vm->_system->updateScreen();                      // Update
-	_vm->_video->playMovieBlockingRiven(4);            // Play carriage beginning to drop
-	_vm->_gfx->scheduleTransition(14);                 // Set pan up transition
-	_vm->changeToCard(_vm->getStack()->getCardStackId(0x183a9));  // Change to card looking straight again
-	_vm->_video->playMovieBlockingRiven(2);
-
-	if (_vm->_vars["jgallows"] == 1) {
-		// If the gallows is open, play the up movie and return
-		_vm->_video->playMovieBlockingRiven(3);
-		return;
-	}
-
-	// Give the player 5 seconds to click (anywhere)
-	uint32 startTime = _vm->_system->getMillis();
-	bool gotClick = false;
-	while (_vm->_system->getMillis() - startTime <= 5000 && !gotClick) {
-		Common::Event event;
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_MOUSEMOVE:
-				_vm->_system->updateScreen();
-				break;
-			case Common::EVENT_LBUTTONUP:
-				gotClick = true;
-				break;
-			default:
-				break;
-			}
-		}
-
-		_vm->_system->delayMillis(10);
-	}
-
-	_vm->_cursor->setCursor(kRivenHideCursor);             // Hide the cursor
-	_vm->_system->updateScreen();                          // Update
-
-	if (gotClick) {
-		_vm->_gfx->scheduleTransition(16);                 // Schedule dissolve transition
-		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18d4d));  // Move forward
-		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
-		_vm->_system->updateScreen();                      // Update
-		_vm->_system->delayMillis(500);                    // Delay a half second before changing again
-		_vm->_gfx->scheduleTransition(12);                 // Schedule pan left transition
-		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18ab5));  // Turn right
-		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
-		_vm->_system->updateScreen();                      // Update
-		_vm->_video->playMovieBlockingRiven(1);            // Play carriage ride movie
-		_vm->changeToCard(_vm->getStack()->getCardStackId(0x17167));  // We have arrived at the top
-	} else
-		_vm->_video->playMovieBlockingRiven(3);            // Too slow!
-}
-
-void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(81, 10);
-}
-
-void RivenExternal::xjdome25_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(81, 10);
-}
-
-void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(10);
-}
-
-void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) {
-	runDomeButtonMovie();
-}
-
-void RivenExternal::xjisland3500_domecheck(uint16 argc, uint16 *argv) {
-	runDomeCheck();
-}
-
-int RivenExternal::jspitElevatorLoop() {
-	Common::Point startPos = _vm->_system->getEventManager()->getMousePos();
-
-	Common::Event event;
-	int changeLevel = 0;
-
-	_vm->_cursor->setCursor(kRivenClosedHandCursor);
-	_vm->_system->updateScreen();
-
-	for (;;) {
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_MOUSEMOVE:
-				if (event.mouse.y > (startPos.y + 10)) {
-					changeLevel = -1;
-				} else if (event.mouse.y < (startPos.y - 10)) {
-					changeLevel = 1;
-				} else {
-					changeLevel = 0;
-				}
-				_vm->_system->updateScreen();
-				break;
-			case Common::EVENT_LBUTTONUP:
-				_vm->_cursor->setCursor(kRivenMainCursor);
-				_vm->_system->updateScreen();
-				return changeLevel;
-			default:
-				break;
-			}
-		}
-		_vm->_system->delayMillis(10);
-	}
-}
-
-void RivenExternal::xhandlecontrolup(uint16 argc, uint16 *argv) {
-	int changeLevel = jspitElevatorLoop();
-
-	// If we've moved the handle down, go down a floor
-	if (changeLevel == -1) {
-		_vm->_video->playMovieBlockingRiven(1);
-		_vm->_video->playMovieBlockingRiven(2);
-		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
-	}
-}
-
-void RivenExternal::xhandlecontroldown(uint16 argc, uint16 *argv) {
-	int changeLevel = jspitElevatorLoop();
-
-	// If we've moved the handle up, go up a floor
-	if (changeLevel == 1) {
-		_vm->_video->playMovieBlockingRiven(1);
-		_vm->_video->playMovieBlockingRiven(2);
-		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
-	}
-}
-
-void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {
-	int changeLevel = jspitElevatorLoop();
-
-	if (changeLevel == 0)
-		return;
-
-	// Play the handle moving video
-	if (changeLevel == 1)
-		_vm->_video->playMovieBlockingRiven(7);
-	else
-		_vm->_video->playMovieBlockingRiven(6);
-
-	// If the whark's mouth is open, close it
-	uint32 &mouthVar = _vm->_vars["jwmouth"];
-	if (mouthVar == 1) {
-		_vm->_video->playMovieBlockingRiven(3);
-		_vm->_video->playMovieBlockingRiven(8);
-		mouthVar = 0;
-	}
-
-	// Play the elevator video and then change the card
-	if (changeLevel == 1) {
-		_vm->_video->playMovieBlockingRiven(5);
-		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e597));
-	} else {
-		_vm->_video->playMovieBlockingRiven(4);
-		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e29c));
-	}
-}
-
-void RivenExternal::xjplaybeetle_550(uint16 argc, uint16 *argv) {
-	// Play a beetle animation 25% of the time
-	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
-}
-
-void RivenExternal::xjplaybeetle_600(uint16 argc, uint16 *argv) {
-	// Play a beetle animation 25% of the time
-	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
-}
-
-void RivenExternal::xjplaybeetle_950(uint16 argc, uint16 *argv) {
-	// Play a beetle animation 25% of the time
-	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
-}
-
-void RivenExternal::xjplaybeetle_1050(uint16 argc, uint16 *argv) {
-	// Play a beetle animation 25% of the time
-	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
-}
-
-void RivenExternal::xjplaybeetle_1450(uint16 argc, uint16 *argv) {
-	// Play a beetle animation 25% of the time as long as the girl is not present
-	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0 && _vm->_vars["jgirl"] != 1) ? 1 : 0;
-}
-
-void RivenExternal::xjlagoon700_alert(uint16 argc, uint16 *argv) {
-	// Handle sunner reactions (mid-staircase)
-
-	if (_vm->_vars["jsunners"] == 0)
-		_vm->_video->playMovieRiven(1);
-}
-
-void RivenExternal::xjlagoon800_alert(uint16 argc, uint16 *argv) {
-	// Handle sunner reactions (lower-staircase)
-
-	uint32 &sunners = _vm->_vars["jsunners"];
-
-	if (sunners == 0) {
-		// Show the sunners alert video
-		_vm->_video->playMovieRiven(1);
-	} else if (sunners == 1) {
-		// Show the sunners leaving if you moved forward in their "alert" status
-		_vm->_video->playMovieBlockingRiven(2);
-		_vm->_video->playMovieBlockingRiven(6);
-		sunners = 2;
-		_vm->refreshCard();
-	}
-}
-
-void RivenExternal::xjlagoon1500_alert(uint16 argc, uint16 *argv) {
-	// Handle sunner reactions (beach)
-
-	uint32 &sunners = _vm->_vars["jsunners"];
-
-	if (sunners == 0) {
-		// Show the sunners alert video
-		_vm->_video->playMovieBlockingRiven(3);
-	} else if (sunners == 1) {
-		// Show the sunners leaving if you moved forward in their "alert" status
-		_vm->_video->playMovieBlockingRiven(2);
-		sunners = 2;
-		_vm->refreshCard();
-	}
-}
-
-void RivenExternal::xjschool280_resetleft(uint16 argc, uint16 *argv) {
-	// Dummy function. This resets the unneeded video timing variable (dropLeftStart) in
-	// the DVD version.
-}
-
-void RivenExternal::xjschool280_resetright(uint16 argc, uint16 *argv) {
-	// Dummy function. This resets the unneeded video timing variable (dropRightStart) in
-	// the DVD version.
-}
-
-void RivenExternal::redrawWharkNumberPuzzle(uint16 overlay, uint16 number) {
-	// Update the screen for the whark number puzzle
-	// We don't update the whole screen here because we don't want to overwrite the video data
-	_vm->getCard()->drawPicture(overlay);
-	_vm->getCard()->drawPicture(number + 1);
-	_vm->_gfx->updateScreen(Common::Rect(80, 212, 477, 392));
-	_vm->_system->updateScreen();
-}
-
-void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) {
-	// The "monstrous" whark puzzle that teaches the number system
-
-	uint32 *posVar;
-	uint16 spinMLST, overlayPLST, doomMLST, snackMLST;
-
-	// Choose left or right based on jwharkpos (which is set by the scripts)
-	if (_vm->_vars["jwharkpos"] == 1) {
-		posVar = &_vm->_vars["jleftpos"];
-		spinMLST = 1;
-		overlayPLST = 12;
-		doomMLST = 3;
-		snackMLST = 4;
-	} else {
-		posVar = &_vm->_vars["jrightpos"];
-		spinMLST = 2;
-		overlayPLST = 13;
-		doomMLST = 5;
-		snackMLST = 6;
-	}
-
-	// Hide the cursor
-	_vm->_cursor->setCursor(kRivenHideCursor);
-	_vm->_system->updateScreen();
-
-	// Play the spin movie
-	_vm->_video->playMovieBlockingRiven(spinMLST);
-
-	// Get our random number and redraw the area
-	uint16 number = _vm->_rnd->getRandomNumberRng(1, 10);
-	redrawWharkNumberPuzzle(overlayPLST, number);
-
-	// Handle movement
-	// (11560/600)s is the length of each of the two movies. We divide it into 19 parts
-	// (one for each of the possible positions the villager can have).
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(doomMLST);
-	Audio::Timestamp startTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
-	*posVar += number; // Adjust to the end
-	Audio::Timestamp endTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
-	handle->setBounds(startTime, endTime);
-	_vm->_video->waitUntilMovieEnds(handle);
-
-	if (*posVar > 19) {
-		// The villager has died :(
-		_vm->_video->playMovieBlockingRiven(snackMLST);
-		redrawWharkNumberPuzzle(overlayPLST, number);
-		*posVar = 0;
-	}
-
-	// Enable the correct hotspots for the movement now
-	RivenHotspot *rotateLeft = _vm->getCard()->getHotspotByName("rotateLeft");
-	RivenHotspot *rotateRight = _vm->getCard()->getHotspotByName("rotateRight");
-	rotateLeft->enable(!rotateLeft->isEnabled());
-	rotateRight->enable(!rotateRight->isEnabled());
-
-	// Update the cursor
-	_vm->updateCurrentHotspot();
-}
-
-void RivenExternal::xjatboundary(uint16 argc, uint16 *argv) {
-	runDemoBoundaryDialog();
-}
-
-// ------------------------------------------------------------------------------------
-// ospit (Gehn's Office) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xorollcredittime(uint16 argc, uint16 *argv) {
-	// WORKAROUND: The special change stuff only handles one destination and it would
-	// be messy to modify the way that currently works. If we use the trap book on Tay,
-	// we should be using the Tay end game sequences.
-	if (_vm->_vars["returnstackid"] == kStackRspit) {
-		_vm->changeToStack(kStackRspit);
-		_vm->changeToCard(2);
-		return;
-	}
-
-	// You used the trap book... why? What were you thinking?
-	uint32 gehnState = _vm->_vars["agehn"];
-
-	if (gehnState == 0)         // Gehn who?
-		runEndGame(1, 9500);
-	else if (gehnState == 4)    // You freed him? Are you kidding me?
-		runEndGame(2, 12000);
-	else                        // You already spoke with Gehn. What were you thinking?
-		runEndGame(3, 8000);
-}
-
-void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
-	// Hide the cursor
-	_vm->_cursor->setCursor(kRivenHideCursor);
-	_vm->_system->updateScreen();
-
-	// Let's hook onto our video
-	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
-
-	// Convert from the standard QuickTime base time to milliseconds
-	// The values are in terms of 1/600 of a second.
-	// Have I said how much I just *love* QuickTime? </sarcasm>
-	uint32 startTime = argv[1] * 1000 / 600;
-	uint32 endTime = argv[2] * 1000 / 600;
-
-	// Track down our hotspot
-	Common::String hotspotName = Common::String::format("touchBook%d", argv[3]);
-	RivenHotspot *hotspot = _vm->getCard()->getHotspotByName(hotspotName);
-	Common::Rect hotspotRect = hotspot->getRect();
-
-	debug(0, "xbookclick:");
-	debug(0, "\tVideo Code = %d", argv[0]);
-	debug(0, "\tStart Time = %dms", startTime);
-	debug(0, "\tEnd Time   = %dms", endTime);
-	debug(0, "\tHotspot    = %d -> %s", argv[3], hotspotName.c_str());
-
-	// Just let the video play while we wait until Gehn opens the trap book for us
-	while (video->getTime() < startTime && !_vm->shouldQuit()) {
-		if (_vm->_video->updateMovies())
-			_vm->_system->updateScreen();
-
-		Common::Event event;
-		while (_vm->_system->getEventManager()->pollEvent(event))
-			;
-
-		_vm->_system->delayMillis(10);
-	}
-
-	// Break out if we're quitting
-	if (_vm->shouldQuit())
-		return;
-
-	// Update our hotspot stuff
-	if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
-		_vm->_cursor->setCursor(kRivenOpenHandCursor);
-	else
-		_vm->_cursor->setCursor(kRivenMainCursor);
-
-	_vm->_system->updateScreen();
-
-	// OK, Gehn has opened the trap book and has asked us to go in. Let's watch
-	// and see what the player will do...
-	while (video->getTime() < endTime && !_vm->shouldQuit()) {
-		bool updateScreen = _vm->_video->updateMovies();
-
-		Common::Event event;
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_MOUSEMOVE:
-				if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
-					_vm->_cursor->setCursor(kRivenOpenHandCursor);
-				else
-					_vm->_cursor->setCursor(kRivenMainCursor);
-				updateScreen = true;
-				break;
-			case Common::EVENT_LBUTTONUP:
-				if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos())) {
-					// OK, we've used the trap book! We go for ride lady!
-					_vm->_scriptMan->stopAllScripts();                  // Stop all running scripts (so we don't remain in the cage)
-					_vm->_video->stopVideos();                          // Stop all videos
-					_vm->_cursor->setCursor(kRivenHideCursor);          // Hide the cursor
-					_vm->getCard()->drawPicture(3);                  // Black out the screen
-					_vm->_sound->playSound(0);                          // Play the link sound
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(7));    // Activate Gehn Link Video
-					_vm->_video->playMovieBlockingRiven(1);             // Play Gehn Link Video
-					_vm->_vars["agehn"] = 4;                            // Set Gehn to the trapped state
-					_vm->_vars["atrapbook"] = 1;                        // We've got the trap book again
-					_vm->_sound->playSound(0);                          // Play the link sound again
-					_vm->changeToCard(_vm->getStack()->getCardStackId(0x2885));    // Link out!
-					return;
-				}
-				break;
-			default:
-				break;
-			}
-		}
-
-		if (updateScreen && !_vm->shouldQuit())
-			_vm->_system->updateScreen();
-
-		_vm->_system->delayMillis(10);
-	}
-
-	// Break out if we're quitting
-	if (_vm->shouldQuit())
-		return;
-
-	// Hide the cursor again
-	_vm->_cursor->setCursor(kRivenHideCursor);
-	_vm->_system->updateScreen();
-
-	// If there was no click and this is the third time Gehn asks us to
-	// use the trap book, he will shoot the player. Dead on arrival.
-	// Run the credits from here.
-	if (_vm->_vars["agehn"] == 3) {
-		_vm->_scriptMan->stopAllScripts();
-		runCredits(argv[0], 5000);
-		return;
-	}
-
-	// There was no click, so just play the rest of the video.
-	_vm->_video->waitUntilMovieEnds(video);
-}
-
-void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {
-	// Close the blank linking book if it's open
-	uint32 &book = _vm->_vars["odeskbook"];
-	if (book != 1)
-		return;
-
-	// Set the variable to be "closed"
-	book = 0;
-
-	// Play the movie
-	_vm->_video->playMovieBlockingRiven(1);
-
-	// Set the hotspots into their correct states
-	RivenHotspot *closeBook = _vm->getCard()->getHotspotByName("closeBook");
-	RivenHotspot *nullHotspot = _vm->getCard()->getHotspotByName("null");
-	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
-
-	closeBook->enable(false);
-	nullHotspot->enable(false);
-	openBook->enable(true);
-
-	// We now need to draw PLST 1 and refresh, but PLST 1 is
-	// drawn when refreshing anyway, so don't worry about that.
-	_vm->refreshCard();
-}
-
-void RivenExternal::xobedroom5_closedrawer(uint16 argc, uint16 *argv) {
-	// Close the drawer if open when clicking on the journal.
-	_vm->_video->playMovieBlockingRiven(2);
-	_vm->_vars["ostanddrawer"] = 0;
-}
-
-void RivenExternal::xogehnopenbook(uint16 argc, uint16 *argv) {
-	_vm->getCard()->drawPicture(_vm->_vars["ogehnpage"]);
-}
-
-void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
-	// Get the page variable
-	uint32 &page = _vm->_vars["ogehnpage"];
-
-	// Decrement the page if it's not the first page
-	if (page == 1)
-		return;
-	page--;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(12);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(1);
-	_vm->getCard()->drawPicture(page);
-}
-
-void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
-	// Get the page variable
-	uint32 &page = _vm->_vars["ogehnpage"];
-
-	// Increment the page if it's not the last page
-	if (page == 13)
-		return;
-	page++;
-
-	// Play the page turning sound
-	_vm->_sound->playSound(13);
-
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(0);
-	_vm->getCard()->drawPicture(page);
-}
-
-uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) {
-	static const uint32 powers[] = { 100000, 10000, 1000, 100, 10, 1 };
-	return (correctCombo % powers[digit]) / powers[digit + 1];
-}
-
-void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
-	// Hide the cursor
-	_vm->_cursor->setCursor(kRivenHideCursor);
-	_vm->_system->updateScreen();
-
-	uint32 &prisonCombo = _vm->_vars["pcorrectorder"];
-	uint32 soundTime = _vm->_system->getMillis() - 500; // Start the first sound instantly
-	byte curSound = 0;
-
-	while (!_vm->shouldQuit()) {
-		// Play the next sound every half second
-		if (_vm->_system->getMillis() - soundTime >= 500) {
-			if (curSound == 5) // Break out after the last sound is done
-				break;
-
-			_vm->_sound->playSound(getComboDigit(prisonCombo, curSound) + 13);
-			curSound++;
-			soundTime = _vm->_system->getMillis();
-		}
-
-		// Poll events just to check for quitting
-		Common::Event event;
-		while (_vm->_system->getEventManager()->pollEvent(event)) {}
-
-		// Cut down on CPU usage
-		_vm->_system->delayMillis(10);
-	}
-
-	// Now play the video for the watch
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(1));
-	_vm->_video->playMovieBlockingRiven(1);
-
-	// And, finally, refresh
-	_vm->refreshCard();
-}
-
-// ------------------------------------------------------------------------------------
-// pspit (Prison Island) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xpisland990_elevcombo(uint16 argc, uint16 *argv) {
-	// Play button sound based on argv[0]
-	_vm->_sound->playSound(argv[0] + 5);
-
-	// It is impossible to get here if Gehn is not trapped. However,
-	// the original also disallows brute forcing the ending if you have
-	// not yet trapped Gehn.
-	if (_vm->_vars["agehn"] != 4)
-		return;
-
-	uint32 &correctDigits = _vm->_vars["pelevcombo"];
-
-	// pelevcombo keeps count of how many buttons we have pressed in the correct order.
-	// When pelevcombo is 5, clicking the handle will show the video freeing Catherine.
-	if (correctDigits < 5 && argv[0] == getComboDigit(_vm->_vars["pcorrectorder"], correctDigits))
-		correctDigits++;
-	else
-		correctDigits = 0;
-}
-
-void RivenExternal::xpscpbtn(uint16 argc, uint16 *argv) {
-	runDomeButtonMovie();
-}
-
-void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) {
-	runDomeCheck();
-}
-
-void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders();
-}
-
-void RivenExternal::xpisland25_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(10, 14);
-}
-
-void RivenExternal::xpisland25_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(10, 14);
-}
-
-void RivenExternal::xpisland25_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(14);
-}
-
-// ------------------------------------------------------------------------------------
-// rspit (Rebel Age) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xrcredittime(uint16 argc, uint16 *argv) {
-	// Nice going, you used the trap book on Tay.
-
-	// The game chooses what ending based on agehn for us,
-	// so we just have to play the video and credits.
-	// For the record, when agehn == 4, Gehn will thank you for
-	// showing him the rebel age and then leave you to die.
-	// Otherwise, the rebels burn the book. Epic fail either way.
-	runEndGame(1, 1500);
-}
-
-void RivenExternal::xrshowinventory(uint16 argc, uint16 *argv) {
-	// Give the trap book and Catherine's journal to the player
-	_vm->_vars["atrapbook"] = 1;
-	_vm->_vars["acathbook"] = 1;
-	_vm->_gfx->showInventory();
-}
-
-void RivenExternal::xrhideinventory(uint16 argc, uint16 *argv) {
-	_vm->_gfx->hideInventory();
-}
-
-static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) {
-	// Randomize a video out in the middle of Tay
-	uint16 movie = vm->_rnd->getRandomNumberRng(2, 13);
-	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
-	VideoEntryPtr handle = vm->_video->playMovieRiven(movie);
-
-	// Ensure the next video starts after this one ends
-	uint32 timeUntilNextVideo = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(38, 58) * 1000;
-
-	// Save the time in case we leave the card and return
-	vm->_vars["rvillagetime"] = timeUntilNextVideo + vm->getTotalPlayTime();
-
-	// Reinstall this timer with the new time
-	vm->installTimer(&rebelPrisonWindowTimer, timeUntilNextVideo);
-}
-
-void RivenExternal::xrwindowsetup(uint16 argc, uint16 *argv) {
-	// Randomize what effect happens when you look out into the middle of Tay
-
-	uint32 villageTime = _vm->_vars["rvillagetime"];
-
-	// If we have time leftover from a previous run, set up the timer again
-	if (_vm->getTotalPlayTime() < villageTime) {
-		_vm->installTimer(&rebelPrisonWindowTimer, villageTime - _vm->getTotalPlayTime());
-		return;
-	}
-
-	uint32 timeUntilNextVideo;
-
-	// Randomize the time until the next video
-	if (_vm->_rnd->getRandomNumber(2) == 0 && _vm->_vars["rrichard"] == 0) {
-		// In this case, a rebel is placed on a bridge
-		// The video itself is handled by the scripts later on
-		_vm->_vars["rrebelview"] = 0;
-		timeUntilNextVideo = _vm->_rnd->getRandomNumberRng(38, 58) * 1000;
-	} else {
-		// Otherwise, just a random video from the timer
-		_vm->_vars["rrebelview"] = 1;
-		timeUntilNextVideo = _vm->_rnd->getRandomNumber(20) * 1000;
-	}
-
-	// We don't set rvillagetime here because the scripts later just reset it to 0
-	// Of course, because of this, you can't return to the window twice and expect
-	// the timer to reinstall itself...
-
-	// Install our timer and we're on our way
-	_vm->installTimer(&rebelPrisonWindowTimer, timeUntilNextVideo);
-}
-
-// ------------------------------------------------------------------------------------
-// tspit (Temple Island) external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
-	// First, show the button movie
-	_vm->_video->playMovieBlockingRiven(3);
-
-	// Don't do anything else if the telescope power is off
-	if (_vm->_vars["ttelevalve"] == 0)
-		return;
-
-	uint32 &telescopePos = _vm->_vars["ttelescope"];
-	uint32 &telescopeCover = _vm->_vars["ttelecover"];
-
-	if (telescopePos == 1) {
-		// We're at the bottom, which means one of two things can happen...
-		if (telescopeCover == 1 && _vm->_vars["ttelepin"] == 1) {
-			// ...if the cover is open and the pin is up, the game is now over.
-			if (_vm->_vars["pcage"] == 2) {
-				// The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
-				// And now we fall back to Earth... all the way...
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
-				runEndGame(8, 5000);
-			} else if (_vm->_vars["agehn"] == 4) {
-				// The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
-				// Nice going! Catherine and the islanders are all dead now! Just go back to your home...
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
-				runEndGame(9, 5000);
-			} else if (_vm->_vars["atrapbook"] == 1) {
-				// The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
-				// And then you get shot by Cho. Nice going! Catherine and the islanders are dead
-				// and you have just set Gehn free from Riven, not to mention you're dead.
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
-				runEndGame(10, 5000);
-			} else {
-				// The impossible ending: You don't have Catherine's journal and yet you were somehow
-				// able to open the hatch on the telescope. The game provides an ending for those who
-				// cheat, load a saved game with the combo, or just guess the telescope combo. Atrus
-				// doesn't come and you just fall into the fissure.
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
-				runEndGame(11, 5000);
-			}
-		} else {
-			// ...the telescope can't move down anymore.
-			// Play the sound of not being able to move
-			_vm->_cursor->setCursor(kRivenHideCursor);
-			_vm->_system->updateScreen();
-			_vm->_sound->playSound(13);
-		}
-	} else {
-		// We're not at the bottom, and we can move down again
-
-		// Play a piece of the moving down movie
-		static const uint32 timeIntervals[] = { 4320, 3440, 2560, 1760, 880, 0 };
-		uint16 movieCode = telescopeCover ? 1 : 2;
-		VideoEntryPtr handle = _vm->_video->playMovieRiven(movieCode);
-		handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos], 600), Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600));
-		_vm->_sound->playSound(14); // Play the moving sound
-		_vm->_video->waitUntilMovieEnds(handle);
-
-		// Now move the telescope down a position and refresh
-		telescopePos--;
-		_vm->refreshCard();
-	}
-}
-
-void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
-	// First, show the button movie
-	_vm->_video->playMovieBlockingRiven(3);
-
-	// Don't do anything else if the telescope power is off
-	if (_vm->_vars["ttelevalve"] == 0)
-		return;
-
-	uint32 &telescopePos = _vm->_vars["ttelescope"];
-
-	// Check if we can't move up anymore
-	if (telescopePos == 5) {
-		// Play the sound of not being able to move
-		_vm->_cursor->setCursor(kRivenHideCursor);
-		_vm->_system->updateScreen();
-		_vm->_sound->playSound(13);
-		return;
-	}
-
-	// Play a piece of the moving up movie
-	static const uint32 timeIntervals[] = { 0, 800, 1680, 2560, 3440, 4320 };
-	uint16 movieCode = _vm->_vars["ttelecover"] ? 4 : 5;
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(movieCode);
-	handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600), Audio::Timestamp(0, timeIntervals[telescopePos], 600));
-	_vm->_sound->playSound(14); // Play the moving sound
-	_vm->_video->waitUntilMovieEnds(handle);
-
-	// Now move the telescope up a position and refresh
-	telescopePos++;
-	_vm->refreshCard();
-}
-
-void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {
-	// Called when clicking the telescope cover buttons. argv[0] is the button number (1...5).
-	uint32 &correctDigits = _vm->_vars["tcovercombo"];
-
-	if (correctDigits < 5 && argv[0] == getComboDigit(_vm->_vars["tcorrectorder"], correctDigits))
-		correctDigits++;
-	else
-		correctDigits = 0;
-
-	// If we have hit the correct 5 buttons in a row, activate the hotspot to open up the
-	// telescope cover.
-	RivenHotspot *openCover = _vm->getCard()->getHotspotByName("openCover");
-	openCover->enable(correctDigits == 5);
-}
-
-// Atrus' Journal and Trap Book are added to inventory
-void RivenExternal::xtatrusgivesbooks(uint16 argc, uint16 *argv) {
-	// Give the player Atrus' Journal and the Trap book
-	_vm->_vars["aatrusbook"] = 1;
-	_vm->_vars["atrapbook"] = 1;
-}
-
-// Trap Book is removed from inventory
-void RivenExternal::xtchotakesbook(uint16 argc, uint16 *argv) {
-	// And now Cho takes the trap book. Sure, this isn't strictly
-	// necessary to add and them remove the trap book... but it
-	// seems better to do this ;)
-	_vm->_vars["atrapbook"] = 0;
-}
-
-void RivenExternal::xthideinventory(uint16 argc, uint16 *argv) {
-	_vm->_gfx->hideInventory();
-}
-
-// Marble Puzzle related constants
-static const uint32 kMarbleCount = 6;
-static const int kSmallMarbleWidth = 4;
-static const int kSmallMarbleHeight = 2;
-//static const int kLargeMarbleSize = 8;
-static const int kMarbleHotspotSize = 13;
-static const char *s_marbleNames[] = { "tred", "torange", "tyellow", "tgreen", "tblue", "tviolet" };
-
-// Marble Puzzle helper functions
-// The y portion takes the upper 16 bits, while the x portion takes the lower 16 bits
-static void setMarbleX(uint32 &var, byte x) {
-	var = (var & 0xff00) | (x + 1);
-}
-
-static void setMarbleY(uint32 &var, byte y) {
-	var = ((y + 1) << 16) | (var & 0xff);
-}
-
-static byte getMarbleX(uint32 var) {
-	return (var & 0xff) - 1;
-}
-
-static byte getMarbleY(uint32 var) { // Give that that Y you old hag! </bad Seinfeld reference>
-	return ((var >> 16) & 0xff) - 1;
-}
-
-static Common::Rect generateMarbleGridRect(uint16 x, uint16 y) {
-	// x/y in terms of 0!
-	static const int marbleGridOffsetX[] = { 134, 202, 270, 338, 406 };
-	static const int marbleGridOffsetY[] = {  24,  92, 159, 227, 295 };
-
-	uint16 offsetX = marbleGridOffsetX[x / 5] + (x % 5) * kMarbleHotspotSize;
-	uint16 offsetY = marbleGridOffsetY[y / 5] + (y % 5) * kMarbleHotspotSize;
-	return Common::Rect(offsetX, offsetY, offsetX + kMarbleHotspotSize, offsetY + kMarbleHotspotSize);
-}
-
-void RivenExternal::xt7500_checkmarbles(uint16 argc, uint16 *argv) {
-	// Set apower if the marbles are in their correct spot.
-
-	bool valid = true;
-	static const uint32 marbleFinalValues[] = { 1114121, 1441798, 0, 65552, 65558, 262146 };
-
-	for (uint16 i = 0; i < kMarbleCount; i++)
-		if (_vm->_vars[s_marbleNames[i]] != marbleFinalValues[i]) {
-			valid = false;
-			break;
-		}
-
-	// If we have the correct combo, activate the power and reset the marble positions
-	// Otherwise, make sure the power is off
-	if (valid) {
-		_vm->_vars["apower"] = 1;
-		for (uint16 i = 0; i < kMarbleCount; i++)
-			_vm->_vars[s_marbleNames[i]] = 0;
-	} else
-		_vm->_vars["apower"] = 0;
-}
-
-void RivenExternal::xt7600_setupmarbles(uint16 argc, uint16 *argv) {
-	// Draw the small marbles when we're a step away from the waffle
-
-	// Convert from marble X coordinate to screen X coordinate
-	static const uint16 xPosOffsets[] = {
-		246, 245, 244, 243, 243, 241, 240, 240, 239, 238, 237, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 226, 225
-	};
-
-	// Convert from marble Y coordinate to screen Y coordinate
-	static const uint16 yPosOffsets[] = {
-		261, 263, 265, 267, 268, 270, 272, 274, 276, 278, 281, 284, 285, 288, 290, 293, 295, 298, 300, 303, 306, 309, 311, 314, 316
-	};
-
-	// Handle spacing for y coordinates due to the angle
-	static const double yAdjusts[] = {
-		4.56, 4.68, 4.76, 4.84, 4.84, 4.96, 5.04, 5.04, 5.12, 5.2, 5.28, 5.28, 5.36, 5.44, 5.4, 5.6, 5.72, 5.8, 5.88, 5.96, 6.04, 6.12, 6.2, 6.2, 6.28
-	};
-
-	// Waffle state of 0 is up, 1 down
-	bool waffleDown = _vm->_vars["twaffle"] != 0;
-
-	// Note that each of the small marble images is exactly 4x2
-	// The original seems to scale the marble images from extras.mhk, but
-	// we're using the pre-scaled images in the stack.
-	uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred");
-
-	for (uint16 i = 0; i < kMarbleCount; i++) {
-		uint32 var = _vm->_vars[s_marbleNames[i]];
-
-		if (var == 0) {
-			// The marble is still in its initial place
-			// (Note that this is still drawn even if the waffle is down)
-			static const uint16 defaultX[] = { 375, 377, 379, 381, 383, 385 };
-			static const uint16 defaultY[] = { 253, 257, 261, 265, 268, 273 };
-			_vm->_gfx->copyImageToScreen(baseBitmapId + i, defaultX[i], defaultY[i], defaultX[i] + kSmallMarbleWidth, defaultY[i] + kSmallMarbleHeight);
-		} else if (waffleDown) {
-			// The marble is on the grid and the waffle is down
-			// (Nothing to draw here)
-		} else {
-			// The marble is on the grid and the waffle is up
-			int marbleX = (int)floor(getMarbleX(var) * yAdjusts[getMarbleY(var)] + xPosOffsets[getMarbleY(var)] + 0.5);
-			int marbleY = yPosOffsets[getMarbleY(var)];
-			_vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight);
-		}
-	}
-}
-
-void RivenExternal::setMarbleHotspots() {
-	// Set the hotspots
-	for (uint16 i = 0; i < kMarbleCount; i++) {
-		uint32 marblePos = _vm->_vars[s_marbleNames[i]];
-		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
-
-		if (marblePos == 0) // In the receptacle
-			marbleHotspot->setRect(_marbleBaseHotspots[i]);
-		else                 // On the grid
-			marbleHotspot->setRect(generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)));
-	}
-}
-
-void RivenExternal::xt7800_setup(uint16 argc, uint16 *argv) {
-	// First, let's store the base receptacle hotspots for the marbles
-	if (_marbleBaseHotspots.empty())
-		for (uint16 i = 0; i < kMarbleCount; i++) {
-			RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
-			_marbleBaseHotspots.push_back(marbleHotspot->getRect());
-		}
-
-	// Move the marble hotspots based on their position variables
-	setMarbleHotspots();
-	_vm->_vars["themarble"] = 0;
-}
-
-void RivenExternal::drawMarbles() {
-	for (uint32 i = 0; i < kMarbleCount; i++) {
-		// Don't draw the marble if we're holding it
-		if (_vm->_vars["themarble"] - 1 == i)
-			continue;
-
-		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
-
-		Common::Rect rect = marbleHotspot->getRect();
-		// Trim the rect down a bit
-		rect.left += 3;
-		rect.top += 3;
-		rect.right -= 2;
-		rect.bottom -= 2;
-		_vm->_gfx->drawExtrasImage(i + 200, rect);
-	}
-}
-
-void RivenExternal::xdrawmarbles(uint16 argc, uint16 *argv) {
-	// Draw marbles in the closeup
-	drawMarbles();
-}
-
-void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
-	// Pick up and move a marble
-
-	// First, let's figure out what marble we're now holding
-	uint32 &marble = _vm->_vars["themarble"];
-	marble = 0;
-
-	for (uint32 i = 0; i < kMarbleCount; i++) {
-		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
-		if (marbleHotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
-			marble = i + 1;
-			break;
-		}
-	}
-
-	// xtakeit() shouldn't be called if we're not on a marble hotspot
-	assert(marble != 0);
-
-	// Redraw the background
-	_vm->getCard()->drawPicture(1);
-
-	// Loop until the player lets go (or quits)
-	Common::Event event;
-	bool mouseDown = true;
-	while (mouseDown) {
-		while (_vm->_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONUP)
-				mouseDown = false;
-			else if (event.type == Common::EVENT_MOUSEMOVE)
-				_vm->_system->updateScreen();
-			else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
-				return;
-		}
-
-		_vm->_system->delayMillis(10); // Take it easy on the CPU
-	}
-
-	// Check if we landed in a valid location and no other marble has that location
-	uint32 &marblePos = _vm->_vars[s_marbleNames[marble - 1]];
-
-	bool foundMatch = false;
-	for (int y = 0; y < 25 && !foundMatch; y++) {
-		for (int x = 0; x < 25 && !foundMatch; x++) {
-			Common::Rect testHotspot = generateMarbleGridRect(x, y);
-
-			// Let's try to place the marble!
-			if (testHotspot.contains(_vm->_system->getEventManager()->getMousePos())) {
-				// Set this as the position
-				setMarbleX(marblePos, x);
-				setMarbleY(marblePos, y);
-
-				// Let's make sure no other marble is in this spot...
-				for (uint16 i = 0; i < kMarbleCount; i++)
-					if (i != marble - 1 && _vm->_vars[s_marbleNames[i]] == marblePos)
-						marblePos = 0;
-
-				// We have a match
-				foundMatch = true;
-			}
-		}
-	}
-
-	// If we still don't have a match, reset it to the original location
-	if (!foundMatch)
-		marblePos = 0;
-
-	// Check the new hotspots and refresh everything
-	marble = 0;
-	setMarbleHotspots();
-	_vm->updateCurrentHotspot();
-	_vm->_gfx->updateScreen();
-}
-
-void RivenExternal::xtscpbtn(uint16 argc, uint16 *argv) {
-	runDomeButtonMovie();
-}
-
-void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) {
-	runDomeCheck();
-}
-
-void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) {
-	checkDomeSliders();
-}
-
-void RivenExternal::xtisland5056_resetsliders(uint16 argc, uint16 *argv) {
-	resetDomeSliders(37, 24);
-}
-
-void RivenExternal::xtisland5056_slidermd(uint16 argc, uint16 *argv) {
-	dragDomeSlider(37, 24);
-}
-
-void RivenExternal::xtisland5056_slidermw(uint16 argc, uint16 *argv) {
-	checkSliderCursorChange(24);
-}
-
-void RivenExternal::xtatboundary(uint16 argc, uint16 *argv) {
-	runDemoBoundaryDialog();
-}
-
-// ------------------------------------------------------------------------------------
-// Common external commands
-// ------------------------------------------------------------------------------------
-
-void RivenExternal::xflies(uint16 argc, uint16 *argv) {
-	_vm->_gfx->setFliesEffect(argv[1], argv[0] == 1);
-}
-
-} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
deleted file mode 100644
index f1740f4..0000000
--- a/engines/mohawk/riven_external.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef RIVEN_EXTERNAL_H
-#define RIVEN_EXTERNAL_H
-
-#include "mohawk/riven.h"
-
-namespace Mohawk {
-
-#define COMMAND(x) _externalCommands.push_back(new RivenExternalCmd(#x, &RivenExternal::x))
-
-class RivenExternal {
-public:
-	RivenExternal(MohawkEngine_Riven *vm);
-	~RivenExternal();
-
-	void runCommand(uint16 argc, uint16 *argv);
-	uint16 getComboDigit(uint32 correctCombo, uint32 digit);
-	uint32 getDomeSliderState() { return _sliderState; }
-	void setDomeSliderState(uint32 state) { _sliderState = state; }
-	void checkYtramCatch(bool playSound);
-
-private:
-	MohawkEngine_Riven *_vm;
-	uint32 _sliderState;
-	Common::Array<Common::Rect> _marbleBaseHotspots;
-
-	typedef void (RivenExternal::*ExternalCmd)(uint16 argc, uint16 *argv);
-
-	struct RivenExternalCmd {
-		RivenExternalCmd(const char *d, ExternalCmd p) : desc(d), proc(p) {}
-		const char *desc;
-		ExternalCmd proc;
-	};
-
-	Common::Array<RivenExternalCmd *> _externalCommands;
-	void setupCommands();
-
-	// Supplementary Functions
-	int jspitElevatorLoop();
-	void runDemoBoundaryDialog();
-	void runEndGame(uint16 video, uint32 delay);
-	void runCredits(uint16 video, uint32 delay);
-	void runDomeCheck();
-	void runDomeButtonMovie();
-	void resetDomeSliders(uint16 soundId, uint16 startHotspot);
-	void checkDomeSliders();
-	void checkSliderCursorChange(uint16 startHotspot);
-	void dragDomeSlider(uint16 soundId, uint16 startHotspot);
-	void drawDomeSliders(uint16 startHotspot);
-	void drawMarbles();
-	void setMarbleHotspots();
-	void redrawWharkNumberPuzzle(uint16 overlay, uint16 number);
-	void lowerPins();
-
-	// -----------------------------------------------------
-	// aspit (Main Menu, Books, Setup) external commands
-	// Main Menu
-	void xastartupbtnhide(uint16 argc, uint16 *argv);
-	void xasetupcomplete(uint16 argc, uint16 *argv);
-	// Atrus' Journal
-	void xaatrusopenbook(uint16 argc, uint16 *argv);
-	void xaatrusbookback(uint16 argc, uint16 *argv);
-	void xaatrusbookprevpage(uint16 argc, uint16 *argv);
-	void xaatrusbooknextpage(uint16 argc, uint16 *argv);
-	// Catherine's Journal
-	void xacathopenbook(uint16 argc, uint16 *argv);
-	void xacathbookback(uint16 argc, uint16 *argv);
-	void xacathbookprevpage(uint16 argc, uint16 *argv);
-	void xacathbooknextpage(uint16 argc, uint16 *argv);
-	// Trap Book
-	void xtrapbookback(uint16 argc, uint16 *argv);
-	void xatrapbookclose(uint16 argc, uint16 *argv);
-	void xatrapbookopen(uint16 argc, uint16 *argv);
-	// aspit DVD-specific commands
-	void xarestoregame(uint16 argc, uint16 *argv);
-	// aspit Demo-specific commands
-	void xadisablemenureturn(uint16 argc, uint16 *argv);
-	void xaenablemenureturn(uint16 argc, uint16 *argv);
-	void xalaunchbrowser(uint16 argc, uint16 *argv);
-	void xadisablemenuintro(uint16 argc, uint16 *argv);
-	void xaenablemenuintro(uint16 argc, uint16 *argv);
-	void xademoquit(uint16 argc, uint16 *argv);
-	void xaexittomain(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// bspit (Boiler Island) external commands
-	// Gehn's Lab Journal
-	void xblabopenbook(uint16 argc, uint16 *argv);
-	void xblabbooknextpage(uint16 argc, uint16 *argv);
-	void xblabbookprevpage(uint16 argc, uint16 *argv);
-	// Boiler Puzzle
-	void xsoundplug(uint16 argc, uint16 *argv);
-	void xbchangeboiler(uint16 argc, uint16 *argv);
-	void xbupdateboiler(uint16 argc, uint16 *argv);
-	// Frog Trap
-	void xbsettrap(uint16 argc, uint16 *argv);
-	void xbcheckcatch(uint16 argc, uint16 *argv);
-	void xbait(uint16 argc, uint16 *argv);
-	void xbfreeytram(uint16 argc, uint16 *argv);
-	void xbaitplate(uint16 argc, uint16 *argv);
-	// Dome
-	void xbisland190_opencard(uint16 argc, uint16 *argv);
-	void xbisland190_resetsliders(uint16 argc, uint16 *argv);
-	void xbisland190_slidermd(uint16 argc, uint16 *argv);
-	void xbisland190_slidermw(uint16 argc, uint16 *argv);
-	void xbscpbtn(uint16 argc, uint16 *argv);
-	void xbisland_domecheck(uint16 argc, uint16 *argv);
-	// Water Control
-	void xvalvecontrol(uint16 argc, uint16 *argv);
-	// Run the Wood Chipper
-	void xbchipper(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// gspit (Garden Island) external commands
-	// Pins
-	void xgresetpins(uint16 argc, uint16 *argv);
-	void xgrotatepins(uint16 argc, uint16 *argv);
-	void xgpincontrols(uint16 argc, uint16 *argv);
-	// Dome
-	void xgisland25_opencard(uint16 argc, uint16 *argv);
-	void xgisland25_resetsliders(uint16 argc, uint16 *argv);
-	void xgisland25_slidermd(uint16 argc, uint16 *argv);
-	void xgisland25_slidermw(uint16 argc, uint16 *argv);
-	void xgscpbtn(uint16 argc, uint16 *argv);
-	void xgisland1490_domecheck(uint16 argc, uint16 *argv);
-	// Mapping
-	void xgplateau3160_dopools(uint16 argc, uint16 *argv);
-	// Scribe Taking the Tram
-	void xgwt200_scribetime(uint16 argc, uint16 *argv);
-	void xgwt900_scribe(uint16 argc, uint16 *argv);
-	// Periscope/Prison Viewer
-	void xgplaywhark(uint16 argc, uint16 *argv);
-	void xgrviewer(uint16 argc, uint16 *argv);
-	void xgwharksnd(uint16 argc, uint16 *argv);
-	void xglview_prisonoff(uint16 argc, uint16 *argv);
-	void xglview_villageoff(uint16 argc, uint16 *argv);
-	void xglviewer(uint16 argc, uint16 *argv);
-	void xglview_prisonon(uint16 argc, uint16 *argv);
-	void xglview_villageon(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// jspit (Jungle Island) external commands
-	// Rebel Tunnel Puzzle
-	void xreseticons(uint16 argc, uint16 *argv);
-	void xicon(uint16 argc, uint16 *argv);
-	void xcheckicons(uint16 argc, uint16 *argv);
-	void xtoggleicon(uint16 argc, uint16 *argv);
-	void xjtunnel103_pictfix(uint16 argc, uint16 *argv);
-	void xjtunnel104_pictfix(uint16 argc, uint16 *argv);
-	void xjtunnel105_pictfix(uint16 argc, uint16 *argv);
-	void xjtunnel106_pictfix(uint16 argc, uint16 *argv);
-	// Lower the gallows carriage
-	void xvga1300_carriage(uint16 argc, uint16 *argv);
-	// Dome
-	void xjdome25_resetsliders(uint16 argc, uint16 *argv);
-	void xjdome25_slidermd(uint16 argc, uint16 *argv);
-	void xjdome25_slidermw(uint16 argc, uint16 *argv);
-	void xjscpbtn(uint16 argc, uint16 *argv);
-	void xjisland3500_domecheck(uint16 argc, uint16 *argv);
-	// Whark Elevator
-	void xhandlecontroldown(uint16 argc, uint16 *argv);
-	void xhandlecontrolmid(uint16 argc, uint16 *argv);
-	void xhandlecontrolup(uint16 argc, uint16 *argv);
-	// Beetle
-	void xjplaybeetle_550(uint16 argc, uint16 *argv);
-	void xjplaybeetle_600(uint16 argc, uint16 *argv);
-	void xjplaybeetle_950(uint16 argc, uint16 *argv);
-	void xjplaybeetle_1050(uint16 argc, uint16 *argv);
-	void xjplaybeetle_1450(uint16 argc, uint16 *argv);
-	// Creatures in the Lagoon
-	void xjlagoon700_alert(uint16 argc, uint16 *argv);
-	void xjlagoon800_alert(uint16 argc, uint16 *argv);
-	void xjlagoon1500_alert(uint16 argc, uint16 *argv);
-	// Play the Whark Game
-	void xschool280_playwhark(uint16 argc, uint16 *argv);
-	void xjschool280_resetleft(uint16 argc, uint16 *argv); // DVD only
-	void xjschool280_resetright(uint16 argc, uint16 *argv); // DVD only
-	// jspit Demo-specific commands
-	void xjatboundary(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// ospit (233rd Age / Gehn's Office) external commands
-	// Death!
-	void xorollcredittime(uint16 argc, uint16 *argv);
-	// Trap Book Puzzle
-	void xbookclick(uint16 argc, uint16 *argv); // Four params -- movie_sref, start_time, end_time, u0
-	// Blank Linking Book
-	void xooffice30_closebook(uint16 argc, uint16 *argv);
-	// Gehn's Journal
-    void xobedroom5_closedrawer(uint16 argc, uint16 *argv);
-    void xogehnopenbook(uint16 argc, uint16 *argv);
-    void xogehnbookprevpage(uint16 argc, uint16 *argv);
-    void xogehnbooknextpage(uint16 argc, uint16 *argv);
-	// Elevator Combination
-    void xgwatch(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// pspit (Prison Island) external commands
-	// Prison Elevator
-	void xpisland990_elevcombo(uint16 argc, uint16 *argv);	// Param1: button
-	// Dome
-	void xpscpbtn(uint16 argc, uint16 *argv);
-	void xpisland290_domecheck(uint16 argc, uint16 *argv);
-	void xpisland25_opencard(uint16 argc, uint16 *argv);
-	void xpisland25_resetsliders(uint16 argc, uint16 *argv);
-	void xpisland25_slidermd(uint16 argc, uint16 *argv);
-	void xpisland25_slidermw(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// rspit (Rebel Age / Tay) external commands
-	void xrcredittime(uint16 argc, uint16 *argv);
-	void xrshowinventory(uint16 argc, uint16 *argv);
-	void xrhideinventory(uint16 argc, uint16 *argv);
-	void xrwindowsetup(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// tspit (Temple Island) external commands
-	// Telescope
-	void xtexterior300_telescopedown(uint16 argc, uint16 *argv);
-	void xtexterior300_telescopeup(uint16 argc, uint16 *argv);
-	// Called when clicking the telescope cover buttons. button is the button number (1...5).
-	void xtisland390_covercombo(uint16 argc, uint16 *argv);	// Param1: button
-	// Atrus' Journal and Trap Book are added to inventory
-	void xtatrusgivesbooks(uint16 argc, uint16 *argv);
-	// Trap Book is removed from inventory
-	void xtchotakesbook(uint16 argc, uint16 *argv);
-	void xthideinventory(uint16 argc, uint16 *argv);
-	// Marble Puzzle
-	void xt7500_checkmarbles(uint16 argc, uint16 *argv);
-	void xt7600_setupmarbles(uint16 argc, uint16 *argv);
-	void xt7800_setup(uint16 argc, uint16 *argv);
-	void xdrawmarbles(uint16 argc, uint16 *argv);
-	void xtakeit(uint16 argc, uint16 *argv);
-	// Dome
-	void xtscpbtn(uint16 argc, uint16 *argv);
-	void xtisland4990_domecheck(uint16 argc, uint16 *argv);
-	void xtisland5056_opencard(uint16 argc, uint16 *argv);
-	void xtisland5056_resetsliders(uint16 argc, uint16 *argv);
-	void xtisland5056_slidermd(uint16 argc, uint16 *argv);
-	void xtisland5056_slidermw(uint16 argc, uint16 *argv);
-	// tspit Demo-specific commands
-	void xtatboundary(uint16 argc, uint16 *argv);
-
-	// -----------------------------------------------------
-	// Common external commands
-	void xflies(uint16 argc, uint16 *argv); // Start the "flies" realtime effect. u0 seems always 0, u1 is a small number (< 10).
-};
-
-} // End of namespace Mohawk
-
-#endif
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 44ecca1..c26d4bd 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -23,7 +23,6 @@
 #include "mohawk/cursors.h"
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
-#include "mohawk/riven_external.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_scripts.h"
 #include "mohawk/riven_sound.h"
@@ -444,7 +443,7 @@ void RivenSimpleCommand::delay(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 17: call external command
 void RivenSimpleCommand::runExternalCommand(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_externalScriptHandler->runCommand(argc, argv);
+	_vm->getStack()->runCommand(argc, argv);
 }
 
 // Command 18: transition
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index 83ebf57..225e699 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -22,10 +22,16 @@
 
 #include "mohawk/riven_stack.h"
 
+#include "mohawk/cursors.h"
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
 #include "mohawk/resource.h"
 
+#include "common/events.h"
+
+#include "gui/message.h"
+
 namespace Mohawk {
 
 RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
@@ -34,6 +40,8 @@ RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
 	loadResourceNames();
 	loadCardIdMap();
 	setCurrentStackVariable();
+
+	REGISTER_COMMAND(RivenStack, xflies);
 }
 
 RivenStack::~RivenStack() {
@@ -140,6 +148,77 @@ void RivenStack::dump() const {
 	}
 }
 
+void RivenStack::runCommand(uint16 argc, uint16 *argv) {
+	Common::String externalCommandName = getName(kExternalCommandNames, argv[0]);
+
+	if (!_commands.contains(externalCommandName)) {
+		error("Unknown external command \'%s\'", externalCommandName.c_str());
+	}
+
+	(*_commands[externalCommandName])(argv[1], argv[1] ? argv + 2 : nullptr);
+}
+
+void RivenStack::registerCommand(const Common::String &name, ExternalCommand *command) {
+	_commands[name] = Common::SharedPtr<ExternalCommand>(command);
+}
+
+void RivenStack::xflies(uint16 argc, uint16 *argv) {
+	_vm->_gfx->setFliesEffect(argv[1], argv[0] == 1);
+}
+
+uint16 RivenStack::getComboDigit(uint32 correctCombo, uint32 digit) {
+	static const uint32 powers[] = { 100000, 10000, 1000, 100, 10, 1 };
+	return (correctCombo % powers[digit]) / powers[digit + 1];
+}
+
+void RivenStack::runDemoBoundaryDialog() {
+	GUI::MessageDialog dialog("Exploration beyond this point available only within the full version of\n"
+			                          "the game.");
+	dialog.runModal();
+}
+
+void RivenStack::runEndGame(uint16 video, uint32 delay) {
+	_vm->_sound->stopAllSLST();
+	_vm->_video->playMovieRiven(video);
+	runCredits(video, delay);
+}
+
+void RivenStack::runCredits(uint16 video, uint32 delay) {
+	// Initialize our credits state
+	_vm->_cursor->hideCursor();
+	_vm->_gfx->beginCredits();
+	uint nextCreditsFrameStart = 0;
+
+	VideoEntryPtr videoPtr = _vm->_video->findVideoRiven(video);
+
+	while (!_vm->shouldQuit() && _vm->_gfx->getCurCreditsImage() <= 320) {
+		if (videoPtr->getCurFrame() >= (int32)videoPtr->getFrameCount() - 1) {
+			if (nextCreditsFrameStart == 0) {
+				// Set us up to start after delay ms
+				nextCreditsFrameStart = _vm->_system->getMillis() + delay;
+			} else if (_vm->_system->getMillis() >= nextCreditsFrameStart) {
+				// the first two frames stay on for 4 seconds
+				// the rest of the scroll updates happen at 30Hz
+				if (_vm->_gfx->getCurCreditsImage() < 304)
+					nextCreditsFrameStart = _vm->_system->getMillis() + 4000;
+				else
+					nextCreditsFrameStart = _vm->_system->getMillis() + 1000 / 30;
+
+				_vm->_gfx->updateCredits();
+			}
+		} else if (_vm->_video->updateMovies())
+			_vm->_system->updateScreen();
+
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event))
+			;
+
+		_vm->_system->delayMillis(10);
+	}
+
+	_vm->setGameOver();
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 33c6f48..0911fbb 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -23,6 +23,8 @@
 #ifndef RIVEN_STACK_H
 #define RIVEN_STACK_H
 
+#include "common/hash-str.h"
+#include "common/ptr.h"
 #include "common/str-array.h"
 
 namespace Mohawk {
@@ -99,14 +101,41 @@ public:
 	/** Get the global id of a card in the stack */
 	uint32 getCardGlobalId(uint16 cardId) const;
 
+	/** Run an external command with the specified parameters */
+	void runCommand(uint16 argc, uint16 *argv);
+
 	/** Write all of the stack's data including its cards to standard output */
 	void dump() const;
+
+	// Common external commands
+	void xflies(uint16 argc, uint16 *argv); // Start the "flies" effect
+
+	// TODO: Misc stuff move elsewhere
+	uint16 getComboDigit(uint32 correctCombo, uint32 digit);
+	void runDemoBoundaryDialog();
+	void runEndGame(uint16 video, uint32 delay);
+	void runCredits(uint16 video, uint32 delay);
+
+protected:
+	typedef Common::Functor2<uint16, uint16 *, void> ExternalCommand;
+
+	MohawkEngine_Riven *_vm;
+
+	/** Register an external command for use by the scripts */
+	void registerCommand(const Common::String &name, ExternalCommand *command);
+
 private:
+	typedef Common::HashMap<Common::String, Common::SharedPtr<ExternalCommand>, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> CommandsMap;
+
+#define REGISTER_COMMAND(cls, method) \
+		registerCommand( \
+			#method, new Common::Functor2Mem<uint16, uint16 *, void, cls>(this, &cls::method) \
+		)
+
 	void loadResourceNames();
 	void loadCardIdMap();
 	void setCurrentStackVariable();
 
-	MohawkEngine_Riven *_vm;
 
 	uint16 _id;
 
@@ -118,6 +147,8 @@ private:
 	RivenNameList _stackNames;
 
 	Common::Array<uint32> _cardIdMap;
+
+	CommandsMap _commands;
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp
index f8aa53a..f4ab7d1 100644
--- a/engines/mohawk/riven_stacks/aspit.cpp
+++ b/engines/mohawk/riven_stacks/aspit.cpp
@@ -22,7 +22,14 @@
 
 #include "mohawk/riven_stacks/aspit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
+#include "mohawk/riven_sound.h"
+
+#include "common/translation.h"
+
+#include "gui/message.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,6 +37,300 @@ namespace RivenStacks {
 ASpit::ASpit(MohawkEngine_Riven *vm) :
 		RivenStack(vm, kStackAspit) {
 
+	REGISTER_COMMAND(ASpit, xastartupbtnhide);
+	REGISTER_COMMAND(ASpit, xasetupcomplete);
+	REGISTER_COMMAND(ASpit, xaatrusopenbook);
+	REGISTER_COMMAND(ASpit, xaatrusbookback);
+	REGISTER_COMMAND(ASpit, xaatrusbookprevpage);
+	REGISTER_COMMAND(ASpit, xaatrusbooknextpage);
+	REGISTER_COMMAND(ASpit, xacathopenbook);
+	REGISTER_COMMAND(ASpit, xacathbookback);
+	REGISTER_COMMAND(ASpit, xacathbookprevpage);
+	REGISTER_COMMAND(ASpit, xacathbooknextpage);
+	REGISTER_COMMAND(ASpit, xtrapbookback);
+	REGISTER_COMMAND(ASpit, xatrapbookclose);
+	REGISTER_COMMAND(ASpit, xatrapbookopen);
+	REGISTER_COMMAND(ASpit, xarestoregame);
+	REGISTER_COMMAND(ASpit, xadisablemenureturn);
+	REGISTER_COMMAND(ASpit, xaenablemenureturn);
+	REGISTER_COMMAND(ASpit, xalaunchbrowser);
+	REGISTER_COMMAND(ASpit, xadisablemenuintro);
+	REGISTER_COMMAND(ASpit, xaenablemenuintro);
+	REGISTER_COMMAND(ASpit, xademoquit);
+	REGISTER_COMMAND(ASpit, xaexittomain);
+}
+
+void ASpit::xastartupbtnhide(uint16 argc, uint16 *argv) {
+	// The original game hides the start/setup buttons depending on an ini entry.
+	// It's safe to ignore this command.
+}
+
+void ASpit::xasetupcomplete(uint16 argc, uint16 *argv) {
+	// The original game sets an ini entry to disable the setup button and use the
+	// start button only. It's safe to ignore this part of the command.
+	_vm->_sound->stopSound();
+	_vm->changeToCard(1);
+}
+
+void ASpit::xaatrusopenbook(uint16 argc, uint16 *argv) {
+	// Get the variable
+	uint32 &page = _vm->_vars["aatruspage"];
+
+	// Set hotspots depending on the page
+	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->getCard()->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->getCard()->getHotspotByName("prevpage");
+	if (page == 1) {
+		prevPage->enable(false);
+		nextPage->enable(false);
+		openBook->enable(true);
+	} else {
+		prevPage->enable(true);
+		nextPage->enable(true);
+		openBook->enable(false);
+	}
+
+	// Draw the image of the page
+	_vm->getCard()->drawPicture(page);
+}
+
+void ASpit::xaatrusbookback(uint16 argc, uint16 *argv) {
+	// Return to where we were before entering the book
+	_vm->changeToStack(_vm->_vars["returnstackid"]);
+	_vm->changeToCard(_vm->_vars["returncardid"]);
+}
+
+void ASpit::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
+	// Get the page variable
+	uint32 &page = _vm->_vars["aatruspage"];
+
+	// Decrement the page if it's not the first page
+	if (page == 1)
+		return;
+	page--;
+
+	// Play the page turning sound
+	if (_vm->getFeatures() & GF_DEMO)
+		_vm->_sound->playSound(4);
+	else
+		_vm->_sound->playSound(3);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(1);
+	_vm->getCard()->drawPicture(page);
+}
+
+void ASpit::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
+	// Get the page variable
+	uint32 &page = _vm->_vars["aatruspage"];
+
+	// Increment the page if it's not the last page
+	if (((_vm->getFeatures() & GF_DEMO) && page == 6) || page == 10)
+		return;
+	page++;
+
+	// Play the page turning sound
+	if (_vm->getFeatures() & GF_DEMO)
+		_vm->_sound->playSound(5);
+	else
+		_vm->_sound->playSound(4);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(0);
+	_vm->getCard()->drawPicture(page);
+}
+
+void ASpit::xacathopenbook(uint16 argc, uint16 *argv) {
+	// Get the variable
+	uint32 page = _vm->_vars["acathpage"];
+
+	// Set hotspots depending on the page
+	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
+	RivenHotspot *nextPage = _vm->getCard()->getHotspotByName("nextpage");
+	RivenHotspot *prevPage = _vm->getCard()->getHotspotByName("prevpage");
+	if (page == 1) {
+		prevPage->enable(false);
+		nextPage->enable(false);
+		openBook->enable(true);
+	} else {
+		prevPage->enable(true);
+		nextPage->enable(true);
+		openBook->enable(false);
+	}
+
+	// Draw the image of the page
+	_vm->getCard()->drawPicture(page);
+
+	// Draw the white page edges
+	if (page > 1 && page < 5)
+		_vm->getCard()->drawPicture(50);
+	else if (page > 5)
+		_vm->getCard()->drawPicture(51);
+
+	if (page == 28) {
+		// Draw the telescope combination
+		// The images for the numbers are tBMP's 13 through 17.
+		// The start point is at (156, 247)
+		uint32 teleCombo = _vm->_vars["tcorrectorder"];
+		static const uint16 kNumberWidth = 32;
+		static const uint16 kNumberHeight = 25;
+		static const uint16 kDstX = 156;
+		static const uint16 kDstY = 247;
+
+		for (byte i = 0; i < 5; i++) {
+			uint16 offset = (getComboDigit(teleCombo, i) - 1) * kNumberWidth;
+			Common::Rect srcRect = Common::Rect(offset, 0, offset + kNumberWidth, kNumberHeight);
+			Common::Rect dstRect = Common::Rect(i * kNumberWidth + kDstX, kDstY, (i + 1) * kNumberWidth + kDstX, kDstY + kNumberHeight);
+			_vm->_gfx->drawImageRect(i + 13, srcRect, dstRect);
+		}
+	}
+}
+
+void ASpit::xacathbookback(uint16 argc, uint16 *argv) {
+	// Return to where we were before entering the book
+	_vm->changeToStack(_vm->_vars["returnstackid"]);
+	_vm->changeToCard(_vm->_vars["returncardid"]);
+}
+
+void ASpit::xacathbookprevpage(uint16 argc, uint16 *argv) {
+	// Get the variable
+	uint32 &page = _vm->_vars["acathpage"];
+
+	// Increment the page if it's not the first page
+	if (page == 1)
+		return;
+	page--;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(5);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(3);
+	_vm->getCard()->drawPicture(page);
+}
+
+void ASpit::xacathbooknextpage(uint16 argc, uint16 *argv) {
+	// Get the variable
+	uint32 &page = _vm->_vars["acathpage"];
+
+	// Increment the page if it's not the last page
+	if (page == 49)
+		return;
+	page++;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(6);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(2);
+	_vm->getCard()->drawPicture(page);
+}
+
+void ASpit::xtrapbookback(uint16 argc, uint16 *argv) {
+	// Return to where we were before entering the book
+	_vm->_vars["atrap"] = 0;
+	_vm->changeToStack(_vm->_vars["returnstackid"]);
+	_vm->changeToCard(_vm->_vars["returncardid"]);
+}
+
+void ASpit::xatrapbookclose(uint16 argc, uint16 *argv) {
+	// Close the trap book
+	_vm->_vars["atrap"] = 0;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(8);
+
+	_vm->refreshCard();
+}
+
+void ASpit::xatrapbookopen(uint16 argc, uint16 *argv) {
+	// Open the trap book
+	_vm->_vars["atrap"] = 1;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(9);
+
+	_vm->refreshCard();
+}
+
+void ASpit::xarestoregame(uint16 argc, uint16 *argv) {
+	// Launch the load game dialog
+	_vm->runLoadDialog();
+}
+
+void ASpit::xadisablemenureturn(uint16 argc, uint16 *argv) {
+	// This function would normally enable the Windows menu item for
+	// returning to the main menu. Ctrl+r will do this instead.
+	// The original also had this shortcut.
+}
+
+void ASpit::xaenablemenureturn(uint16 argc, uint16 *argv) {
+	// This function would normally enable the Windows menu item for
+	// returning to the main menu. Ctrl+r will do this instead.
+	// The original also had this shortcut.
+}
+
+void ASpit::xalaunchbrowser(uint16 argc, uint16 *argv) {
+	// Well, we can't launch a browser for obvious reasons ;)
+	// The original text is as follows (for reference):
+
+	// If you have an auto-dial configured connection to the Internet,
+	// please select YES below.
+	//
+	// America Online and CompuServe users may experience difficulty. If
+	// you find that you are unable to connect, please quit the Riven
+	// Demo, launch your browser and type in the following URL:
+	//
+	//     www.redorb.com/buyriven
+	//
+	// Would you like to attempt to make the connection?
+	//
+	// [YES] [NO]
+
+	GUI::MessageDialog dialog(_("At this point, the Riven Demo would\n"
+			                          "ask if you would like to open a web browser\n"
+			                          "to bring you to the Red Orb store to buy\n"
+			                          "the game. ScummVM cannot do that and\n"
+			                          "the site no longer exists."));
+	dialog.runModal();
+}
+
+void ASpit::xadisablemenuintro(uint16 argc, uint16 *argv) {
+	// This function would normally enable the Windows menu item for
+	// playing the intro. Ctrl+p will play the intro movies instead.
+	// The original also had this shortcut.
+
+	// Hide the "exit" button here
+	_vm->_gfx->hideInventory();
+}
+
+void ASpit::xaenablemenuintro(uint16 argc, uint16 *argv) {
+	// This function would normally enable the Windows menu item for
+	// playing the intro. Ctrl+p will play the intro movies instead.
+	// The original also had this shortcut.
+
+	// Show the "exit" button here
+	_vm->_gfx->showInventory();
+}
+
+void ASpit::xademoquit(uint16 argc, uint16 *argv) {
+	// Exactly as it says on the tin. In the demo, this function quits.
+	_vm->setGameOver();
+}
+
+void ASpit::xaexittomain(uint16 argc, uint16 *argv) {
+	// One could potentially implement this function, but there would be no
+	// point. This function is only used in the demo's aspit card 9 update
+	// screen script. However, card 9 is not accessible from the game without
+	// jumping to the card and there's nothing going on in the card so it
+	// never gets called. There's also no card 9 in the full game, so the
+	// functionality of this card was likely removed before release. The
+	// demo executable references some other external commands relating to
+	// setting and getting the volume, as well as drawing the volume. I'd
+	// venture to guess that this would have been some sort of options card
+	// replaced with the Windows/Mac API in the final product.
+	//
+	// Yeah, this function is just dummied and holds a big comment ;)
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/aspit.h b/engines/mohawk/riven_stacks/aspit.h
index 3a55714..60400dd 100644
--- a/engines/mohawk/riven_stacks/aspit.h
+++ b/engines/mohawk/riven_stacks/aspit.h
@@ -28,10 +28,45 @@
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * Main Menu, Books, Setup
+ */
 class ASpit : public RivenStack {
 public:
 	ASpit(MohawkEngine_Riven *vm);
 
+	// External commands - Main Menu
+	void xastartupbtnhide(uint16 argc, uint16 *argv);
+	void xasetupcomplete(uint16 argc, uint16 *argv);
+
+	// External commands - Atrus' Journal
+	void xaatrusopenbook(uint16 argc, uint16 *argv);
+	void xaatrusbookback(uint16 argc, uint16 *argv);
+	void xaatrusbookprevpage(uint16 argc, uint16 *argv);
+	void xaatrusbooknextpage(uint16 argc, uint16 *argv);
+
+	// External commands - Catherine's Journal
+	void xacathopenbook(uint16 argc, uint16 *argv);
+	void xacathbookback(uint16 argc, uint16 *argv);
+	void xacathbookprevpage(uint16 argc, uint16 *argv);
+	void xacathbooknextpage(uint16 argc, uint16 *argv);
+
+	// External commands - Trap Book
+	void xtrapbookback(uint16 argc, uint16 *argv);
+	void xatrapbookclose(uint16 argc, uint16 *argv);
+	void xatrapbookopen(uint16 argc, uint16 *argv);
+
+	// External commands - DVD-specific
+	void xarestoregame(uint16 argc, uint16 *argv);
+
+	// External commands - Demo-specific
+	void xadisablemenureturn(uint16 argc, uint16 *argv);
+	void xaenablemenureturn(uint16 argc, uint16 *argv);
+	void xalaunchbrowser(uint16 argc, uint16 *argv);
+	void xadisablemenuintro(uint16 argc, uint16 *argv);
+	void xaenablemenuintro(uint16 argc, uint16 *argv);
+	void xademoquit(uint16 argc, uint16 *argv);
+	void xaexittomain(uint16 argc, uint16 *argv);
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/bspit.cpp b/engines/mohawk/riven_stacks/bspit.cpp
index 791317c..f94098a 100644
--- a/engines/mohawk/riven_stacks/bspit.cpp
+++ b/engines/mohawk/riven_stacks/bspit.cpp
@@ -22,7 +22,12 @@
 
 #include "mohawk/riven_stacks/bspit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/cursors.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
+
+#include "common/events.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,6 +35,465 @@ namespace RivenStacks {
 BSpit::BSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackBspit) {
 
+	REGISTER_COMMAND(BSpit, xblabopenbook);
+	REGISTER_COMMAND(BSpit, xblabbookprevpage);
+	REGISTER_COMMAND(BSpit, xblabbooknextpage);
+	REGISTER_COMMAND(BSpit, xsoundplug);
+	REGISTER_COMMAND(BSpit, xbchangeboiler);
+	REGISTER_COMMAND(BSpit, xbupdateboiler);
+	REGISTER_COMMAND(BSpit, xbsettrap);
+	REGISTER_COMMAND(BSpit, xbcheckcatch);
+	REGISTER_COMMAND(BSpit, xbait);
+	REGISTER_COMMAND(BSpit, xbfreeytram);
+	REGISTER_COMMAND(BSpit, xbaitplate);
+	REGISTER_COMMAND(BSpit, xbisland190_opencard);
+	REGISTER_COMMAND(BSpit, xbisland190_resetsliders);
+	REGISTER_COMMAND(BSpit, xbisland190_slidermd);
+	REGISTER_COMMAND(BSpit, xbisland190_slidermw);
+	REGISTER_COMMAND(BSpit, xbscpbtn);
+	REGISTER_COMMAND(BSpit, xbisland_domecheck);
+	REGISTER_COMMAND(BSpit, xvalvecontrol);
+	REGISTER_COMMAND(BSpit, xbchipper);
+}
+
+void BSpit::xblabopenbook(uint16 argc, uint16 *argv) {
+	// Get the variable
+	uint32 page = _vm->_vars["blabpage"];
+
+	// Draw the image of the page based on the blabbook variable
+	_vm->getCard()->drawPicture(page);
+
+	if (page == 14) {
+		// Draw the dome combination
+		// The images for the numbers are tBMP's 364 through 368
+		// The start point is at (240, 82)
+		uint32 domeCombo = _vm->_vars["adomecombo"];
+		static const uint16 kNumberWidth = 32;
+		static const uint16 kNumberHeight = 24;
+		static const uint16 kDstX = 240;
+		static const uint16 kDstY = 82;
+		byte numCount = 0;
+
+		for (int bitPos = 24; bitPos >= 0; bitPos--) {
+			if (domeCombo & (1 << bitPos)) {
+				uint16 offset = (24 - bitPos) * kNumberWidth;
+				Common::Rect srcRect = Common::Rect(offset, 0, offset + kNumberWidth, kNumberHeight);
+				Common::Rect dstRect = Common::Rect(numCount * kNumberWidth + kDstX, kDstY, (numCount + 1) * kNumberWidth + kDstX, kDstY + kNumberHeight);
+				_vm->_gfx->drawImageRect(numCount + 364, srcRect, dstRect);
+				numCount++;
+			}
+		}
+
+		assert(numCount == 5); // Sanity check
+	}
+}
+
+void BSpit::xblabbookprevpage(uint16 argc, uint16 *argv) {
+	// Get the page variable
+	uint32 &page = _vm->_vars["blabpage"];
+
+	// Decrement the page if it's not the first page
+	if (page == 1)
+		return;
+	page--;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(22);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(1);
+	_vm->getCard()->drawPicture(page);
+}
+
+void BSpit::xblabbooknextpage(uint16 argc, uint16 *argv) {
+	// Get the page variable
+	uint32 &page = _vm->_vars["blabpage"];
+
+	// Increment the page if it's not the last page
+	if (page == 22)
+		return;
+	page++;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(23);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(0);
+	_vm->getCard()->drawPicture(page);
+}
+
+void BSpit::xsoundplug(uint16 argc, uint16 *argv) {
+	if (_vm->_vars["bheat"] != 0)
+		_vm->getCard()->playSound(1);
+	else if (_vm->_vars["bcratergg"] != 0)
+		_vm->getCard()->playSound(2);
+	else
+		_vm->getCard()->playSound(3);
+}
+
+void BSpit::xbchangeboiler(uint16 argc, uint16 *argv) {
+	uint32 heat = _vm->_vars["bheat"];
+	uint32 water = _vm->_vars["bblrwtr"];
+	uint32 platform = _vm->_vars["bblrgrt"];
+
+	// Stop any background videos
+	_vm->_video->stopVideos();
+
+	if (argv[0] == 1) {
+		// Water is filling/draining from the boiler
+		if (water == 0) {
+			if (platform == 1)
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(12));
+			else
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
+		} else if (heat == 1) {
+			if (platform == 1)
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(22));
+			else
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(19));
+		} else {
+			if (platform == 1)
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(16));
+			else
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(13));
+		}
+	} else if (argv[0] == 2 && water != 0) {
+		if (heat == 1) {
+			// Turning on the heat
+			if (platform == 1)
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(23));
+			else
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(20));
+		} else {
+			// Turning off the heat
+			if (platform == 1)
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(18));
+			else
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(15));
+		}
+	} else if (argv[0] == 3) {
+		if (platform == 1) {
+			// Lowering the platform
+			if (water == 1) {
+				if (heat == 1)
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(24));
+				else
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(17));
+			} else
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
+		} else {
+			// Raising the platform
+			if (water == 1) {
+				if (heat == 1)
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(21));
+				else
+					_vm->_video->activateMLST(_vm->getCard()->getMovie(14));
+			} else
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
+		}
+	}
+
+	if (argc > 1)
+		_vm->getCard()->playSound(argv[1]);
+	else if (argv[0] == 2)
+		_vm->getCard()->playSound(1);
+
+	_vm->_cursor->setCursor(kRivenHideCursor);
+	_vm->_video->playMovieBlockingRiven(11);
+}
+
+void BSpit::xbupdateboiler(uint16 argc, uint16 *argv) {
+	if (_vm->_vars["bheat"] != 0) {
+		if (_vm->_vars["bblrgrt"] == 0) {
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
+			_vm->_video->playMovieRiven(8);
+		} else {
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(7));
+			_vm->_video->playMovieRiven(7);
+		}
+	} else {
+		VideoEntryPtr video = _vm->_video->findVideoRiven(7);
+		if (video)
+			video->setEnabled(false);
+		video = _vm->_video->findVideoRiven(8);
+		if (video)
+			video->setEnabled(false);
+	}
+}
+
+static void ytramTrapTimer(MohawkEngine_Riven *vm) {
+	// Remove this timer
+	vm->removeTimer();
+
+	// FIXME: Improve the timer system (use a functor ?)
+
+	// Check if we've caught a Ytram
+	BSpit *bspit = dynamic_cast<BSpit *>(vm->getStack());
+	if (!bspit) {
+		error("Unexpected stack type in 'ytramTrapTimer'");
+	}
+
+	bspit->checkYtramCatch(true);
+}
+
+void BSpit::xbsettrap(uint16 argc, uint16 *argv) {
+	// Set the Ytram trap
+
+	// We can catch the Ytram between 10 seconds and 3 minutes from now
+	uint32 timeUntilCatch = _vm->_rnd->getRandomNumberRng(10, 60 * 3) * 1000;
+	_vm->_vars["bytramtime"] = timeUntilCatch + _vm->getTotalPlayTime();
+
+	// And set the timer too
+	_vm->installTimer(&ytramTrapTimer, timeUntilCatch);
+}
+
+void BSpit::checkYtramCatch(bool playSound) {
+	// Check if we've caught a Ytram
+
+	uint32 &ytramTime = _vm->_vars["bytramtime"];
+
+	// If the trap still has not gone off, reinstall our timer
+	// This is in case you set the trap, walked away, and returned
+	if (_vm->getTotalPlayTime() < ytramTime) {
+		_vm->installTimer(&ytramTrapTimer, ytramTime - _vm->getTotalPlayTime());
+		return;
+	}
+
+	// Increment the movie per catch (max = 3)
+	uint32 &ytramMovie = _vm->_vars["bytram"];
+	ytramMovie++;
+	if (ytramMovie > 3)
+		ytramMovie = 3;
+
+	// Reset variables
+	_vm->_vars["bytrapped"] = 1;
+	_vm->_vars["bbait"] = 0;
+	_vm->_vars["bytrap"] = 0;
+	ytramTime = 0;
+
+	// Play the capture sound, if requested
+	if (playSound)
+		_vm->_sound->playSound(33);
+}
+
+void BSpit::xbcheckcatch(uint16 argc, uint16 *argv) {
+	// Just pass our parameter along...
+	checkYtramCatch(argv[0] != 0);
+}
+
+void BSpit::xbait(uint16 argc, uint16 *argv) {
+	// Set the cursor to the pellet
+	_vm->_cursor->setCursor(kRivenPelletCursor);
+	_vm->_system->updateScreen();
+
+	// Loop until the player lets go (or quits)
+	Common::Event event;
+	bool mouseDown = true;
+	while (mouseDown) {
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_LBUTTONUP)
+				mouseDown = false;
+			else if (event.type == Common::EVENT_MOUSEMOVE)
+				_vm->_system->updateScreen();
+			else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
+				return;
+		}
+
+		_vm->_system->delayMillis(10); // Take it easy on the CPU
+	}
+
+	// Set back the cursor
+	_vm->_cursor->setCursor(kRivenMainCursor);
+	_vm->_system->updateScreen();
+
+	RivenHotspot *bait = _vm->getCard()->getHotspotByBlstId(9);
+	RivenHotspot *baitPlate = _vm->getCard()->getHotspotByBlstId(16);
+
+	// Set the bait if we put it on the plate
+	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+		_vm->_vars["bbait"] = 1;
+		_vm->getCard()->drawPicture(4);
+
+		bait->enable(false); // Disable bait hotspot
+		baitPlate->enable(true); // Enable baitplate hotspot
+	}
+}
+
+void BSpit::xbfreeytram(uint16 argc, uint16 *argv) {
+	// Play a random Ytram movie after freeing it
+	uint16 mlstId;
+
+	switch (_vm->_vars["bytram"]) {
+		case 1:
+			mlstId = 11;
+			break;
+		case 2:
+			mlstId = 12;
+			break;
+		default:
+			mlstId = _vm->_rnd->getRandomNumberRng(13, 15);
+			break;
+	}
+
+	// Activate the MLST and play the video
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId));
+	_vm->_video->playMovieBlockingRiven(11);
+
+	// Now play the second movie
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId + 5));
+	_vm->_video->playMovieBlockingRiven(12);
+}
+
+void BSpit::xbaitplate(uint16 argc, uint16 *argv) {
+	// Remove the pellet from the plate and put it in your hand
+	_vm->_cursor->setCursor(kRivenPelletCursor);
+	_vm->getCard()->drawPicture(3);
+
+	// Loop until the player lets go (or quits)
+	Common::Event event;
+	bool mouseDown = true;
+	while (mouseDown) {
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_LBUTTONUP)
+				mouseDown = false;
+			else if (event.type == Common::EVENT_MOUSEMOVE)
+				_vm->_system->updateScreen();
+			else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
+				return;
+		}
+
+		_vm->_system->delayMillis(10); // Take it easy on the CPU
+	}
+
+	// Set back the cursor
+	_vm->_cursor->setCursor(kRivenMainCursor);
+	_vm->_system->updateScreen();
+
+	RivenHotspot *bait = _vm->getCard()->getHotspotByBlstId(9);
+	RivenHotspot *baitPlate = _vm->getCard()->getHotspotByBlstId(16);
+
+	// Set the bait if we put it on the plate, remove otherwise
+	if (baitPlate->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+		_vm->_vars["bbait"] = 1;
+		_vm->getCard()->drawPicture(4);
+		bait->enable(false); // Disable bait hotspot
+		baitPlate->enable(true); // Enable baitplate hotspot
+	} else {
+		_vm->_vars["bbait"] = 0;
+		bait->enable(true); // Enable bait hotspot
+		baitPlate->enable(false); // Disable baitplate hotspot
+	}
+}
+
+void BSpit::xbisland190_opencard(uint16 argc, uint16 *argv) {
+	checkDomeSliders();
+}
+
+void BSpit::xbisland190_resetsliders(uint16 argc, uint16 *argv) {
+	resetDomeSliders(41, 9);
+}
+
+void BSpit::xbisland190_slidermd(uint16 argc, uint16 *argv) {
+	dragDomeSlider(41, 9);
+}
+
+void BSpit::xbisland190_slidermw(uint16 argc, uint16 *argv) {
+	checkSliderCursorChange(9);
+}
+
+void BSpit::xbscpbtn(uint16 argc, uint16 *argv) {
+	runDomeButtonMovie();
+}
+
+void BSpit::xbisland_domecheck(uint16 argc, uint16 *argv) {
+	runDomeCheck();
+}
+
+void BSpit::xvalvecontrol(uint16 argc, uint16 *argv) {
+	Common::Point startPos = _vm->_system->getEventManager()->getMousePos();
+
+	// Get the variable for the valve
+	uint32 &valve = _vm->_vars["bvalve"];
+
+	int changeX = 0;
+	int changeY = 0;
+	bool done = false;
+
+	// Set the cursor to the closed position
+	_vm->_cursor->setCursor(kRivenClosedHandCursor);
+	_vm->_system->updateScreen();
+
+	while (!done) {
+		Common::Event event;
+
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+				case Common::EVENT_MOUSEMOVE:
+					changeX = event.mouse.x - startPos.x;
+					changeY = startPos.y - event.mouse.y;
+					_vm->_system->updateScreen();
+					break;
+				case Common::EVENT_LBUTTONUP:
+					// FIXME: These values for changes in x/y could be tweaked.
+					if (valve == 0 && changeY <= -10) {
+						valve = 1;
+						_vm->_cursor->setCursor(kRivenHideCursor);
+						_vm->_system->updateScreen();
+						_vm->_video->playMovieBlockingRiven(2);
+						_vm->refreshCard();
+					} else if (valve == 1) {
+						if (changeX >= 0 && changeY >= 10) {
+							valve = 0;
+							_vm->_cursor->setCursor(kRivenHideCursor);
+							_vm->_system->updateScreen();
+							_vm->_video->playMovieBlockingRiven(3);
+							_vm->refreshCard();
+						} else if (changeX <= -10 && changeY <= 10) {
+							valve = 2;
+							_vm->_cursor->setCursor(kRivenHideCursor);
+							_vm->_system->updateScreen();
+							_vm->_video->playMovieBlockingRiven(1);
+							_vm->refreshCard();
+						}
+					} else if (valve == 2 && changeX >= 10) {
+						valve = 1;
+						_vm->_cursor->setCursor(kRivenHideCursor);
+						_vm->_system->updateScreen();
+						_vm->_video->playMovieBlockingRiven(4);
+						_vm->refreshCard();
+					}
+					done = true;
+				default:
+					break;
+			}
+		}
+		_vm->_system->delayMillis(10);
+	}
+
+	// If we changed state and the new state is that the valve is flowing to
+	// the boiler, we need to update the boiler state.
+	if (valve == 1) {
+		if (_vm->_vars["bidvlv"] == 1) { // Check which way the water is going at the boiler
+			if (_vm->_vars["bblrarm"] == 1) {
+				// If the pipe is open, make sure the water is drained out
+				_vm->_vars["bheat"] = 0;
+				_vm->_vars["bblrwtr"] = 0;
+			} else {
+				// If the pipe is closed, fill the boiler again
+				_vm->_vars["bheat"] = _vm->_vars["bblrvalve"];
+				_vm->_vars["bblrwtr"] = 1;
+			}
+		} else {
+			// Have the grating inside the boiler match the switch outside
+			_vm->_vars["bblrgrt"] = (_vm->_vars["bblrsw"] == 1) ? 0 : 1;
+		}
+	}
+}
+
+void BSpit::xbchipper(uint16 argc, uint16 *argv) {
+	// Why is this an external command....?
+	if (_vm->_vars["bvalve"] == 2)
+		_vm->_video->playMovieBlockingRiven(2);
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/bspit.h b/engines/mohawk/riven_stacks/bspit.h
index be3c052..7e810f2 100644
--- a/engines/mohawk/riven_stacks/bspit.h
+++ b/engines/mohawk/riven_stacks/bspit.h
@@ -28,10 +28,46 @@
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * Boiler Island
+ */
 class BSpit : public DomeSpit {
 public:
 	BSpit(MohawkEngine_Riven *vm);
 
+	// External commands - Gehn's Lab Journal
+	void xblabopenbook(uint16 argc, uint16 *argv);
+	void xblabbooknextpage(uint16 argc, uint16 *argv);
+	void xblabbookprevpage(uint16 argc, uint16 *argv);
+
+	// External commands - Boiler Puzzle
+	void xsoundplug(uint16 argc, uint16 *argv);
+	void xbchangeboiler(uint16 argc, uint16 *argv);
+	void xbupdateboiler(uint16 argc, uint16 *argv);
+
+	// External commands - Frog Trap
+	void xbsettrap(uint16 argc, uint16 *argv);
+	void xbcheckcatch(uint16 argc, uint16 *argv);
+	void xbait(uint16 argc, uint16 *argv);
+	void xbfreeytram(uint16 argc, uint16 *argv);
+	void xbaitplate(uint16 argc, uint16 *argv);
+
+	// External commands - Dome
+	void xbisland190_opencard(uint16 argc, uint16 *argv);
+	void xbisland190_resetsliders(uint16 argc, uint16 *argv);
+	void xbisland190_slidermd(uint16 argc, uint16 *argv);
+	void xbisland190_slidermw(uint16 argc, uint16 *argv);
+	void xbscpbtn(uint16 argc, uint16 *argv);
+	void xbisland_domecheck(uint16 argc, uint16 *argv);
+
+	// External commands - Water Control
+	void xvalvecontrol(uint16 argc, uint16 *argv);
+
+	// External commands - Run the Wood Chipper
+	void xbchipper(uint16 argc, uint16 *argv);
+
+	// Time callback
+	void checkYtramCatch(bool playSound);
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/domespit.cpp b/engines/mohawk/riven_stacks/domespit.cpp
index 4674a24..71a82ab 100644
--- a/engines/mohawk/riven_stacks/domespit.cpp
+++ b/engines/mohawk/riven_stacks/domespit.cpp
@@ -22,12 +22,213 @@
 
 #include "mohawk/riven_stacks/domespit.h"
 
+#include "mohawk/cursors.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
+
+#include "common/events.h"
+
 namespace Mohawk {
 namespace RivenStacks {
 
+static const uint32 kDomeSliderDefaultState = 0x01F00000;
+static const uint32 kDomeSliderSlotCount = 25;
+
 DomeSpit::DomeSpit(MohawkEngine_Riven *vm, uint16 id) :
 		RivenStack(vm, id) {
+	_sliderState = kDomeSliderDefaultState;
+}
+
+void DomeSpit::runDomeButtonMovie() {
+	// This command just plays the video of the button moving down and up.
+	_vm->_video->playMovieBlockingRiven(2);
+}
+
+void DomeSpit::runDomeCheck() {
+	// Check if we clicked while the golden frame was showing
+
+	VideoEntryPtr video = _vm->_video->findVideoRiven(1);
+	assert(video);
+
+	int32 curFrame = video->getCurFrame();
+	int32 frameCount = video->getFrameCount();
+
+	// The final frame of the video is the 'golden' frame (double meaning: the
+	// frame that is the magic one is the one with the golden symbol) but we
+	// give a 3 frame leeway in either direction.
+	if (frameCount - curFrame < 3 || curFrame < 3)
+		_vm->_vars["domecheck"] = 1;
+}
+
+void DomeSpit::resetDomeSliders(uint16 soundId, uint16 startHotspot) {
+	// The rightmost slider should move left until it finds the next slider,
+	// then those two continue until they find the third slider. This continues
+	// until all five sliders have returned their starting slots.
+	byte slidersFound = 0;
+	for (uint32 i = 0; i < kDomeSliderSlotCount; i++) {
+		if (_sliderState & (1 << i)) {
+			// A slider occupies this spot. Increase the number of sliders we
+			// have found, but we're not doing any moving this iteration.
+			slidersFound++;
+		} else {
+			// Move all the sliders we have found over one slot
+			for (byte j = 0; j < slidersFound; j++) {
+				_sliderState &= ~(1 << (i - j - 1));
+				_sliderState |= 1 << (i - j);
+			}
+
+			// If we have at least one found slider, it has now moved
+			// so we should redraw and play a tick sound
+			if (slidersFound) {
+				_vm->_sound->playSound(soundId);
+				drawDomeSliders(startHotspot);
+				_vm->_system->delayMillis(100);
+			}
+		}
+	}
+
+	// Sanity checks - the slider count should always be 5 and we should end up at
+	// the default state after moving them all over.
+	assert(slidersFound == 5);
+	assert(_sliderState == kDomeSliderDefaultState);
+}
+
+void DomeSpit::checkDomeSliders() {
+	RivenHotspot *resetSlidersHotspot = _vm->getCard()->getHotspotByName("ResetSliders");
+	RivenHotspot *openDomeHotspot = _vm->getCard()->getHotspotByName("OpenDome");
+
+	// Let's see if we're all matched up...
+	if (_vm->_vars["adomecombo"] == _sliderState) {
+		// Set the button hotspot to the open dome hotspot
+		resetSlidersHotspot->enable(false);
+		openDomeHotspot->enable(true);
+	} else {
+		// Set the button hotspot to the reset sliders hotspot
+		resetSlidersHotspot->enable(true);
+		openDomeHotspot->enable(false);
+	}
+}
+
+void DomeSpit::checkSliderCursorChange(uint16 startHotspot) {
+	// Set the cursor based on _sliderState and what hotspot we're over
+	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
+		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
+		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+			if (_sliderState & (1 << (24 - i)))
+				_vm->_cursor->setCursor(kRivenOpenHandCursor);
+			else
+				_vm->_cursor->setCursor(kRivenMainCursor);
+			_vm->_system->updateScreen();
+			break;
+		}
+	}
+}
+
+void DomeSpit::dragDomeSlider(uint16 soundId, uint16 startHotspot) {
+	int16 foundSlider = -1;
+
+	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
+		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
+		if (hotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+			// If the slider is not at this hotspot, we can't do anything else
+			if (!(_sliderState & (1 << (24 - i))))
+				return;
+
+			foundSlider = i;
+			break;
+		}
+	}
+
+	// We're not over any slider
+	if (foundSlider < 0)
+		return;
+
+	// We've clicked down, so show the closed hand cursor
+	_vm->_cursor->setCursor(kRivenClosedHandCursor);
+	_vm->_system->updateScreen();
+
+	bool done = false;
+	while (!done) {
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+				case Common::EVENT_MOUSEMOVE:
+					if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider)))) {
+						RivenHotspot *nextHotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + foundSlider + 1);
+						if (nextHotspot->containsPoint(event.mouse)) {
+							// We've moved the slider right one space
+							_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
+							foundSlider++;
+							_sliderState |= 1 << (24 - foundSlider);
+
+							// Now play a click sound and redraw
+							_vm->_sound->playSound(soundId);
+							drawDomeSliders(startHotspot);
+						}
+					} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider)))) {
+						RivenHotspot *previousHotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + foundSlider - 1);
+						if (previousHotspot->containsPoint(event.mouse)) {
+							// We've moved the slider left one space
+							_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
+							foundSlider--;
+							_sliderState |= 1 << (24 - foundSlider);
+
+							// Now play a click sound and redraw
+							_vm->_sound->playSound(soundId);
+							drawDomeSliders(startHotspot);
+						}
+					} else
+						_vm->_system->updateScreen(); // A normal update for the cursor
+					break;
+				case Common::EVENT_LBUTTONUP:
+					done = true;
+					break;
+				default:
+					break;
+			}
+		}
+		_vm->_system->delayMillis(10);
+	}
+
+	// Check to see if we have the right combination
+	checkDomeSliders();
+}
+
+void DomeSpit::drawDomeSliders(uint16 startHotspot) {
+	Common::Rect dstAreaRect = Common::Rect(200, 250, 420, 319);
+
+	// On pspit, the rect is different by two pixels
+	// (alternatively, we could just use hotspot 3 here, but only on pspit is there a hotspot for this)
+	if (_vm->getStack()->getId() == kStackPspit)
+		dstAreaRect.translate(-2, 0);
+
+	// Find out bitmap id
+	uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
+
+	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
+		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
+
+		Common::Rect srcRect = hotspot->getRect();
+		srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
+
+		Common::Rect dstRect = hotspot->getRect();
+
+		if (_sliderState & (1 << (24 - i)))
+			_vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect);
+		else
+			_vm->_gfx->drawImageRect(bitmapId + 1, srcRect, dstRect);
+	}
+
+	_vm->_gfx->updateScreen();
+}
+
+void DomeSpit::setDomeSliderState(uint32 sliderState) {
+	_sliderState = sliderState;
+}
 
+uint32 DomeSpit::getDomeSliderState() const {
+	return _sliderState;
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/domespit.h b/engines/mohawk/riven_stacks/domespit.h
index 776736d..7ca513c 100644
--- a/engines/mohawk/riven_stacks/domespit.h
+++ b/engines/mohawk/riven_stacks/domespit.h
@@ -32,6 +32,19 @@ class DomeSpit : public RivenStack {
 public:
 	DomeSpit(MohawkEngine_Riven *vm, uint16 id);
 
+	uint32 getDomeSliderState() const;
+	void setDomeSliderState(uint32 sliderState);
+
+protected:
+	void runDomeCheck();
+	void runDomeButtonMovie();
+	void resetDomeSliders(uint16 soundId, uint16 startHotspot);
+	void checkDomeSliders();
+	void checkSliderCursorChange(uint16 startHotspot);
+	void dragDomeSlider(uint16 soundId, uint16 startHotspot);
+	void drawDomeSliders(uint16 startHotspot);
+
+	uint32 _sliderState;
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/gspit.cpp b/engines/mohawk/riven_stacks/gspit.cpp
index 8617b28..b70a12c 100644
--- a/engines/mohawk/riven_stacks/gspit.cpp
+++ b/engines/mohawk/riven_stacks/gspit.cpp
@@ -22,7 +22,12 @@
 
 #include "mohawk/riven_stacks/gspit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/cursors.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_sound.h"
+
+#include "common/events.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,6 +35,436 @@ namespace RivenStacks {
 GSpit::GSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackGspit) {
 
+	REGISTER_COMMAND(GSpit, xgresetpins);
+	REGISTER_COMMAND(GSpit, xgrotatepins);
+	REGISTER_COMMAND(GSpit, xgpincontrols);
+	REGISTER_COMMAND(GSpit, xgisland25_opencard);
+	REGISTER_COMMAND(GSpit, xgisland25_resetsliders);
+	REGISTER_COMMAND(GSpit, xgisland25_slidermd);
+	REGISTER_COMMAND(GSpit, xgisland25_slidermw);
+	REGISTER_COMMAND(GSpit, xgscpbtn);
+	REGISTER_COMMAND(GSpit, xgisland1490_domecheck);
+	REGISTER_COMMAND(GSpit, xgplateau3160_dopools);
+	REGISTER_COMMAND(GSpit, xgwt200_scribetime);
+	REGISTER_COMMAND(GSpit, xgwt900_scribe);
+	REGISTER_COMMAND(GSpit, xgplaywhark);
+	REGISTER_COMMAND(GSpit, xgrviewer);
+	REGISTER_COMMAND(GSpit, xgwharksnd);
+	REGISTER_COMMAND(GSpit, xglview_prisonoff);
+	REGISTER_COMMAND(GSpit, xglview_villageoff);
+	REGISTER_COMMAND(GSpit, xglviewer);
+	REGISTER_COMMAND(GSpit, xglview_prisonon);
+	REGISTER_COMMAND(GSpit, xglview_villageon);
+}
+
+void GSpit::lowerPins() {
+	// Lower the pins
+
+	uint32 &pinUp = _vm->_vars["gpinup"];
+
+	if (pinUp == 0)
+		return;
+
+	uint32 &pinPos = _vm->_vars["gpinpos"];
+	uint32 startTime = (pinPos - 1) * 600 + 4830;
+	pinUp = 0;
+
+	// Play the down sound
+	_vm->_sound->playSound(13);
+
+	uint32 &upMovie = _vm->_vars["gupmoov"];
+
+	// Play the video of the pins going down
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(upMovie);
+	assert(handle);
+	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
+	_vm->_video->waitUntilMovieEnds(handle);
+
+	upMovie = 0;
+}
+
+void GSpit::xgresetpins(uint16 argc, uint16 *argv) {
+	// As the function name suggests, this resets the pins
+	lowerPins();
+	_vm->_vars["gupmoov"] = 0;
+}
+
+void GSpit::xgrotatepins(uint16 argc, uint16 *argv) {
+	// Rotate the pins, if necessary
+
+	if (_vm->_vars["gpinup"] == 0)
+		return;
+
+	uint32 &pinPos = _vm->_vars["gpinpos"];
+	uint32 startTime = (pinPos - 1) * 1200;
+
+	if (pinPos == 4)
+		pinPos = 1;
+	else
+		pinPos++;
+
+	// Play the rotating sound
+	_vm->_sound->playSound(12);
+
+	// Play the video of the pins rotating
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(_vm->_vars["gupmoov"]);
+	assert(handle);
+	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 1215, 600));
+	_vm->_video->waitUntilMovieEnds(handle);
+}
+
+void GSpit::xgpincontrols(uint16 argc, uint16 *argv) {
+	// Handle a click on a section of an island
+
+	RivenHotspot *panel = _vm->getCard()->getHotspotByBlstId(13);
+
+	// Get our mouse position and adjust it to the beginning of the hotspot
+	Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
+	mousePos.x -= panel->getRect().left;
+	mousePos.y -= panel->getRect().top;
+
+	// And now adjust it to which box we hit
+	mousePos.x /= 10;
+	mousePos.y /= 11;
+
+	// Lastly, adjust it based on the rotational position
+	uint32 &pinPos = _vm->_vars["gpinpos"];
+	switch (pinPos) {
+		case 1:
+			mousePos.x = 5 - mousePos.x;
+			mousePos.y = (4 - mousePos.y) * 5;
+			break;
+		case 2:
+			mousePos.x = (4 - mousePos.x) * 5;
+			mousePos.y = 1 + mousePos.y;
+			break;
+		case 3:
+			mousePos.x = 1 + mousePos.x;
+			mousePos.y = mousePos.y * 5;
+			break;
+		case 4:
+			mousePos.x = mousePos.x * 5;
+			mousePos.y = 5 - mousePos.y;
+			break;
+		default:
+			// (Should never happen)
+			error("Bad pin pos");
+	}
+
+	// Now check to see if this section of the island exists
+	uint32 islandIndex = _vm->_vars["glkbtns"] - 1;
+	uint16 imagePos = mousePos.x + mousePos.y;
+
+	static const uint16 islandImages[5][11] = {
+			{ 1, 2, 6, 7 },
+			{ 11, 16, 21, 22 },
+			{ 12, 13, 14, 15, 17, 18, 19, 20, 23, 24, 25 },
+			{ 5 },
+			{ 3, 4, 8, 9, 10 }
+	};
+
+	// The scripts set gimagemax to hold the max pin array length in islandPins above
+	uint32 imageCount = _vm->_vars["gimagemax"];
+	uint32 image = 0;
+	for (; image < imageCount; image++)
+		if (islandImages[islandIndex][image] == imagePos)
+			break;
+
+	// If we past it, we don't have a valid map coordinate
+	if (image == imageCount)
+		return;
+
+	uint32 &pinUp = _vm->_vars["gpinup"];
+	uint32 &curImage = _vm->_vars["gimagecurr"];
+
+	// Lower the pins if they are currently raised
+	if (pinUp == 1) {
+		lowerPins();
+
+		// If we just lowered the selected section, don't raise it up again
+		if (curImage == image)
+			return;
+	}
+
+	// Raise the pins by translating the position to the movie code
+	static const uint16 pinMovieCodes[] = { 1, 2, 1, 2, 1, 3, 4, 3, 4, 5, 1, 1, 2, 3, 4, 2, 5, 6, 7, 8, 3, 4, 9, 10, 11 };
+
+	// Play the up sound
+	_vm->_sound->playSound(14);
+
+	// Actually play the movie
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(pinMovieCodes[imagePos - 1]);
+	assert(handle);
+	uint32 startTime = 9630 - pinPos * 600;
+	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
+	_vm->_video->waitUntilMovieEnds(handle);
+
+	// Update the relevant variables
+	_vm->_vars["gupmoov"] = pinMovieCodes[imagePos - 1];
+	pinUp = 1;
+	curImage = image;
+}
+
+void GSpit::xgisland25_opencard(uint16 argc, uint16 *argv) {
+	checkDomeSliders();
+}
+
+void GSpit::xgisland25_resetsliders(uint16 argc, uint16 *argv) {
+	resetDomeSliders(16, 11);
+}
+
+void GSpit::xgisland25_slidermd(uint16 argc, uint16 *argv) {
+	dragDomeSlider(16, 11);
+}
+
+void GSpit::xgisland25_slidermw(uint16 argc, uint16 *argv) {
+	checkSliderCursorChange(11);
+}
+
+void GSpit::xgscpbtn(uint16 argc, uint16 *argv) {
+	runDomeButtonMovie();
+}
+
+void GSpit::xgisland1490_domecheck(uint16 argc, uint16 *argv) {
+	runDomeCheck();
+}
+
+void GSpit::xgplateau3160_dopools(uint16 argc, uint16 *argv) {
+	// Play the deactivation of a pool if one is active and a different one is activated
+	_vm->_cursor->setCursor(kRivenHideCursor);
+	_vm->_system->updateScreen();
+	_vm->_video->playMovieBlockingRiven(_vm->_vars["glkbtns"] * 2);
+}
+
+void GSpit::xgwt200_scribetime(uint16 argc, uint16 *argv) {
+	// Get the current time
+	_vm->_vars["gscribetime"] = _vm->_system->getMillis();
+}
+
+void GSpit::xgwt900_scribe(uint16 argc, uint16 *argv) {
+	uint32 &scribeVar = _vm->_vars["gscribe"];
+
+	if (scribeVar == 1 && _vm->_system->getMillis() > _vm->_vars["gscribetime"] + 40000)
+		scribeVar = 2;
+}
+
+static const uint16 s_viewerTimeIntervals[] = { 0, 816, 1617, 2416, 3216, 4016, 4816, 5616, 6416, 7216, 8016, 8816 };
+
+void GSpit::xgrviewer(uint16 argc, uint16 *argv) {
+	// This controls the viewer on the right side of the 'throne' on Garden Island
+	// (It shows the colors of the marbles)
+
+	// If the light is on, turn it off
+	uint32 &viewerLight = _vm->_vars["grview"];
+	if (viewerLight == 1) {
+		viewerLight = 0;
+		_vm->_sound->playSound(27);
+		_vm->refreshCard();
+
+		// Delay a bit before turning
+		_vm->_system->delayMillis(200);
+	}
+
+	// Calculate how much we're moving
+	Common::String buttonName = _vm->getCard()->getCurHotspot()->getName();
+	uint32 buttonPos = buttonName.lastChar() - '0';
+
+	uint32 &curPos = _vm->_vars["grviewpos"];
+	uint32 newPos = curPos + buttonPos;
+
+	// Now play the movie
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
+	assert(handle);
+	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
+	_vm->_video->waitUntilMovieEnds(handle);
+
+	// Set the new position and let the card's scripts take over again
+	curPos = newPos % 6; // Clip it to 0-5
+	_vm->refreshCard();
+}
+
+void GSpit::xgplaywhark(uint16 argc, uint16 *argv) {
+	// The whark response to using the lights
+
+	// If we've gotten a visit already since we turned out the light, bail out
+	uint32 &wharkState = _vm->_vars["gwharktime"];
+
+	if (wharkState != 1)
+		return;
+
+	wharkState = 0;
+
+	// Increase the amount of times the whark has visited
+	uint32 &wharkVisits = _vm->_vars["gwhark"];
+	wharkVisits++;
+
+	// If we're at 5 or more, the whark will no longer visit us :(
+	if (wharkVisits >= 5) {
+		wharkVisits = 5;
+		return;
+	}
+
+	// Activate the correct video based on the amount of times we've been visited
+	switch (wharkVisits) {
+		case 1:
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(3));
+			break;
+		case 2:
+			// One of two random videos
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(4 + _vm->_rnd->getRandomBit()));
+			break;
+		case 3:
+			// One of two random videos
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(6 + _vm->_rnd->getRandomBit()));
+			break;
+		case 4:
+			// Red alert! Shields online! Brace yourself for impact!
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
+			break;
+	}
+
+	// For whatever reason the devs felt fit, code 31 is used for all of the videos
+	_vm->_video->playMovieBlockingRiven(31);
+	_vm->refreshCard();
+}
+
+void GSpit::xgwharksnd(uint16 argc, uint16 *argv) {
+	// TODO: Random background whark videos
+}
+
+void GSpit::xglviewer(uint16 argc, uint16 *argv) {
+	// This controls the viewer on the left side of the 'throne' on Garden Island
+	// (It shows the village from the middle of the lake)
+
+	// Calculate how much we're moving
+	Common::String buttonName = _vm->getCard()->getCurHotspot()->getName();
+	uint32 buttonPos = buttonName.lastChar() - '0';
+
+	uint32 &curPos = _vm->_vars["glviewpos"];
+	uint32 newPos = curPos + buttonPos;
+
+	// Now play the movie
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
+	assert(handle);
+	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
+	_vm->_video->waitUntilMovieEnds(handle);
+
+	// Set the new position to the variable
+	curPos = newPos % 6; // Clip it to 0-5
+
+	// And update the screen with the new image
+	_vm->getCard()->drawPicture(curPos + 2);
+}
+
+void GSpit::xglview_villageon(uint16 argc, uint16 *argv) {
+	// Turn on the left viewer to 'village mode'
+	_vm->_vars["glview"] = 2;
+	_vm->getCard()->drawPicture(_vm->_vars["glviewpos"] + 2);
+}
+
+void GSpit::xglview_villageoff(uint16 argc, uint16 *argv) {
+	// Turn off the left viewer when in 'village mode' (why is this external?)
+	_vm->_vars["glview"] = 0;
+	_vm->getCard()->drawPicture(1);
+}
+
+static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
+	uint32 &cathState = vm->_vars["gcathstate"];
+	uint16 movie;
+
+	// Choose a new movie
+	if (cathState == 1) {
+		static const int movieList[] = { 9, 10, 19, 19, 21, 21 };
+		movie = movieList[vm->_rnd->getRandomNumber(5)];
+	} else if (cathState == 2) {
+		static const int movieList[] = { 18, 20, 22 };
+		movie = movieList[vm->_rnd->getRandomNumber(2)];
+	} else {
+		static const int movieList[] = { 11, 11, 12, 17, 17, 17, 17, 23 };
+		movie = movieList[vm->_rnd->getRandomNumber(7)];
+	}
+
+	// Update Catherine's state
+	if (movie == 10 || movie == 17 || movie == 18 || movie == 20)
+		cathState = 1;
+	else if (movie == 19 || movie == 21 || movie == 23)
+		cathState = 2;
+	else
+		cathState = 3;
+
+	// Begin playing the new movie
+	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
+	VideoEntryPtr video = vm->_video->playMovieRiven(30);
+
+	// Reset the timer
+	vm->installTimer(&catherineViewerIdleTimer, video->getDuration().msecs() + vm->_rnd->getRandomNumber(60) * 1000);
+}
+
+void GSpit::xglview_prisonon(uint16 argc, uint16 *argv) {
+	// Activate random background Catherine videos
+
+	// Turn on the left viewer to 'prison mode'
+	_vm->_vars["glview"] = 1;
+
+	// Get basic starting states
+	uint16 cathMovie = _vm->_rnd->getRandomNumberRng(8, 23);
+	uint16 turnOnMovie = 4;
+	uint32 &cathState = _vm->_vars["gcathstate"];
+
+	// Adjust the turn on movie
+	if (cathMovie == 14)
+		turnOnMovie = 6;
+	else if (cathMovie == 15)
+		turnOnMovie = 7;
+
+	// Adjust Catherine's state
+	if (cathMovie == 9 || cathMovie == 11 || cathMovie == 12 || cathMovie == 22)
+		cathState = 3;
+	else if (cathMovie == 19 || cathMovie == 21 || cathMovie == 23 || cathMovie == 14)
+		cathState = 2;
+	else
+		cathState = 1;
+
+	// Turn on the viewer
+	_vm->_cursor->hideCursor();
+	_vm->_video->playMovieBlockingRiven(turnOnMovie);
+	_vm->_cursor->showCursor();
+
+	uint32 timeUntilNextMovie;
+
+	// Begin playing a movie immediately if Catherine is already in the viewer
+	if (cathMovie == 8 || (cathMovie >= 13 && cathMovie <= 16)) {
+		_vm->_video->activateMLST(_vm->getCard()->getMovie(cathMovie));
+		VideoEntryPtr video = _vm->_video->playMovieRiven(30);
+
+		timeUntilNextMovie = video->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
+	} else {
+		// Otherwise, just redraw the imager
+		timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000;
+		_vm->getCard()->drawPicture(8);
+	}
+
+	// Create the timer for the next video
+	_vm->installTimer(&catherineViewerIdleTimer, timeUntilNextMovie);
+}
+
+void GSpit::xglview_prisonoff(uint16 argc, uint16 *argv) {
+	// Deactivate random background Catherine videos
+
+	// Update the viewer state (now off)
+	_vm->_vars["glview"] = 0;
+
+	// Remove the timer we set in xglview_prisonon()
+	_vm->removeTimer();
+
+	// Play the 'turn off' movie after stopping any videos still playing
+	_vm->_video->stopVideos();
+	_vm->_cursor->hideCursor();
+	_vm->_video->playMovieBlockingRiven(5);
+	_vm->_cursor->showCursor();
+
+	// Redraw the viewer
+	_vm->getCard()->drawPicture(1);
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/gspit.h b/engines/mohawk/riven_stacks/gspit.h
index 794a95b..045d47a 100644
--- a/engines/mohawk/riven_stacks/gspit.h
+++ b/engines/mohawk/riven_stacks/gspit.h
@@ -28,10 +28,45 @@
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * Garden Island
+ */
 class GSpit : public DomeSpit {
 public:
 	GSpit(MohawkEngine_Riven *vm);
 
+	// External commands - Pins
+	void xgresetpins(uint16 argc, uint16 *argv);
+	void xgrotatepins(uint16 argc, uint16 *argv);
+	void xgpincontrols(uint16 argc, uint16 *argv);
+
+	// External commands - Dome
+	void xgisland25_opencard(uint16 argc, uint16 *argv);
+	void xgisland25_resetsliders(uint16 argc, uint16 *argv);
+	void xgisland25_slidermd(uint16 argc, uint16 *argv);
+	void xgisland25_slidermw(uint16 argc, uint16 *argv);
+	void xgscpbtn(uint16 argc, uint16 *argv);
+	void xgisland1490_domecheck(uint16 argc, uint16 *argv);
+
+	// External commands - Mapping
+	void xgplateau3160_dopools(uint16 argc, uint16 *argv);
+
+	// External commands - Scribe Taking the Tram
+	void xgwt200_scribetime(uint16 argc, uint16 *argv);
+	void xgwt900_scribe(uint16 argc, uint16 *argv);
+
+	// External commands - Periscope/Prison Viewer
+	void xgplaywhark(uint16 argc, uint16 *argv);
+	void xgrviewer(uint16 argc, uint16 *argv);
+	void xgwharksnd(uint16 argc, uint16 *argv);
+	void xglview_prisonoff(uint16 argc, uint16 *argv);
+	void xglview_villageoff(uint16 argc, uint16 *argv);
+	void xglviewer(uint16 argc, uint16 *argv);
+	void xglview_prisonon(uint16 argc, uint16 *argv);
+	void xglview_villageon(uint16 argc, uint16 *argv);
+
+private:
+	void lowerPins();
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
index e19d289..582c1d6 100644
--- a/engines/mohawk/riven_stacks/jspit.cpp
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -22,7 +22,12 @@
 
 #include "mohawk/riven_stacks/jspit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/cursors.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
+
+#include "common/events.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,6 +35,502 @@ namespace RivenStacks {
 JSpit::JSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackJspit) {
 
+	REGISTER_COMMAND(JSpit, xreseticons);
+	REGISTER_COMMAND(JSpit, xicon);
+	REGISTER_COMMAND(JSpit, xcheckicons);
+	REGISTER_COMMAND(JSpit, xtoggleicon);
+	REGISTER_COMMAND(JSpit, xjtunnel103_pictfix);
+	REGISTER_COMMAND(JSpit, xjtunnel104_pictfix);
+	REGISTER_COMMAND(JSpit, xjtunnel105_pictfix);
+	REGISTER_COMMAND(JSpit, xjtunnel106_pictfix);
+	REGISTER_COMMAND(JSpit, xvga1300_carriage);
+	REGISTER_COMMAND(JSpit, xjdome25_resetsliders);
+	REGISTER_COMMAND(JSpit, xjdome25_slidermd);
+	REGISTER_COMMAND(JSpit, xjdome25_slidermw);
+	REGISTER_COMMAND(JSpit, xjscpbtn);
+	REGISTER_COMMAND(JSpit, xjisland3500_domecheck);
+	REGISTER_COMMAND(JSpit, xhandlecontroldown);
+	REGISTER_COMMAND(JSpit, xhandlecontrolmid);
+	REGISTER_COMMAND(JSpit, xhandlecontrolup);
+	REGISTER_COMMAND(JSpit, xjplaybeetle_550);
+	REGISTER_COMMAND(JSpit, xjplaybeetle_600);
+	REGISTER_COMMAND(JSpit, xjplaybeetle_950);
+	REGISTER_COMMAND(JSpit, xjplaybeetle_1050);
+	REGISTER_COMMAND(JSpit, xjplaybeetle_1450);
+	REGISTER_COMMAND(JSpit, xjlagoon700_alert);
+	REGISTER_COMMAND(JSpit, xjlagoon800_alert);
+	REGISTER_COMMAND(JSpit, xjlagoon1500_alert);
+	REGISTER_COMMAND(JSpit, xschool280_playwhark);
+	REGISTER_COMMAND(JSpit, xjschool280_resetleft);
+	REGISTER_COMMAND(JSpit, xjschool280_resetright);
+	REGISTER_COMMAND(JSpit, xjatboundary);
+}
+
+void JSpit::xreseticons(uint16 argc, uint16 *argv) {
+	// Reset the icons when going to Tay (rspit)
+	_vm->_vars["jicons"] = 0;
+	_vm->_vars["jiconorder"] = 0;
+	_vm->_vars["jrbook"] = 0;
+}
+
+// Count up how many icons are pressed
+static byte countDepressedIcons(uint32 iconOrderVar) {
+	if (iconOrderVar >= (1 << 20))
+		return 5;
+	else if (iconOrderVar >= (1 << 15))
+		return 4;
+	else if (iconOrderVar >= (1 << 10))
+		return 3;
+	else if (iconOrderVar >= (1 << 5))
+		return 2;
+	else if (iconOrderVar >= (1 << 1))
+		return 1;
+	else
+		return 0;
+}
+
+void JSpit::xicon(uint16 argc, uint16 *argv) {
+	// Set atemp as the status of whether or not the icon can be depressed.
+	if (_vm->_vars["jicons"] & (1 << (argv[0] - 1))) {
+		// This icon is depressed. Allow depression only if the last depressed icon was this one.
+		if ((_vm->_vars["jiconorder"] & 0x1f) == argv[0])
+			_vm->_vars["atemp"] = 1;
+		else
+			_vm->_vars["atemp"] = 2;
+	} else
+		_vm->_vars["atemp"] = 0;
+}
+
+void JSpit::xcheckicons(uint16 argc, uint16 *argv) {
+	// Reset the icons if this is the sixth icon
+	uint32 &iconOrderVar = _vm->_vars["jiconorder"];
+	if (countDepressedIcons(iconOrderVar) == 5) {
+		iconOrderVar = 0;
+		_vm->_vars["jicons"] = 0;
+		_vm->_sound->playSound(46);
+	}
+}
+
+void JSpit::xtoggleicon(uint16 argc, uint16 *argv) {
+	// Get the variables
+	uint32 &iconsDepressed = _vm->_vars["jicons"];
+	uint32 &iconOrderVar = _vm->_vars["jiconorder"];
+
+	if (iconsDepressed & (1 << (argv[0] - 1))) {
+		// The icon is depressed, now unpress it
+		iconsDepressed &= ~(1 << (argv[0] - 1));
+		iconOrderVar >>= 5;
+	} else {
+		// The icon is not depressed, now depress it
+		iconsDepressed |= 1 << (argv[0] - 1);
+		iconOrderVar = (iconOrderVar << 5) + argv[0];
+	}
+
+	// Check if the puzzle is complete now and assign 1 to jrbook if the puzzle is complete.
+	if (iconOrderVar == _vm->_vars["jiconcorrectorder"])
+		_vm->_vars["jrbook"] = 1;
+}
+
+void JSpit::xjtunnel103_pictfix(uint16 argc, uint16 *argv) {
+	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
+	uint32 iconsDepressed = _vm->_vars["jicons"];
+
+	// Now, draw which icons are depressed based on the bits of the variable
+	if (iconsDepressed & (1 << 0))
+		_vm->getCard()->drawPicture(2);
+	if (iconsDepressed & (1 << 1))
+		_vm->getCard()->drawPicture(3);
+	if (iconsDepressed & (1 << 2))
+		_vm->getCard()->drawPicture(4);
+	if (iconsDepressed & (1 << 3))
+		_vm->getCard()->drawPicture(5);
+	if (iconsDepressed & (1 << 22))
+		_vm->getCard()->drawPicture(6);
+	if (iconsDepressed & (1 << 23))
+		_vm->getCard()->drawPicture(7);
+	if (iconsDepressed & (1 << 24))
+		_vm->getCard()->drawPicture(8);
+}
+
+void JSpit::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
+	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
+	uint32 iconsDepressed = _vm->_vars["jicons"];
+
+	// Now, draw which icons are depressed based on the bits of the variable
+	if (iconsDepressed & (1 << 9))
+		_vm->getCard()->drawPicture(2);
+	if (iconsDepressed & (1 << 10))
+		_vm->getCard()->drawPicture(3);
+	if (iconsDepressed & (1 << 11))
+		_vm->getCard()->drawPicture(4);
+	if (iconsDepressed & (1 << 12))
+		_vm->getCard()->drawPicture(5);
+	if (iconsDepressed & (1 << 13))
+		_vm->getCard()->drawPicture(6);
+	if (iconsDepressed & (1 << 14))
+		_vm->getCard()->drawPicture(7);
+	if (iconsDepressed & (1 << 15))
+		_vm->getCard()->drawPicture(8);
+	if (iconsDepressed & (1 << 16))
+		_vm->getCard()->drawPicture(9);
+}
+
+void JSpit::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
+	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
+	uint32 iconsDepressed = _vm->_vars["jicons"];
+
+	// Now, draw which icons are depressed based on the bits of the variable
+	if (iconsDepressed & (1 << 3))
+		_vm->getCard()->drawPicture(2);
+	if (iconsDepressed & (1 << 4))
+		_vm->getCard()->drawPicture(3);
+	if (iconsDepressed & (1 << 5))
+		_vm->getCard()->drawPicture(4);
+	if (iconsDepressed & (1 << 6))
+		_vm->getCard()->drawPicture(5);
+	if (iconsDepressed & (1 << 7))
+		_vm->getCard()->drawPicture(6);
+	if (iconsDepressed & (1 << 8))
+		_vm->getCard()->drawPicture(7);
+	if (iconsDepressed & (1 << 9))
+		_vm->getCard()->drawPicture(8);
+}
+
+void JSpit::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
+	// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
+	uint32 iconsDepressed = _vm->_vars["jicons"];
+
+	// Now, draw which icons are depressed based on the bits of the variable
+	if (iconsDepressed & (1 << 16))
+		_vm->getCard()->drawPicture(2);
+	if (iconsDepressed & (1 << 17))
+		_vm->getCard()->drawPicture(3);
+	if (iconsDepressed & (1 << 18))
+		_vm->getCard()->drawPicture(4);
+	if (iconsDepressed & (1 << 19))
+		_vm->getCard()->drawPicture(5);
+	if (iconsDepressed & (1 << 20))
+		_vm->getCard()->drawPicture(6);
+	if (iconsDepressed & (1 << 21))
+		_vm->getCard()->drawPicture(7);
+	if (iconsDepressed & (1 << 22))
+		_vm->getCard()->drawPicture(8);
+	if (iconsDepressed & (1 << 23))
+		_vm->getCard()->drawPicture(9);
+}
+
+void JSpit::xvga1300_carriage(uint16 argc, uint16 *argv) {
+	// Run the gallows's carriage
+
+	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
+	_vm->_system->updateScreen();                      // Update
+	_vm->_video->playMovieBlockingRiven(1);            // Play handle movie
+	_vm->_gfx->scheduleTransition(15);                 // Set pan down transition
+	_vm->changeToCard(_vm->getStack()->getCardStackId(0x18e77));  // Change to card facing up
+	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor (again)
+	_vm->_system->updateScreen();                      // Update
+	_vm->_video->playMovieBlockingRiven(4);            // Play carriage beginning to drop
+	_vm->_gfx->scheduleTransition(14);                 // Set pan up transition
+	_vm->changeToCard(_vm->getStack()->getCardStackId(0x183a9));  // Change to card looking straight again
+	_vm->_video->playMovieBlockingRiven(2);
+
+	if (_vm->_vars["jgallows"] == 1) {
+		// If the gallows is open, play the up movie and return
+		_vm->_video->playMovieBlockingRiven(3);
+		return;
+	}
+
+	// Give the player 5 seconds to click (anywhere)
+	uint32 startTime = _vm->_system->getMillis();
+	bool gotClick = false;
+	while (_vm->_system->getMillis() - startTime <= 5000 && !gotClick) {
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+				case Common::EVENT_MOUSEMOVE:
+					_vm->_system->updateScreen();
+					break;
+				case Common::EVENT_LBUTTONUP:
+					gotClick = true;
+					break;
+				default:
+					break;
+			}
+		}
+
+		_vm->_system->delayMillis(10);
+	}
+
+	_vm->_cursor->setCursor(kRivenHideCursor);             // Hide the cursor
+	_vm->_system->updateScreen();                          // Update
+
+	if (gotClick) {
+		_vm->_gfx->scheduleTransition(16);                 // Schedule dissolve transition
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18d4d));  // Move forward
+		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
+		_vm->_system->updateScreen();                      // Update
+		_vm->_system->delayMillis(500);                    // Delay a half second before changing again
+		_vm->_gfx->scheduleTransition(12);                 // Schedule pan left transition
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18ab5));  // Turn right
+		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
+		_vm->_system->updateScreen();                      // Update
+		_vm->_video->playMovieBlockingRiven(1);            // Play carriage ride movie
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x17167));  // We have arrived at the top
+	} else
+		_vm->_video->playMovieBlockingRiven(3);            // Too slow!
+}
+
+void JSpit::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
+	resetDomeSliders(81, 10);
+}
+
+void JSpit::xjdome25_slidermd(uint16 argc, uint16 *argv) {
+	dragDomeSlider(81, 10);
+}
+
+void JSpit::xjdome25_slidermw(uint16 argc, uint16 *argv) {
+	checkSliderCursorChange(10);
+}
+
+void JSpit::xjscpbtn(uint16 argc, uint16 *argv) {
+	runDomeButtonMovie();
+}
+
+void JSpit::xjisland3500_domecheck(uint16 argc, uint16 *argv) {
+	runDomeCheck();
+}
+
+int JSpit::jspitElevatorLoop() {
+	Common::Point startPos = _vm->_system->getEventManager()->getMousePos();
+
+	Common::Event event;
+	int changeLevel = 0;
+
+	_vm->_cursor->setCursor(kRivenClosedHandCursor);
+	_vm->_system->updateScreen();
+
+	for (;;) {
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+				case Common::EVENT_MOUSEMOVE:
+					if (event.mouse.y > (startPos.y + 10)) {
+						changeLevel = -1;
+					} else if (event.mouse.y < (startPos.y - 10)) {
+						changeLevel = 1;
+					} else {
+						changeLevel = 0;
+					}
+					_vm->_system->updateScreen();
+					break;
+				case Common::EVENT_LBUTTONUP:
+					_vm->_cursor->setCursor(kRivenMainCursor);
+					_vm->_system->updateScreen();
+					return changeLevel;
+				default:
+					break;
+			}
+		}
+		_vm->_system->delayMillis(10);
+	}
+}
+
+void JSpit::xhandlecontrolup(uint16 argc, uint16 *argv) {
+	int changeLevel = jspitElevatorLoop();
+
+	// If we've moved the handle down, go down a floor
+	if (changeLevel == -1) {
+		_vm->_video->playMovieBlockingRiven(1);
+		_vm->_video->playMovieBlockingRiven(2);
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
+	}
+}
+
+void JSpit::xhandlecontroldown(uint16 argc, uint16 *argv) {
+	int changeLevel = jspitElevatorLoop();
+
+	// If we've moved the handle up, go up a floor
+	if (changeLevel == 1) {
+		_vm->_video->playMovieBlockingRiven(1);
+		_vm->_video->playMovieBlockingRiven(2);
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
+	}
+}
+
+void JSpit::xhandlecontrolmid(uint16 argc, uint16 *argv) {
+	int changeLevel = jspitElevatorLoop();
+
+	if (changeLevel == 0)
+		return;
+
+	// Play the handle moving video
+	if (changeLevel == 1)
+		_vm->_video->playMovieBlockingRiven(7);
+	else
+		_vm->_video->playMovieBlockingRiven(6);
+
+	// If the whark's mouth is open, close it
+	uint32 &mouthVar = _vm->_vars["jwmouth"];
+	if (mouthVar == 1) {
+		_vm->_video->playMovieBlockingRiven(3);
+		_vm->_video->playMovieBlockingRiven(8);
+		mouthVar = 0;
+	}
+
+	// Play the elevator video and then change the card
+	if (changeLevel == 1) {
+		_vm->_video->playMovieBlockingRiven(5);
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e597));
+	} else {
+		_vm->_video->playMovieBlockingRiven(4);
+		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e29c));
+	}
+}
+
+void JSpit::xjplaybeetle_550(uint16 argc, uint16 *argv) {
+	// Play a beetle animation 25% of the time
+	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+}
+
+void JSpit::xjplaybeetle_600(uint16 argc, uint16 *argv) {
+	// Play a beetle animation 25% of the time
+	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+}
+
+void JSpit::xjplaybeetle_950(uint16 argc, uint16 *argv) {
+	// Play a beetle animation 25% of the time
+	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+}
+
+void JSpit::xjplaybeetle_1050(uint16 argc, uint16 *argv) {
+	// Play a beetle animation 25% of the time
+	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+}
+
+void JSpit::xjplaybeetle_1450(uint16 argc, uint16 *argv) {
+	// Play a beetle animation 25% of the time as long as the girl is not present
+	_vm->_vars["jplaybeetle"] = (_vm->_rnd->getRandomNumberRng(0, 3) == 0 && _vm->_vars["jgirl"] != 1) ? 1 : 0;
+}
+
+void JSpit::xjlagoon700_alert(uint16 argc, uint16 *argv) {
+	// Handle sunner reactions (mid-staircase)
+
+	if (_vm->_vars["jsunners"] == 0)
+		_vm->_video->playMovieRiven(1);
+}
+
+void JSpit::xjlagoon800_alert(uint16 argc, uint16 *argv) {
+	// Handle sunner reactions (lower-staircase)
+
+	uint32 &sunners = _vm->_vars["jsunners"];
+
+	if (sunners == 0) {
+		// Show the sunners alert video
+		_vm->_video->playMovieRiven(1);
+	} else if (sunners == 1) {
+		// Show the sunners leaving if you moved forward in their "alert" status
+		_vm->_video->playMovieBlockingRiven(2);
+		_vm->_video->playMovieBlockingRiven(6);
+		sunners = 2;
+		_vm->refreshCard();
+	}
+}
+
+void JSpit::xjlagoon1500_alert(uint16 argc, uint16 *argv) {
+	// Handle sunner reactions (beach)
+
+	uint32 &sunners = _vm->_vars["jsunners"];
+
+	if (sunners == 0) {
+		// Show the sunners alert video
+		_vm->_video->playMovieBlockingRiven(3);
+	} else if (sunners == 1) {
+		// Show the sunners leaving if you moved forward in their "alert" status
+		_vm->_video->playMovieBlockingRiven(2);
+		sunners = 2;
+		_vm->refreshCard();
+	}
+}
+
+void JSpit::xjschool280_resetleft(uint16 argc, uint16 *argv) {
+	// Dummy function. This resets the unneeded video timing variable (dropLeftStart) in
+	// the DVD version.
+}
+
+void JSpit::xjschool280_resetright(uint16 argc, uint16 *argv) {
+	// Dummy function. This resets the unneeded video timing variable (dropRightStart) in
+	// the DVD version.
+}
+
+void JSpit::redrawWharkNumberPuzzle(uint16 overlay, uint16 number) {
+	// Update the screen for the whark number puzzle
+	// We don't update the whole screen here because we don't want to overwrite the video data
+	_vm->getCard()->drawPicture(overlay);
+	_vm->getCard()->drawPicture(number + 1);
+	_vm->_gfx->updateScreen(Common::Rect(80, 212, 477, 392));
+	_vm->_system->updateScreen();
+}
+
+void JSpit::xschool280_playwhark(uint16 argc, uint16 *argv) {
+	// The "monstrous" whark puzzle that teaches the number system
+
+	uint32 *posVar;
+	uint16 spinMLST, overlayPLST, doomMLST, snackMLST;
+
+	// Choose left or right based on jwharkpos (which is set by the scripts)
+	if (_vm->_vars["jwharkpos"] == 1) {
+		posVar = &_vm->_vars["jleftpos"];
+		spinMLST = 1;
+		overlayPLST = 12;
+		doomMLST = 3;
+		snackMLST = 4;
+	} else {
+		posVar = &_vm->_vars["jrightpos"];
+		spinMLST = 2;
+		overlayPLST = 13;
+		doomMLST = 5;
+		snackMLST = 6;
+	}
+
+	// Hide the cursor
+	_vm->_cursor->setCursor(kRivenHideCursor);
+	_vm->_system->updateScreen();
+
+	// Play the spin movie
+	_vm->_video->playMovieBlockingRiven(spinMLST);
+
+	// Get our random number and redraw the area
+	uint16 number = _vm->_rnd->getRandomNumberRng(1, 10);
+	redrawWharkNumberPuzzle(overlayPLST, number);
+
+	// Handle movement
+	// (11560/600)s is the length of each of the two movies. We divide it into 19 parts
+	// (one for each of the possible positions the villager can have).
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(doomMLST);
+	Audio::Timestamp startTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
+	*posVar += number; // Adjust to the end
+	Audio::Timestamp endTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
+	handle->setBounds(startTime, endTime);
+	_vm->_video->waitUntilMovieEnds(handle);
+
+	if (*posVar > 19) {
+		// The villager has died :(
+		_vm->_video->playMovieBlockingRiven(snackMLST);
+		redrawWharkNumberPuzzle(overlayPLST, number);
+		*posVar = 0;
+	}
+
+	// Enable the correct hotspots for the movement now
+	RivenHotspot *rotateLeft = _vm->getCard()->getHotspotByName("rotateLeft");
+	RivenHotspot *rotateRight = _vm->getCard()->getHotspotByName("rotateRight");
+	rotateLeft->enable(!rotateLeft->isEnabled());
+	rotateRight->enable(!rotateRight->isEnabled());
+
+	// Update the cursor
+	_vm->updateCurrentHotspot();
+}
+
+void JSpit::xjatboundary(uint16 argc, uint16 *argv) {
+	runDemoBoundaryDialog();
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/jspit.h b/engines/mohawk/riven_stacks/jspit.h
index 9e82a13..d9f5ead 100644
--- a/engines/mohawk/riven_stacks/jspit.h
+++ b/engines/mohawk/riven_stacks/jspit.h
@@ -28,10 +28,61 @@
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * Jungle Island
+ */
 class JSpit : public DomeSpit {
 public:
 	JSpit(MohawkEngine_Riven *vm);
 
+	// External commands - Rebel Tunnel Puzzle
+	void xreseticons(uint16 argc, uint16 *argv);
+	void xicon(uint16 argc, uint16 *argv);
+	void xcheckicons(uint16 argc, uint16 *argv);
+	void xtoggleicon(uint16 argc, uint16 *argv);
+	void xjtunnel103_pictfix(uint16 argc, uint16 *argv);
+	void xjtunnel104_pictfix(uint16 argc, uint16 *argv);
+	void xjtunnel105_pictfix(uint16 argc, uint16 *argv);
+	void xjtunnel106_pictfix(uint16 argc, uint16 *argv);
+
+	// External commands - Lower the gallows carriage
+	void xvga1300_carriage(uint16 argc, uint16 *argv);
+
+	// External commands - Dome
+	void xjdome25_resetsliders(uint16 argc, uint16 *argv);
+	void xjdome25_slidermd(uint16 argc, uint16 *argv);
+	void xjdome25_slidermw(uint16 argc, uint16 *argv);
+	void xjscpbtn(uint16 argc, uint16 *argv);
+	void xjisland3500_domecheck(uint16 argc, uint16 *argv);
+
+	// External commands - Whark Elevator
+	void xhandlecontroldown(uint16 argc, uint16 *argv);
+	void xhandlecontrolmid(uint16 argc, uint16 *argv);
+	void xhandlecontrolup(uint16 argc, uint16 *argv);
+
+	// External commands - Beetle
+	void xjplaybeetle_550(uint16 argc, uint16 *argv);
+	void xjplaybeetle_600(uint16 argc, uint16 *argv);
+	void xjplaybeetle_950(uint16 argc, uint16 *argv);
+	void xjplaybeetle_1050(uint16 argc, uint16 *argv);
+	void xjplaybeetle_1450(uint16 argc, uint16 *argv);
+
+	// External commands - Creatures in the Lagoon
+	void xjlagoon700_alert(uint16 argc, uint16 *argv);
+	void xjlagoon800_alert(uint16 argc, uint16 *argv);
+	void xjlagoon1500_alert(uint16 argc, uint16 *argv);
+
+	// External commands - Play the Whark Game
+	void xschool280_playwhark(uint16 argc, uint16 *argv);
+	void xjschool280_resetleft(uint16 argc, uint16 *argv); // DVD only
+	void xjschool280_resetright(uint16 argc, uint16 *argv); // DVD only
+
+	// External commands - Demo-specific
+	void xjatboundary(uint16 argc, uint16 *argv);
+
+private:
+	int jspitElevatorLoop();
+	void redrawWharkNumberPuzzle(uint16 overlay, uint16 number);
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/ospit.cpp b/engines/mohawk/riven_stacks/ospit.cpp
index f36be94..7607a93 100644
--- a/engines/mohawk/riven_stacks/ospit.cpp
+++ b/engines/mohawk/riven_stacks/ospit.cpp
@@ -22,7 +22,12 @@
 
 #include "mohawk/riven_stacks/ospit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/cursors.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
+
+#include "common/events.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,6 +35,254 @@ namespace RivenStacks {
 OSpit::OSpit(MohawkEngine_Riven *vm) :
 		RivenStack(vm, kStackOspit) {
 
+	REGISTER_COMMAND(OSpit, xorollcredittime);
+	REGISTER_COMMAND(OSpit, xbookclick);
+	REGISTER_COMMAND(OSpit, xooffice30_closebook);
+	REGISTER_COMMAND(OSpit, xobedroom5_closedrawer);
+	REGISTER_COMMAND(OSpit, xogehnopenbook);
+	REGISTER_COMMAND(OSpit, xogehnbookprevpage);
+	REGISTER_COMMAND(OSpit, xogehnbooknextpage);
+	REGISTER_COMMAND(OSpit, xgwatch);
+}
+
+void OSpit::xorollcredittime(uint16 argc, uint16 *argv) {
+	// WORKAROUND: The special change stuff only handles one destination and it would
+	// be messy to modify the way that currently works. If we use the trap book on Tay,
+	// we should be using the Tay end game sequences.
+	if (_vm->_vars["returnstackid"] == kStackRspit) {
+		_vm->changeToStack(kStackRspit);
+		_vm->changeToCard(2);
+		return;
+	}
+
+	// You used the trap book... why? What were you thinking?
+	uint32 gehnState = _vm->_vars["agehn"];
+
+	if (gehnState == 0)         // Gehn who?
+		runEndGame(1, 9500);
+	else if (gehnState == 4)    // You freed him? Are you kidding me?
+		runEndGame(2, 12000);
+	else                        // You already spoke with Gehn. What were you thinking?
+		runEndGame(3, 8000);
+}
+
+void OSpit::xbookclick(uint16 argc, uint16 *argv) {
+	// Hide the cursor
+	_vm->_cursor->setCursor(kRivenHideCursor);
+	_vm->_system->updateScreen();
+
+	// Let's hook onto our video
+	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
+
+	// Convert from the standard QuickTime base time to milliseconds
+	// The values are in terms of 1/600 of a second.
+	// Have I said how much I just *love* QuickTime? </sarcasm>
+	uint32 startTime = argv[1] * 1000 / 600;
+	uint32 endTime = argv[2] * 1000 / 600;
+
+	// Track down our hotspot
+	Common::String hotspotName = Common::String::format("touchBook%d", argv[3]);
+	RivenHotspot *hotspot = _vm->getCard()->getHotspotByName(hotspotName);
+	Common::Rect hotspotRect = hotspot->getRect();
+
+	debug(0, "xbookclick:");
+	debug(0, "\tVideo Code = %d", argv[0]);
+	debug(0, "\tStart Time = %dms", startTime);
+	debug(0, "\tEnd Time   = %dms", endTime);
+	debug(0, "\tHotspot    = %d -> %s", argv[3], hotspotName.c_str());
+
+	// Just let the video play while we wait until Gehn opens the trap book for us
+	while (video->getTime() < startTime && !_vm->shouldQuit()) {
+		if (_vm->_video->updateMovies())
+			_vm->_system->updateScreen();
+
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event))
+			;
+
+		_vm->_system->delayMillis(10);
+	}
+
+	// Break out if we're quitting
+	if (_vm->shouldQuit())
+		return;
+
+	// Update our hotspot stuff
+	if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
+		_vm->_cursor->setCursor(kRivenOpenHandCursor);
+	else
+		_vm->_cursor->setCursor(kRivenMainCursor);
+
+	_vm->_system->updateScreen();
+
+	// OK, Gehn has opened the trap book and has asked us to go in. Let's watch
+	// and see what the player will do...
+	while (video->getTime() < endTime && !_vm->shouldQuit()) {
+		bool updateScreen = _vm->_video->updateMovies();
+
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+				case Common::EVENT_MOUSEMOVE:
+					if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
+						_vm->_cursor->setCursor(kRivenOpenHandCursor);
+					else
+						_vm->_cursor->setCursor(kRivenMainCursor);
+					updateScreen = true;
+					break;
+				case Common::EVENT_LBUTTONUP:
+					if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos())) {
+						// OK, we've used the trap book! We go for ride lady!
+						_vm->_scriptMan->stopAllScripts();                  // Stop all running scripts (so we don't remain in the cage)
+						_vm->_video->stopVideos();                          // Stop all videos
+						_vm->_cursor->setCursor(kRivenHideCursor);          // Hide the cursor
+						_vm->getCard()->drawPicture(3);                  // Black out the screen
+						_vm->_sound->playSound(0);                          // Play the link sound
+						_vm->_video->activateMLST(_vm->getCard()->getMovie(7));    // Activate Gehn Link Video
+						_vm->_video->playMovieBlockingRiven(1);             // Play Gehn Link Video
+						_vm->_vars["agehn"] = 4;                            // Set Gehn to the trapped state
+						_vm->_vars["atrapbook"] = 1;                        // We've got the trap book again
+						_vm->_sound->playSound(0);                          // Play the link sound again
+						_vm->changeToCard(_vm->getStack()->getCardStackId(0x2885));    // Link out!
+						return;
+					}
+					break;
+				default:
+					break;
+			}
+		}
+
+		if (updateScreen && !_vm->shouldQuit())
+			_vm->_system->updateScreen();
+
+		_vm->_system->delayMillis(10);
+	}
+
+	// Break out if we're quitting
+	if (_vm->shouldQuit())
+		return;
+
+	// Hide the cursor again
+	_vm->_cursor->setCursor(kRivenHideCursor);
+	_vm->_system->updateScreen();
+
+	// If there was no click and this is the third time Gehn asks us to
+	// use the trap book, he will shoot the player. Dead on arrival.
+	// Run the credits from here.
+	if (_vm->_vars["agehn"] == 3) {
+		_vm->_scriptMan->stopAllScripts();
+		runCredits(argv[0], 5000);
+		return;
+	}
+
+	// There was no click, so just play the rest of the video.
+	_vm->_video->waitUntilMovieEnds(video);
+}
+
+void OSpit::xooffice30_closebook(uint16 argc, uint16 *argv) {
+	// Close the blank linking book if it's open
+	uint32 &book = _vm->_vars["odeskbook"];
+	if (book != 1)
+		return;
+
+	// Set the variable to be "closed"
+	book = 0;
+
+	// Play the movie
+	_vm->_video->playMovieBlockingRiven(1);
+
+	// Set the hotspots into their correct states
+	RivenHotspot *closeBook = _vm->getCard()->getHotspotByName("closeBook");
+	RivenHotspot *nullHotspot = _vm->getCard()->getHotspotByName("null");
+	RivenHotspot *openBook = _vm->getCard()->getHotspotByName("openBook");
+
+	closeBook->enable(false);
+	nullHotspot->enable(false);
+	openBook->enable(true);
+
+	// We now need to draw PLST 1 and refresh, but PLST 1 is
+	// drawn when refreshing anyway, so don't worry about that.
+	_vm->refreshCard();
+}
+
+void OSpit::xobedroom5_closedrawer(uint16 argc, uint16 *argv) {
+	// Close the drawer if open when clicking on the journal.
+	_vm->_video->playMovieBlockingRiven(2);
+	_vm->_vars["ostanddrawer"] = 0;
+}
+
+void OSpit::xogehnopenbook(uint16 argc, uint16 *argv) {
+	_vm->getCard()->drawPicture(_vm->_vars["ogehnpage"]);
+}
+
+void OSpit::xogehnbookprevpage(uint16 argc, uint16 *argv) {
+	// Get the page variable
+	uint32 &page = _vm->_vars["ogehnpage"];
+
+	// Decrement the page if it's not the first page
+	if (page == 1)
+		return;
+	page--;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(12);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(1);
+	_vm->getCard()->drawPicture(page);
+}
+
+void OSpit::xogehnbooknextpage(uint16 argc, uint16 *argv) {
+	// Get the page variable
+	uint32 &page = _vm->_vars["ogehnpage"];
+
+	// Increment the page if it's not the last page
+	if (page == 13)
+		return;
+	page++;
+
+	// Play the page turning sound
+	_vm->_sound->playSound(13);
+
+	// Now update the screen :)
+	_vm->_gfx->scheduleTransition(0);
+	_vm->getCard()->drawPicture(page);
+}
+
+void OSpit::xgwatch(uint16 argc, uint16 *argv) {
+	// Hide the cursor
+	_vm->_cursor->setCursor(kRivenHideCursor);
+	_vm->_system->updateScreen();
+
+	uint32 &prisonCombo = _vm->_vars["pcorrectorder"];
+	uint32 soundTime = _vm->_system->getMillis() - 500; // Start the first sound instantly
+	byte curSound = 0;
+
+	while (!_vm->shouldQuit()) {
+		// Play the next sound every half second
+		if (_vm->_system->getMillis() - soundTime >= 500) {
+			if (curSound == 5) // Break out after the last sound is done
+				break;
+
+			_vm->_sound->playSound(getComboDigit(prisonCombo, curSound) + 13);
+			curSound++;
+			soundTime = _vm->_system->getMillis();
+		}
+
+		// Poll events just to check for quitting
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event)) {}
+
+		// Cut down on CPU usage
+		_vm->_system->delayMillis(10);
+	}
+
+	// Now play the video for the watch
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(1));
+	_vm->_video->playMovieBlockingRiven(1);
+
+	// And, finally, refresh
+	_vm->refreshCard();
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/ospit.h b/engines/mohawk/riven_stacks/ospit.h
index 0792571..4181ce3 100644
--- a/engines/mohawk/riven_stacks/ospit.h
+++ b/engines/mohawk/riven_stacks/ospit.h
@@ -28,10 +28,31 @@
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * 233rd Age / Gehn's Office
+ */
 class OSpit : public RivenStack {
 public:
 	OSpit(MohawkEngine_Riven *vm);
 
+	// External commands - Death!
+	void xorollcredittime(uint16 argc, uint16 *argv);
+
+	// External commands - Trap Book Puzzle
+	void xbookclick(uint16 argc, uint16 *argv); // Four params -- movie_sref, start_time, end_time, u0
+
+	// External commands - Blank Linking Book
+	void xooffice30_closebook(uint16 argc, uint16 *argv);
+
+	// External commands - Gehn's Journal
+	void xobedroom5_closedrawer(uint16 argc, uint16 *argv);
+	void xogehnopenbook(uint16 argc, uint16 *argv);
+	void xogehnbookprevpage(uint16 argc, uint16 *argv);
+	void xogehnbooknextpage(uint16 argc, uint16 *argv);
+
+	// External commands - Elevator Combination
+	void xgwatch(uint16 argc, uint16 *argv);
+
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/pspit.cpp b/engines/mohawk/riven_stacks/pspit.cpp
index c6b1b97..ae10bd0 100644
--- a/engines/mohawk/riven_stacks/pspit.cpp
+++ b/engines/mohawk/riven_stacks/pspit.cpp
@@ -22,7 +22,8 @@
 
 #include "mohawk/riven_stacks/pspit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_sound.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,6 +31,57 @@ namespace RivenStacks {
 PSpit::PSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackPspit) {
 
+	REGISTER_COMMAND(PSpit, xpisland990_elevcombo);
+	REGISTER_COMMAND(PSpit, xpscpbtn);
+	REGISTER_COMMAND(PSpit, xpisland290_domecheck);
+	REGISTER_COMMAND(PSpit, xpisland25_opencard);
+	REGISTER_COMMAND(PSpit, xpisland25_resetsliders);
+	REGISTER_COMMAND(PSpit, xpisland25_slidermd);
+	REGISTER_COMMAND(PSpit, xpisland25_slidermw);
+}
+
+void PSpit::xpisland990_elevcombo(uint16 argc, uint16 *argv) {
+	// Play button sound based on argv[0]
+	_vm->_sound->playSound(argv[0] + 5);
+
+	// It is impossible to get here if Gehn is not trapped. However,
+	// the original also disallows brute forcing the ending if you have
+	// not yet trapped Gehn.
+	if (_vm->_vars["agehn"] != 4)
+		return;
+
+	uint32 &correctDigits = _vm->_vars["pelevcombo"];
+
+	// pelevcombo keeps count of how many buttons we have pressed in the correct order.
+	// When pelevcombo is 5, clicking the handle will show the video freeing Catherine.
+	if (correctDigits < 5 && argv[0] == getComboDigit(_vm->_vars["pcorrectorder"], correctDigits))
+		correctDigits++;
+	else
+		correctDigits = 0;
+}
+
+void PSpit::xpscpbtn(uint16 argc, uint16 *argv) {
+	runDomeButtonMovie();
+}
+
+void PSpit::xpisland290_domecheck(uint16 argc, uint16 *argv) {
+	runDomeCheck();
+}
+
+void PSpit::xpisland25_opencard(uint16 argc, uint16 *argv) {
+	checkDomeSliders();
+}
+
+void PSpit::xpisland25_resetsliders(uint16 argc, uint16 *argv) {
+	resetDomeSliders(10, 14);
+}
+
+void PSpit::xpisland25_slidermd(uint16 argc, uint16 *argv) {
+	dragDomeSlider(10, 14);
+}
+
+void PSpit::xpisland25_slidermw(uint16 argc, uint16 *argv) {
+	checkSliderCursorChange(14);
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/pspit.h b/engines/mohawk/riven_stacks/pspit.h
index 797eb29..ca09417 100644
--- a/engines/mohawk/riven_stacks/pspit.h
+++ b/engines/mohawk/riven_stacks/pspit.h
@@ -28,10 +28,24 @@
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * Prison Island
+ */
 class PSpit : public DomeSpit {
 public:
 	PSpit(MohawkEngine_Riven *vm);
 
+	// External commands - Prison Elevator
+	void xpisland990_elevcombo(uint16 argc, uint16 *argv);	// Param1: button
+
+	// External commands - Dome
+	void xpscpbtn(uint16 argc, uint16 *argv);
+	void xpisland290_domecheck(uint16 argc, uint16 *argv);
+	void xpisland25_opencard(uint16 argc, uint16 *argv);
+	void xpisland25_resetsliders(uint16 argc, uint16 *argv);
+	void xpisland25_slidermd(uint16 argc, uint16 *argv);
+	void xpisland25_slidermw(uint16 argc, uint16 *argv);
+
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/rspit.cpp b/engines/mohawk/riven_stacks/rspit.cpp
index c099c43..992319d 100644
--- a/engines/mohawk/riven_stacks/rspit.cpp
+++ b/engines/mohawk/riven_stacks/rspit.cpp
@@ -22,7 +22,9 @@
 
 #include "mohawk/riven_stacks/rspit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,7 +32,83 @@ namespace RivenStacks {
 RSpit::RSpit(MohawkEngine_Riven *vm) :
 		RivenStack(vm, kStackRspit) {
 
+	REGISTER_COMMAND(RSpit, xrshowinventory);
+	REGISTER_COMMAND(RSpit, xrhideinventory);
+	REGISTER_COMMAND(RSpit, xrcredittime);
+	REGISTER_COMMAND(RSpit, xrwindowsetup);
 }
 
+void RSpit::xrcredittime(uint16 argc, uint16 *argv) {
+	// Nice going, you used the trap book on Tay.
+
+	// The game chooses what ending based on agehn for us,
+	// so we just have to play the video and credits.
+	// For the record, when agehn == 4, Gehn will thank you for
+	// showing him the rebel age and then leave you to die.
+	// Otherwise, the rebels burn the book. Epic fail either way.
+	runEndGame(1, 1500);
+}
+
+void RSpit::xrshowinventory(uint16 argc, uint16 *argv) {
+	// Give the trap book and Catherine's journal to the player
+	_vm->_vars["atrapbook"] = 1;
+	_vm->_vars["acathbook"] = 1;
+	_vm->_gfx->showInventory();
+}
+
+void RSpit::xrhideinventory(uint16 argc, uint16 *argv) {
+	_vm->_gfx->hideInventory();
+}
+
+static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) {
+	// Randomize a video out in the middle of Tay
+	uint16 movie = vm->_rnd->getRandomNumberRng(2, 13);
+	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
+	VideoEntryPtr handle = vm->_video->playMovieRiven(movie);
+
+	// Ensure the next video starts after this one ends
+	uint32 timeUntilNextVideo = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(38, 58) * 1000;
+
+	// Save the time in case we leave the card and return
+	vm->_vars["rvillagetime"] = timeUntilNextVideo + vm->getTotalPlayTime();
+
+	// Reinstall this timer with the new time
+	vm->installTimer(&rebelPrisonWindowTimer, timeUntilNextVideo);
+}
+
+void RSpit::xrwindowsetup(uint16 argc, uint16 *argv) {
+	// Randomize what effect happens when you look out into the middle of Tay
+
+	uint32 villageTime = _vm->_vars["rvillagetime"];
+
+	// If we have time leftover from a previous run, set up the timer again
+	if (_vm->getTotalPlayTime() < villageTime) {
+		_vm->installTimer(&rebelPrisonWindowTimer, villageTime - _vm->getTotalPlayTime());
+		return;
+	}
+
+	uint32 timeUntilNextVideo;
+
+	// Randomize the time until the next video
+	if (_vm->_rnd->getRandomNumber(2) == 0 && _vm->_vars["rrichard"] == 0) {
+		// In this case, a rebel is placed on a bridge
+		// The video itself is handled by the scripts later on
+		_vm->_vars["rrebelview"] = 0;
+		timeUntilNextVideo = _vm->_rnd->getRandomNumberRng(38, 58) * 1000;
+	} else {
+		// Otherwise, just a random video from the timer
+		_vm->_vars["rrebelview"] = 1;
+		timeUntilNextVideo = _vm->_rnd->getRandomNumber(20) * 1000;
+	}
+
+	// We don't set rvillagetime here because the scripts later just reset it to 0
+	// Of course, because of this, you can't return to the window twice and expect
+	// the timer to reinstall itself...
+
+	// Install our timer and we're on our way
+	_vm->installTimer(&rebelPrisonWindowTimer, timeUntilNextVideo);
+}
+
+
 } // End of namespace RivenStacks
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/rspit.h b/engines/mohawk/riven_stacks/rspit.h
index 67384cb..c46537c 100644
--- a/engines/mohawk/riven_stacks/rspit.h
+++ b/engines/mohawk/riven_stacks/rspit.h
@@ -28,10 +28,19 @@
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * Rebel Age / Tay
+ */
 class RSpit : public RivenStack {
 public:
 	RSpit(MohawkEngine_Riven *vm);
 
+	// External commands
+	void xrcredittime(uint16 argc, uint16 *argv);
+	void xrshowinventory(uint16 argc, uint16 *argv);
+	void xrhideinventory(uint16 argc, uint16 *argv);
+	void xrwindowsetup(uint16 argc, uint16 *argv);
+
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/tspit.cpp b/engines/mohawk/riven_stacks/tspit.cpp
index aa4634a..763f535 100644
--- a/engines/mohawk/riven_stacks/tspit.cpp
+++ b/engines/mohawk/riven_stacks/tspit.cpp
@@ -22,7 +22,12 @@
 
 #include "mohawk/riven_stacks/tspit.h"
 
-#include "engines/mohawk/riven.h"
+#include "mohawk/cursors.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
+
+#include "common/events.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -30,6 +35,408 @@ namespace RivenStacks {
 TSpit::TSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackTspit) {
 
+	REGISTER_COMMAND(TSpit, xtexterior300_telescopedown);
+	REGISTER_COMMAND(TSpit, xtexterior300_telescopeup);
+	REGISTER_COMMAND(TSpit, xtisland390_covercombo);
+	REGISTER_COMMAND(TSpit, xtatrusgivesbooks);
+	REGISTER_COMMAND(TSpit, xtchotakesbook);
+	REGISTER_COMMAND(TSpit, xthideinventory);
+	REGISTER_COMMAND(TSpit, xt7500_checkmarbles);
+	REGISTER_COMMAND(TSpit, xt7600_setupmarbles);
+	REGISTER_COMMAND(TSpit, xt7800_setup);
+	REGISTER_COMMAND(TSpit, xdrawmarbles);
+	REGISTER_COMMAND(TSpit, xtakeit);
+	REGISTER_COMMAND(TSpit, xtscpbtn);
+	REGISTER_COMMAND(TSpit, xtisland4990_domecheck);
+	REGISTER_COMMAND(TSpit, xtisland5056_opencard);
+	REGISTER_COMMAND(TSpit, xtisland5056_resetsliders);
+	REGISTER_COMMAND(TSpit, xtisland5056_slidermd);
+	REGISTER_COMMAND(TSpit, xtisland5056_slidermw);
+	REGISTER_COMMAND(TSpit, xtatboundary);
+}
+
+void TSpit::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
+	// First, show the button movie
+	_vm->_video->playMovieBlockingRiven(3);
+
+	// Don't do anything else if the telescope power is off
+	if (_vm->_vars["ttelevalve"] == 0)
+		return;
+
+	uint32 &telescopePos = _vm->_vars["ttelescope"];
+	uint32 &telescopeCover = _vm->_vars["ttelecover"];
+
+	if (telescopePos == 1) {
+		// We're at the bottom, which means one of two things can happen...
+		if (telescopeCover == 1 && _vm->_vars["ttelepin"] == 1) {
+			// ...if the cover is open and the pin is up, the game is now over.
+			if (_vm->_vars["pcage"] == 2) {
+				// The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
+				// And now we fall back to Earth... all the way...
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
+				runEndGame(8, 5000);
+			} else if (_vm->_vars["agehn"] == 4) {
+				// The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
+				// Nice going! Catherine and the islanders are all dead now! Just go back to your home...
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
+				runEndGame(9, 5000);
+			} else if (_vm->_vars["atrapbook"] == 1) {
+				// The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
+				// And then you get shot by Cho. Nice going! Catherine and the islanders are dead
+				// and you have just set Gehn free from Riven, not to mention you're dead.
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
+				runEndGame(10, 5000);
+			} else {
+				// The impossible ending: You don't have Catherine's journal and yet you were somehow
+				// able to open the hatch on the telescope. The game provides an ending for those who
+				// cheat, load a saved game with the combo, or just guess the telescope combo. Atrus
+				// doesn't come and you just fall into the fissure.
+				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
+				runEndGame(11, 5000);
+			}
+		} else {
+			// ...the telescope can't move down anymore.
+			// Play the sound of not being able to move
+			_vm->_cursor->setCursor(kRivenHideCursor);
+			_vm->_system->updateScreen();
+			_vm->_sound->playSound(13);
+		}
+	} else {
+		// We're not at the bottom, and we can move down again
+
+		// Play a piece of the moving down movie
+		static const uint32 timeIntervals[] = { 4320, 3440, 2560, 1760, 880, 0 };
+		uint16 movieCode = telescopeCover ? 1 : 2;
+		VideoEntryPtr handle = _vm->_video->playMovieRiven(movieCode);
+		handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos], 600), Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600));
+		_vm->_sound->playSound(14); // Play the moving sound
+		_vm->_video->waitUntilMovieEnds(handle);
+
+		// Now move the telescope down a position and refresh
+		telescopePos--;
+		_vm->refreshCard();
+	}
+}
+
+void TSpit::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
+	// First, show the button movie
+	_vm->_video->playMovieBlockingRiven(3);
+
+	// Don't do anything else if the telescope power is off
+	if (_vm->_vars["ttelevalve"] == 0)
+		return;
+
+	uint32 &telescopePos = _vm->_vars["ttelescope"];
+
+	// Check if we can't move up anymore
+	if (telescopePos == 5) {
+		// Play the sound of not being able to move
+		_vm->_cursor->setCursor(kRivenHideCursor);
+		_vm->_system->updateScreen();
+		_vm->_sound->playSound(13);
+		return;
+	}
+
+	// Play a piece of the moving up movie
+	static const uint32 timeIntervals[] = { 0, 800, 1680, 2560, 3440, 4320 };
+	uint16 movieCode = _vm->_vars["ttelecover"] ? 4 : 5;
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(movieCode);
+	handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600), Audio::Timestamp(0, timeIntervals[telescopePos], 600));
+	_vm->_sound->playSound(14); // Play the moving sound
+	_vm->_video->waitUntilMovieEnds(handle);
+
+	// Now move the telescope up a position and refresh
+	telescopePos++;
+	_vm->refreshCard();
+}
+
+void TSpit::xtisland390_covercombo(uint16 argc, uint16 *argv) {
+	// Called when clicking the telescope cover buttons. argv[0] is the button number (1...5).
+	uint32 &correctDigits = _vm->_vars["tcovercombo"];
+
+	if (correctDigits < 5 && argv[0] == getComboDigit(_vm->_vars["tcorrectorder"], correctDigits))
+		correctDigits++;
+	else
+		correctDigits = 0;
+
+	// If we have hit the correct 5 buttons in a row, activate the hotspot to open up the
+	// telescope cover.
+	RivenHotspot *openCover = _vm->getCard()->getHotspotByName("openCover");
+	openCover->enable(correctDigits == 5);
+}
+
+// Atrus' Journal and Trap Book are added to inventory
+void TSpit::xtatrusgivesbooks(uint16 argc, uint16 *argv) {
+	// Give the player Atrus' Journal and the Trap book
+	_vm->_vars["aatrusbook"] = 1;
+	_vm->_vars["atrapbook"] = 1;
+}
+
+// Trap Book is removed from inventory
+void TSpit::xtchotakesbook(uint16 argc, uint16 *argv) {
+	// And now Cho takes the trap book. Sure, this isn't strictly
+	// necessary to add and them remove the trap book... but it
+	// seems better to do this ;)
+	_vm->_vars["atrapbook"] = 0;
+}
+
+void TSpit::xthideinventory(uint16 argc, uint16 *argv) {
+	_vm->_gfx->hideInventory();
+}
+
+// Marble Puzzle related constants
+static const uint32 kMarbleCount = 6;
+static const int kSmallMarbleWidth = 4;
+static const int kSmallMarbleHeight = 2;
+//static const int kLargeMarbleSize = 8;
+static const int kMarbleHotspotSize = 13;
+static const char *s_marbleNames[] = { "tred", "torange", "tyellow", "tgreen", "tblue", "tviolet" };
+
+// Marble Puzzle helper functions
+// The y portion takes the upper 16 bits, while the x portion takes the lower 16 bits
+static void setMarbleX(uint32 &var, byte x) {
+	var = (var & 0xff00) | (x + 1);
+}
+
+static void setMarbleY(uint32 &var, byte y) {
+	var = ((y + 1) << 16) | (var & 0xff);
+}
+
+static byte getMarbleX(uint32 var) {
+	return (var & 0xff) - 1;
+}
+
+static byte getMarbleY(uint32 var) { // Give that that Y you old hag! </bad Seinfeld reference>
+	return ((var >> 16) & 0xff) - 1;
+}
+
+static Common::Rect generateMarbleGridRect(uint16 x, uint16 y) {
+	// x/y in terms of 0!
+	static const int marbleGridOffsetX[] = { 134, 202, 270, 338, 406 };
+	static const int marbleGridOffsetY[] = {  24,  92, 159, 227, 295 };
+
+	uint16 offsetX = marbleGridOffsetX[x / 5] + (x % 5) * kMarbleHotspotSize;
+	uint16 offsetY = marbleGridOffsetY[y / 5] + (y % 5) * kMarbleHotspotSize;
+	return Common::Rect(offsetX, offsetY, offsetX + kMarbleHotspotSize, offsetY + kMarbleHotspotSize);
+}
+
+void TSpit::xt7500_checkmarbles(uint16 argc, uint16 *argv) {
+	// Set apower if the marbles are in their correct spot.
+
+	bool valid = true;
+	static const uint32 marbleFinalValues[] = { 1114121, 1441798, 0, 65552, 65558, 262146 };
+
+	for (uint16 i = 0; i < kMarbleCount; i++)
+		if (_vm->_vars[s_marbleNames[i]] != marbleFinalValues[i]) {
+			valid = false;
+			break;
+		}
+
+	// If we have the correct combo, activate the power and reset the marble positions
+	// Otherwise, make sure the power is off
+	if (valid) {
+		_vm->_vars["apower"] = 1;
+		for (uint16 i = 0; i < kMarbleCount; i++)
+			_vm->_vars[s_marbleNames[i]] = 0;
+	} else
+		_vm->_vars["apower"] = 0;
+}
+
+void TSpit::xt7600_setupmarbles(uint16 argc, uint16 *argv) {
+	// Draw the small marbles when we're a step away from the waffle
+
+	// Convert from marble X coordinate to screen X coordinate
+	static const uint16 xPosOffsets[] = {
+			246, 245, 244, 243, 243, 241, 240, 240, 239, 238, 237, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 226, 225
+	};
+
+	// Convert from marble Y coordinate to screen Y coordinate
+	static const uint16 yPosOffsets[] = {
+			261, 263, 265, 267, 268, 270, 272, 274, 276, 278, 281, 284, 285, 288, 290, 293, 295, 298, 300, 303, 306, 309, 311, 314, 316
+	};
+
+	// Handle spacing for y coordinates due to the angle
+	static const double yAdjusts[] = {
+			4.56, 4.68, 4.76, 4.84, 4.84, 4.96, 5.04, 5.04, 5.12, 5.2, 5.28, 5.28, 5.36, 5.44, 5.4, 5.6, 5.72, 5.8, 5.88, 5.96, 6.04, 6.12, 6.2, 6.2, 6.28
+	};
+
+	// Waffle state of 0 is up, 1 down
+	bool waffleDown = _vm->_vars["twaffle"] != 0;
+
+	// Note that each of the small marble images is exactly 4x2
+	// The original seems to scale the marble images from extras.mhk, but
+	// we're using the pre-scaled images in the stack.
+	uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred");
+
+	for (uint16 i = 0; i < kMarbleCount; i++) {
+		uint32 var = _vm->_vars[s_marbleNames[i]];
+
+		if (var == 0) {
+			// The marble is still in its initial place
+			// (Note that this is still drawn even if the waffle is down)
+			static const uint16 defaultX[] = { 375, 377, 379, 381, 383, 385 };
+			static const uint16 defaultY[] = { 253, 257, 261, 265, 268, 273 };
+			_vm->_gfx->copyImageToScreen(baseBitmapId + i, defaultX[i], defaultY[i], defaultX[i] + kSmallMarbleWidth, defaultY[i] + kSmallMarbleHeight);
+		} else if (waffleDown) {
+			// The marble is on the grid and the waffle is down
+			// (Nothing to draw here)
+		} else {
+			// The marble is on the grid and the waffle is up
+			int marbleX = (int)floor(getMarbleX(var) * yAdjusts[getMarbleY(var)] + xPosOffsets[getMarbleY(var)] + 0.5);
+			int marbleY = yPosOffsets[getMarbleY(var)];
+			_vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight);
+		}
+	}
+}
+
+void TSpit::setMarbleHotspots() {
+	// Set the hotspots
+	for (uint16 i = 0; i < kMarbleCount; i++) {
+		uint32 marblePos = _vm->_vars[s_marbleNames[i]];
+		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+
+		if (marblePos == 0) // In the receptacle
+			marbleHotspot->setRect(_marbleBaseHotspots[i]);
+		else                 // On the grid
+			marbleHotspot->setRect(generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)));
+	}
+}
+
+void TSpit::xt7800_setup(uint16 argc, uint16 *argv) {
+	// First, let's store the base receptacle hotspots for the marbles
+	if (_marbleBaseHotspots.empty())
+		for (uint16 i = 0; i < kMarbleCount; i++) {
+			RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+			_marbleBaseHotspots.push_back(marbleHotspot->getRect());
+		}
+
+	// Move the marble hotspots based on their position variables
+	setMarbleHotspots();
+	_vm->_vars["themarble"] = 0;
+}
+
+void TSpit::drawMarbles() {
+	for (uint32 i = 0; i < kMarbleCount; i++) {
+		// Don't draw the marble if we're holding it
+		if (_vm->_vars["themarble"] - 1 == i)
+			continue;
+
+		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+
+		Common::Rect rect = marbleHotspot->getRect();
+		// Trim the rect down a bit
+		rect.left += 3;
+		rect.top += 3;
+		rect.right -= 2;
+		rect.bottom -= 2;
+		_vm->_gfx->drawExtrasImage(i + 200, rect);
+	}
+}
+
+void TSpit::xdrawmarbles(uint16 argc, uint16 *argv) {
+	// Draw marbles in the closeup
+	drawMarbles();
+}
+
+void TSpit::xtakeit(uint16 argc, uint16 *argv) {
+	// Pick up and move a marble
+
+	// First, let's figure out what marble we're now holding
+	uint32 &marble = _vm->_vars["themarble"];
+	marble = 0;
+
+	for (uint32 i = 0; i < kMarbleCount; i++) {
+		RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+		if (marbleHotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+			marble = i + 1;
+			break;
+		}
+	}
+
+	// xtakeit() shouldn't be called if we're not on a marble hotspot
+	assert(marble != 0);
+
+	// Redraw the background
+	_vm->getCard()->drawPicture(1);
+
+	// Loop until the player lets go (or quits)
+	Common::Event event;
+	bool mouseDown = true;
+	while (mouseDown) {
+		while (_vm->_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_LBUTTONUP)
+				mouseDown = false;
+			else if (event.type == Common::EVENT_MOUSEMOVE)
+				_vm->_system->updateScreen();
+			else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
+				return;
+		}
+
+		_vm->_system->delayMillis(10); // Take it easy on the CPU
+	}
+
+	// Check if we landed in a valid location and no other marble has that location
+	uint32 &marblePos = _vm->_vars[s_marbleNames[marble - 1]];
+
+	bool foundMatch = false;
+	for (int y = 0; y < 25 && !foundMatch; y++) {
+		for (int x = 0; x < 25 && !foundMatch; x++) {
+			Common::Rect testHotspot = generateMarbleGridRect(x, y);
+
+			// Let's try to place the marble!
+			if (testHotspot.contains(_vm->_system->getEventManager()->getMousePos())) {
+				// Set this as the position
+				setMarbleX(marblePos, x);
+				setMarbleY(marblePos, y);
+
+				// Let's make sure no other marble is in this spot...
+				for (uint16 i = 0; i < kMarbleCount; i++)
+					if (i != marble - 1 && _vm->_vars[s_marbleNames[i]] == marblePos)
+						marblePos = 0;
+
+				// We have a match
+				foundMatch = true;
+			}
+		}
+	}
+
+	// If we still don't have a match, reset it to the original location
+	if (!foundMatch)
+		marblePos = 0;
+
+	// Check the new hotspots and refresh everything
+	marble = 0;
+	setMarbleHotspots();
+	_vm->updateCurrentHotspot();
+	_vm->_gfx->updateScreen();
+}
+
+void TSpit::xtscpbtn(uint16 argc, uint16 *argv) {
+	runDomeButtonMovie();
+}
+
+void TSpit::xtisland4990_domecheck(uint16 argc, uint16 *argv) {
+	runDomeCheck();
+}
+
+void TSpit::xtisland5056_opencard(uint16 argc, uint16 *argv) {
+	checkDomeSliders();
+}
+
+void TSpit::xtisland5056_resetsliders(uint16 argc, uint16 *argv) {
+	resetDomeSliders(37, 24);
+}
+
+void TSpit::xtisland5056_slidermd(uint16 argc, uint16 *argv) {
+	dragDomeSlider(37, 24);
+}
+
+void TSpit::xtisland5056_slidermw(uint16 argc, uint16 *argv) {
+	checkSliderCursorChange(24);
+}
+
+void TSpit::xtatboundary(uint16 argc, uint16 *argv) {
+	runDemoBoundaryDialog();
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/tspit.h b/engines/mohawk/riven_stacks/tspit.h
index 90c62a0..6b9e7ef 100644
--- a/engines/mohawk/riven_stacks/tspit.h
+++ b/engines/mohawk/riven_stacks/tspit.h
@@ -25,13 +25,55 @@
 
 #include "mohawk/riven_stacks/domespit.h"
 
+#include "common/rect.h"
+
 namespace Mohawk {
 namespace RivenStacks {
 
+/**
+ * Temple Island
+ */
 class TSpit : public DomeSpit {
 public:
 	TSpit(MohawkEngine_Riven *vm);
 
+	// External commands - Telescope
+	void xtexterior300_telescopedown(uint16 argc, uint16 *argv);
+	void xtexterior300_telescopeup(uint16 argc, uint16 *argv);
+
+	// External commands -  Telescope cover buttons. Button is the button number (1...5).
+	void xtisland390_covercombo(uint16 argc, uint16 *argv);	// Param1: button
+
+	// External commands - Atrus' Journal and Trap Book are added to inventory
+	void xtatrusgivesbooks(uint16 argc, uint16 *argv);
+
+	// External commands - Trap Book is removed from inventory
+	void xtchotakesbook(uint16 argc, uint16 *argv);
+	void xthideinventory(uint16 argc, uint16 *argv);
+
+	// External commands - Marble Puzzle
+	void xt7500_checkmarbles(uint16 argc, uint16 *argv);
+	void xt7600_setupmarbles(uint16 argc, uint16 *argv);
+	void xt7800_setup(uint16 argc, uint16 *argv);
+	void xdrawmarbles(uint16 argc, uint16 *argv);
+	void xtakeit(uint16 argc, uint16 *argv);
+
+	// External commands - Dome
+	void xtscpbtn(uint16 argc, uint16 *argv);
+	void xtisland4990_domecheck(uint16 argc, uint16 *argv);
+	void xtisland5056_opencard(uint16 argc, uint16 *argv);
+	void xtisland5056_resetsliders(uint16 argc, uint16 *argv);
+	void xtisland5056_slidermd(uint16 argc, uint16 *argv);
+	void xtisland5056_slidermw(uint16 argc, uint16 *argv);
+
+	// External commands - Demo-specific
+	void xtatboundary(uint16 argc, uint16 *argv);
+
+private:
+	void drawMarbles();
+	void setMarbleHotspots();
+
+	Common::Array<Common::Rect> _marbleBaseHotspots;
 };
 
 } // End of namespace RivenStacks


Commit: 6d4260719c79d457c1162feae2a91e7bbacae445
    https://github.com/scummvm/scummvm/commit/6d4260719c79d457c1162feae2a91e7bbacae445
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add a convenience method for creating a script with a single command

Changed paths:
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index c26d4bd..67a56af 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -153,6 +153,14 @@ RivenScriptPtr RivenScriptManager::createScriptFromData(uint16 commandCount, ...
 	return readScript(&readStream);
 }
 
+RivenScriptPtr RivenScriptManager::createScriptWithCommand(RivenCommand *command) {
+	assert(command);
+
+	RivenScriptPtr script = RivenScriptPtr(new RivenScript());
+	script->addCommand(command);
+	return script;
+}
+
 RivenScript::RivenScript() {
 	_continueRunning = true;
 }
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index cfe0f3a..cac7955 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -130,6 +130,13 @@ public:
 	/** Create a script from the caller provided arguments containing raw data */
 	RivenScriptPtr createScriptFromData(uint16 commandCount, ...);
 
+	/**
+	 * Create a script with a single user provided command
+	 *
+	 * The script takes ownership of the command.
+	 */
+	RivenScriptPtr createScriptWithCommand(RivenCommand *command);
+
 	/** Read a list of typed scripts from a stream */
 	RivenScriptList readScripts(Common::ReadStream *stream);
 


Commit: e7146c9bf7d5bdc62b977dca47c445e7f828392e
    https://github.com/scummvm/scummvm/commit/e7146c9bf7d5bdc62b977dca47c445e7f828392e
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the changeToStack command to a dedicated class

Changed paths:
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 67a56af..6c744fc 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -66,10 +66,13 @@ RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream) {
 RivenCommand *RivenScriptManager::readCommand(Common::ReadStream *stream) {
 	uint16 type = stream->readUint16BE();
 
-	if (type == 8) {
-		return RivenSwitchCommand::createFromStream(_vm, type, stream);
-	} else {
-		return RivenSimpleCommand::createFromStream(_vm, type, stream);
+	switch (type) {
+		case 8:
+			return RivenSwitchCommand::createFromStream(_vm, type, stream);
+		case 27:
+			return RivenStackChangeCommand::createFromStream(_vm, type, stream);
+		default:
+			return RivenSimpleCommand::createFromStream(_vm, type, stream);
 	}
 }
 
@@ -267,7 +270,7 @@ void RivenSimpleCommand::setupOpcodes() {
 		OPCODE(empty),						// Complex animation (not used)
 		OPCODE(setVariable),
 		// 0x08 (8 decimal)
-		OPCODE(mohawkSwitch),
+		OPCODE(empty),                      // Not a SimpleCommand
 		OPCODE(enableHotspot),
 		OPCODE(disableHotspot),
 		OPCODE(empty),						// Empty
@@ -290,7 +293,7 @@ void RivenSimpleCommand::setupOpcodes() {
 		OPCODE(incrementVariable),
 		OPCODE(empty),						// Empty
 		OPCODE(empty),						// Empty
-		OPCODE(changeStack),
+		OPCODE(empty),                      // Not a SimpleCommand
 		// 0x1C (28 decimal)
 		OPCODE(disableMovie),
 		OPCODE(disableAllMovies),
@@ -385,11 +388,6 @@ void RivenSimpleCommand::setVariable(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->getStackVar(argv[0]) = argv[1];
 }
 
-// Command 8: conditional branch
-void RivenSimpleCommand::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) {
-	// dummy function, this opcode does logic checking in processCommands()
-}
-
 // Command 9: enable hotspot (blst_id)
 void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) {
 	RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(argv[0]);
@@ -488,26 +486,6 @@ void RivenSimpleCommand::incrementVariable(uint16 op, uint16 argc, uint16 *argv)
 	_vm->getStackVar(argv[0]) += argv[1];
 }
 
-// Command 27: go to stack (stack name, code high, code low)
-void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) {
-	Common::String stackName = _vm->getStack()->getName(kStackNames, argv[0]);
-	int8 index = -1;
-
-	for (byte i = 0; i < 8; i++)
-		if (_vm->getStackName(i).equalsIgnoreCase(stackName)) {
-			index = i;
-			break;
-		}
-
-	if (index == -1)
-		error ("'%s' is not a stack name!", stackName.c_str());
-
-	_vm->changeToStack(index);
-	uint32 rmapCode = (argv[1] << 16) + argv[2];
-	uint16 cardID = _vm->getStack()->getCardStackId(rmapCode);
-	_vm->changeToCard(cardID);
-}
-
 // Command 28: disable a movie
 void RivenSimpleCommand::disableMovie(uint16 op, uint16 argc, uint16 *argv) {
 	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
@@ -767,4 +745,51 @@ void RivenSwitchCommand::execute() {
 	}
 }
 
+RivenStackChangeCommand::RivenStackChangeCommand(MohawkEngine_Riven *vm, uint16 stackId, uint32 globalCardId, bool byStackId) :
+		RivenCommand(vm),
+		_stackId(stackId),
+		_cardId(globalCardId),
+		_byStackId(byStackId) {
+
+}
+
+RivenStackChangeCommand::~RivenStackChangeCommand() {
+
+}
+
+RivenStackChangeCommand *RivenStackChangeCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) {
+	/* argumentsSize = */ stream->readUint16BE();
+	uint16 stackId = stream->readUint16BE();
+	uint32 globalCardId = stream->readUint32BE();
+
+	return new RivenStackChangeCommand(vm, stackId, globalCardId, false);
+}
+
+void RivenStackChangeCommand::execute() {
+	int16 stackID = -1;
+	if (_byStackId) {
+		stackID = _stackId;
+	} else {
+		Common::String stackName = _vm->getStack()->getName(kStackNames, _stackId);
+
+		for (byte i = kStackFirst; i < kStackLast; i++)
+			if (_vm->getStackName(i).equalsIgnoreCase(stackName)) {
+				stackID = i;
+				break;
+			}
+
+		if (stackID == -1)
+			error ("'%s' is not a stack name!", stackName.c_str());
+	}
+
+	_vm->changeToStack(stackID);
+	uint16 cardID = _vm->getStack()->getCardStackId(_cardId);
+	_vm->changeToCard(cardID);
+}
+
+void RivenStackChangeCommand::dump(byte tabs) {
+	printTabs(tabs);
+	debugN("changeStack(%d, %d);\n", _stackId, _cardId);
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index cac7955..13c415b 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -222,7 +222,6 @@ private:
 	DECLARE_OPCODE(playScriptSLST);
 	DECLARE_OPCODE(playSound);
 	DECLARE_OPCODE(setVariable);
-	DECLARE_OPCODE(mohawkSwitch);
 	DECLARE_OPCODE(enableHotspot);
 	DECLARE_OPCODE(disableHotspot);
 	DECLARE_OPCODE(stopSound);
@@ -234,7 +233,6 @@ private:
 	DECLARE_OPCODE(beginScreenUpdate);
 	DECLARE_OPCODE(applyScreenUpdate);
 	DECLARE_OPCODE(incrementVariable);
-	DECLARE_OPCODE(changeStack);
 	DECLARE_OPCODE(disableMovie);
 	DECLARE_OPCODE(disableAllMovies);
 	DECLARE_OPCODE(enableMovie);
@@ -287,6 +285,30 @@ private:
 	Common::Array<Branch> _branches;
 };
 
+/**
+ * A command to go to a different stack
+ *
+ * Changes the active stack and sets the initial card.
+ * The stack can be specified by global id or name id in the initial stack.
+ * The destination card must be specified by global id.
+ */
+class RivenStackChangeCommand : public RivenCommand {
+public:
+	RivenStackChangeCommand(MohawkEngine_Riven *vm, uint16 stackId, uint32 globalCardId, bool byStackId);
+
+	static RivenStackChangeCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);
+	virtual ~RivenStackChangeCommand();
+
+	// RivenCommand API
+	virtual void dump(byte tabs) override;
+	virtual void execute() override;
+
+private:
+	uint16 _stackId;
+	uint32 _cardId;
+	bool _byStackId; // Otherwise by stack name id
+};
+
 } // End of namespace Mohawk
 
 #undef DECLARE_OPCODE


Commit: c04edb8f54f6e44079b389263428719c0b3c562c
    https://github.com/scummvm/scummvm/commit/c04edb8f54f6e44079b389263428719c0b3c562c
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Change the back from book commands to use scripts

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_stacks/aspit.cpp
    engines/mohawk/riven_stacks/aspit.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 03af714..7739dd5 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -473,7 +473,7 @@ void MohawkEngine_Riven::checkInventoryClick() {
 
 	// Set the return stack/card id's.
 	_vars["returnstackid"] = _stack->getId();
-	_vars["returncardid"] = _card->getId();
+	_vars["returncardid"] = _stack->getCardGlobalId(_card->getId());
 
 	// See RivenGraphics::showInventory() for an explanation
 	// of the variables' meanings.
diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp
index f4ab7d1..4c88b19 100644
--- a/engines/mohawk/riven_stacks/aspit.cpp
+++ b/engines/mohawk/riven_stacks/aspit.cpp
@@ -95,9 +95,20 @@ void ASpit::xaatrusopenbook(uint16 argc, uint16 *argv) {
 }
 
 void ASpit::xaatrusbookback(uint16 argc, uint16 *argv) {
+	inventoryBackFromItemScript();
+}
+
+void ASpit::inventoryBackFromItemScript() const {
+	RivenScriptPtr stopSoundScript = _vm->_scriptMan->createScriptFromData(1, 12, 1, 1);
+	_vm->_scriptMan->runScript(stopSoundScript, false);
+
+	uint16 backStackId = _vm->_vars["returnstackid"];
+	uint32 backCardId = _vm->_vars["returncardid"];
+
 	// Return to where we were before entering the book
-	_vm->changeToStack(_vm->_vars["returnstackid"]);
-	_vm->changeToCard(_vm->_vars["returncardid"]);
+	RivenCommand *back = new RivenStackChangeCommand(_vm, backStackId, backCardId, true);
+	RivenScriptPtr backScript = _vm->_scriptMan->createScriptWithCommand(back);
+	_vm->_scriptMan->runScript(backScript, false);
 }
 
 void ASpit::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
@@ -187,9 +198,7 @@ void ASpit::xacathopenbook(uint16 argc, uint16 *argv) {
 }
 
 void ASpit::xacathbookback(uint16 argc, uint16 *argv) {
-	// Return to where we were before entering the book
-	_vm->changeToStack(_vm->_vars["returnstackid"]);
-	_vm->changeToCard(_vm->_vars["returncardid"]);
+	inventoryBackFromItemScript();
 }
 
 void ASpit::xacathbookprevpage(uint16 argc, uint16 *argv) {
@@ -229,8 +238,7 @@ void ASpit::xacathbooknextpage(uint16 argc, uint16 *argv) {
 void ASpit::xtrapbookback(uint16 argc, uint16 *argv) {
 	// Return to where we were before entering the book
 	_vm->_vars["atrap"] = 0;
-	_vm->changeToStack(_vm->_vars["returnstackid"]);
-	_vm->changeToCard(_vm->_vars["returncardid"]);
+	inventoryBackFromItemScript();
 }
 
 void ASpit::xatrapbookclose(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_stacks/aspit.h b/engines/mohawk/riven_stacks/aspit.h
index 60400dd..3aa4002 100644
--- a/engines/mohawk/riven_stacks/aspit.h
+++ b/engines/mohawk/riven_stacks/aspit.h
@@ -67,6 +67,8 @@ public:
 	void xaenablemenuintro(uint16 argc, uint16 *argv);
 	void xademoquit(uint16 argc, uint16 *argv);
 	void xaexittomain(uint16 argc, uint16 *argv);
+
+	void inventoryBackFromItemScript() const;
 };
 
 } // End of namespace RivenStacks


Commit: efcf38f95f14272efd8ace91747a45bd53c74b57
    https://github.com/scummvm/scummvm/commit/efcf38f95f14272efd8ace91747a45bd53c74b57
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Factor out stack name-id mapping

Changed paths:
    engines/mohawk/console.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h


diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 64d3a00..37c5661 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -474,7 +474,7 @@ bool RivenConsole::Cmd_StopSound(int argc, const char **argv) {
 }
 
 bool RivenConsole::Cmd_CurStack(int argc, const char **argv) {
-	debugPrintf("Current Stack: %s\n", _vm->getStackName(_vm->getStack()->getId()).c_str());
+	debugPrintf("Current Stack: %s\n", RivenStacks::getName(_vm->getStack()->getId()));
 
 	return true;
 }
@@ -485,28 +485,20 @@ bool RivenConsole::Cmd_ChangeStack(int argc, const char **argv) {
 		debugPrintf("Stacks:\n=======\n");
 
 		for (uint i = kStackFirst; i <= kStackLast; i++)
-			debugPrintf(" %s\n", _vm->getStackName(i).c_str());
+			debugPrintf(" %s\n", RivenStacks::getName(i));
 
 		debugPrintf("\n");
 
 		return true;
 	}
 
-	uint stack = kStackUnknown;
-
-	for (uint i = kStackFirst; i <= kStackLast; i++) {
-		if (!scumm_stricmp(argv[1], _vm->getStackName(i).c_str())) {
-			stack = i;
-			break;
-		}
-	}
-
-	if (stack == kStackUnknown) {
+	uint stackId = RivenStacks::getId(argv[1]);
+	if (stackId == kStackUnknown) {
 		debugPrintf("\'%s\' is not a stack name!\n", argv[1]);
 		return true;
 	}
 
-	_vm->changeToStack(stack);
+	_vm->changeToStack(stackId);
 	_vm->changeToCard((uint16)atoi(argv[2]));
 
 	return false;
@@ -577,15 +569,8 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
 	}
 
 	uint16 oldStack = _vm->getStack()->getId();
-	uint newStack = kStackUnknown;
-
-	for (uint i = kStackFirst; i <= kStackLast; i++) {
-		if (!scumm_stricmp(argv[1], _vm->getStackName(i).c_str())) {
-			newStack = i;
-			break;
-		}
-	}
 
+	uint newStack = RivenStacks::getId(argv[1]);
 	if (newStack == kStackUnknown) {
 		debugPrintf("\'%s\' is not a stack name!\n", argv[1]);
 		return true;
@@ -659,7 +644,7 @@ bool RivenConsole::Cmd_ListZipCards(int argc, const char **argv) {
 
 bool RivenConsole::Cmd_GetRMAP(int argc, const char **argv) {
 	uint32 rmapCode = _vm->getStack()->getCurrentCardGlobalId();
-	debugPrintf("RMAP for %s %d = %08x\n", _vm->getStackName(_vm->getStack()->getId()).c_str(), _vm->getCard()->getId(), rmapCode);
+	debugPrintf("RMAP for %s %d = %08x\n", RivenStacks::getName(_vm->getStack()->getId()), _vm->getCard()->getId(), rmapCode);
 	return true;
 }
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 7739dd5..1b69e35 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -324,7 +324,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 	_mhk.clear();
 
 	// Get the prefix character for the destination stack
-	char prefix = getStackName(n)[0];
+	char prefix = RivenStacks::getName(n)[0];
 
 	// Load any file that fits the patterns
 	for (int i = 0; i < ARRAYSIZE(endings); i++) {
@@ -339,7 +339,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 
 	// Make sure we have loaded files
 	if (_mhk.empty())
-		error("Could not load stack %s", getStackName(n).c_str());
+		error("Could not load stack %s", RivenStacks::getName(n));
 
 	// Stop any currently playing sounds
 	_sound->stopAllSLST();
@@ -554,25 +554,6 @@ Common::Error MohawkEngine_Riven::saveGameState(int slot, const Common::String &
 	return _saveLoad->saveGame(slot, desc);
 }
 
-Common::String MohawkEngine_Riven::getStackName(uint16 stack) const {
-	static const char *rivenStackNames[] = {
-		"<unknown>",
-		"ospit",
-		"pspit",
-		"rspit",
-		"tspit",
-		"bspit",
-		"gspit",
-		"jspit",
-		"aspit"
-	};
-
-	// Sanity check.
-	assert(stack < ARRAYSIZE(rivenStackNames));
-
-	return rivenStackNames[stack];
-}
-
 void MohawkEngine_Riven::installTimer(TimerProc proc, uint32 time) {
 	removeTimer();
 	_timerProc = proc;
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index b2e9188..131d285 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -143,7 +143,6 @@ public:
 	void changeToCard(uint16 dest);
 	void changeToStack(uint16);
 	void refreshCard();
-	Common::String getStackName(uint16 stack) const;
 	RivenCard *getCard() const { return _card; }
 	RivenStack *getStack() const { return _stack; }
 
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 6c744fc..1445731 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -766,20 +766,16 @@ RivenStackChangeCommand *RivenStackChangeCommand::createFromStream(MohawkEngine_
 }
 
 void RivenStackChangeCommand::execute() {
-	int16 stackID = -1;
+	uint16 stackID;
 	if (_byStackId) {
 		stackID = _stackId;
 	} else {
 		Common::String stackName = _vm->getStack()->getName(kStackNames, _stackId);
 
-		for (byte i = kStackFirst; i < kStackLast; i++)
-			if (_vm->getStackName(i).equalsIgnoreCase(stackName)) {
-				stackID = i;
-				break;
-			}
-
-		if (stackID == -1)
+		stackID = RivenStacks::getId(stackName.c_str());
+		if (stackID == kStackUnknown) {
 			error ("'%s' is not a stack name!", stackName.c_str());
+		}
 	}
 
 	_vm->changeToStack(stackID);
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index 225e699..ab79b78 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -136,7 +136,7 @@ uint32 RivenStack::getCardGlobalId(uint16 cardId) const {
 void RivenStack::dump() const {
 	debug("= Stack =");
 	debug("id: %d", _id);
-	debug("name: %s", _vm->getStackName(_id).c_str());
+	debug("name: %s", RivenStacks::getName(_id));
 	debugN("\n");
 
 	for (uint i = 0; i < _cardIdMap.size(); i++) {
@@ -291,4 +291,35 @@ int16 RivenNameList::getNameId(const Common::String &name) const {
 	return -1;
 }
 
+namespace RivenStacks {
+static const char *names[] = {
+		"<unknown>",
+		"ospit",
+		"pspit",
+		"rspit",
+		"tspit",
+		"bspit",
+		"gspit",
+		"jspit",
+		"aspit"
+};
+
+const char *getName(uint16 stackId) {
+	// Sanity check.
+	assert(stackId < ARRAYSIZE(names));
+
+	return names[stackId];
+}
+
+uint16 getId(const char *stackName) {
+	for (byte i = 0; i < ARRAYSIZE(names); i++) {
+		if (scumm_stricmp(stackName, names[i]) == 0) {
+			return i;
+		}
+	}
+
+	return kStackUnknown;
+}
+} // End of namespace RivenStacks
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 0911fbb..d8098f7 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -151,6 +151,14 @@ private:
 	CommandsMap _commands;
 };
 
+namespace RivenStacks {
+	/** Get a stack name from an id */
+	const char *getName(uint16 stackId);
+
+	/** Get a stack id from a name */
+	uint16 getId(const char *stackName);
+}
+
 } // End of namespace Mohawk
 
 #endif


Commit: 22926a1835079f31fa778f2f2eb4e645c451457a
    https://github.com/scummvm/scummvm/commit/22926a1835079f31fa778f2f2eb4e645c451457a
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move the timer callbacks to the stacks

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h
    engines/mohawk/riven_stacks/bspit.cpp
    engines/mohawk/riven_stacks/bspit.h
    engines/mohawk/riven_stacks/gspit.cpp
    engines/mohawk/riven_stacks/gspit.h
    engines/mohawk/riven_stacks/jspit.cpp
    engines/mohawk/riven_stacks/jspit.h
    engines/mohawk/riven_stacks/pspit.cpp
    engines/mohawk/riven_stacks/pspit.h
    engines/mohawk/riven_stacks/rspit.cpp
    engines/mohawk/riven_stacks/rspit.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 1b69e35..7951d0f 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -434,7 +434,7 @@ void MohawkEngine_Riven::refreshCard() {
 	updateCurrentHotspot();
 
 	// Finally, install any hardcoded timer
-	installCardTimer();
+	_stack->installCardTimer();
 }
 
 void MohawkEngine_Riven::updateCurrentHotspot() {
@@ -554,9 +554,9 @@ Common::Error MohawkEngine_Riven::saveGameState(int slot, const Common::String &
 	return _saveLoad->saveGame(slot, desc);
 }
 
-void MohawkEngine_Riven::installTimer(TimerProc proc, uint32 time) {
+void MohawkEngine_Riven::installTimer(TimerProc *proc, uint32 time) {
 	removeTimer();
-	_timerProc = proc;
+	_timerProc = Common::SharedPtr<TimerProc>(proc);
 	_timerTime = time + getTotalPlayTime();
 }
 
@@ -566,208 +566,15 @@ void MohawkEngine_Riven::checkTimer() {
 
 	// NOTE: If the specified timer function is called, it is its job to remove the timer!
 	if (getTotalPlayTime() >= _timerTime) {
-		TimerProc proc = _timerProc;
-		proc(this);
+		(*_timerProc)();
 	}
 }
 
 void MohawkEngine_Riven::removeTimer() {
-	_timerProc = 0;
+	_timerProc.reset();
 	_timerTime = 0;
 }
 
-static void catherineIdleTimer(MohawkEngine_Riven *vm) {
-	uint32 &cathCheck = vm->_vars["pcathcheck"];
-	uint32 &cathState = vm->_vars["acathstate"];
-	uint16 movie;
-
-	// Choose a random movie based on where Catherine is
-	if (cathCheck == 0) {
-		static const int movieList[] = { 5, 6, 7, 8 };
-		cathCheck = 1;
-		movie = movieList[vm->_rnd->getRandomNumber(3)];
-	} else if (cathState == 1) {
-		static const int movieList[] = { 11, 14 };
-		movie = movieList[vm->_rnd->getRandomBit()];
-	} else {
-		static const int movieList[] = { 9, 10, 12, 13 };
-		movie = movieList[vm->_rnd->getRandomNumber(3)];
-	}
-
-	// Update her state if she moves from left/right or right/left, resp.
-	if (movie == 5 || movie == 7 || movie == 11 || movie == 14)
-		cathState = 2;
-	else
-		cathState = 1;
-
-	// Play the movie, blocking
-	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
-	vm->_cursor->hideCursor();
-	vm->_video->playMovieBlockingRiven(movie);
-	vm->_cursor->showCursor();
-	vm->_system->updateScreen();
-
-	// Install the next timer for the next video
-	uint32 timeUntilNextMovie = vm->_rnd->getRandomNumber(120) * 1000;
-
-	vm->_vars["pcathtime"] = timeUntilNextMovie + vm->getTotalPlayTime();
-
-	vm->installTimer(&catherineIdleTimer, timeUntilNextMovie);
-}
-
-static void sunnersTopStairsTimer(MohawkEngine_Riven *vm) {
-	// If the sunners are gone, we have no video to play
-	if (vm->_vars["jsunners"] != 0) {
-		vm->removeTimer();
-		return;
-	}
-
-	// Play a random sunners video if the script one is not playing already
-	// and then set a new timer for when the new video should be played
-
-	VideoEntryPtr oldVideo = vm->_video->findVideoRiven(1);
-	uint32 timerTime = 500;
-
-	if (!oldVideo || oldVideo->endOfVideo()) {
-		uint32 &sunnerTime = vm->_vars["jsunnertime"];
-
-		if (sunnerTime == 0) {
-			timerTime = vm->_rnd->getRandomNumberRng(2, 15) * 1000;
-		} else if (sunnerTime < vm->getTotalPlayTime()) {
-			VideoEntryPtr video = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(1, 3));
-
-			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(2, 15) * 1000;
-		}
-
-		sunnerTime = timerTime + vm->getTotalPlayTime();
-	}
-
-	vm->installTimer(&sunnersTopStairsTimer, timerTime);
-}
-
-static void sunnersMidStairsTimer(MohawkEngine_Riven *vm) {
-	// If the sunners are gone, we have no video to play
-	if (vm->_vars["jsunners"] != 0) {
-		vm->removeTimer();
-		return;
-	}
-
-	// Play a random sunners video if the script one is not playing already
-	// and then set a new timer for when the new video should be played
-
-	VideoEntryPtr oldVideo = vm->_video->findVideoRiven(1);
-	uint32 timerTime = 500;
-
-	if (!oldVideo || oldVideo->endOfVideo()) {
-		uint32 &sunnerTime = vm->_vars["jsunnertime"];
-
-		if (sunnerTime == 0) {
-			timerTime = vm->_rnd->getRandomNumberRng(1, 10) * 1000;
-		} else if (sunnerTime < vm->getTotalPlayTime()) {
-			// Randomize the video
-			int randValue = vm->_rnd->getRandomNumber(5);
-			uint16 movie = 4;
-			if (randValue == 4)
-				movie = 2;
-			else if (randValue == 5)
-				movie = 3;
-
-			VideoEntryPtr video = vm->_video->playMovieRiven(movie);
-
-			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 10) * 1000;
-		}
-
-		sunnerTime = timerTime + vm->getTotalPlayTime();
-	}
-
-	vm->installTimer(&sunnersMidStairsTimer, timerTime);
-}
-
-static void sunnersLowerStairsTimer(MohawkEngine_Riven *vm) {
-	// If the sunners are gone, we have no video to play
-	if (vm->_vars["jsunners"] != 0) {
-		vm->removeTimer();
-		return;
-	}
-
-	// Play a random sunners video if the script one is not playing already
-	// and then set a new timer for when the new video should be played
-
-	VideoEntryPtr oldVideo = vm->_video->findVideoRiven(1);
-	uint32 timerTime = 500;
-
-	if (!oldVideo || oldVideo->endOfVideo()) {
-		uint32 &sunnerTime = vm->_vars["jsunnertime"];
-
-		if (sunnerTime == 0) {
-			timerTime = vm->_rnd->getRandomNumberRng(1, 30) * 1000;
-		} else if (sunnerTime < vm->getTotalPlayTime()) {
-			VideoEntryPtr video = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(3, 5));
-
-			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
-		}
-
-		sunnerTime = timerTime + vm->getTotalPlayTime();
-	}
-
-	vm->installTimer(&sunnersLowerStairsTimer, timerTime);
-}
-
-static void sunnersBeachTimer(MohawkEngine_Riven *vm) {
-	// If the sunners are gone, we have no video to play
-	if (vm->_vars["jsunners"] != 0) {
-		vm->removeTimer();
-		return;
-	}
-
-	// Play a random sunners video if the script one is not playing already
-	// and then set a new timer for when the new video should be played
-
-	VideoEntryPtr oldvideo = vm->_video->findVideoRiven(3);
-	uint32 timerTime = 500;
-
-	if (!oldvideo || oldvideo->endOfVideo()) {
-		uint32 &sunnerTime = vm->_vars["jsunnertime"];
-
-		if (sunnerTime == 0) {
-			timerTime = vm->_rnd->getRandomNumberRng(1, 30) * 1000;
-		} else if (sunnerTime < vm->getTotalPlayTime()) {
-			// Unlike the other cards' scripts which automatically
-			// activate the MLST, we have to set it manually here.
-			uint16 mlstID = vm->_rnd->getRandomNumberRng(3, 8);
-			vm->_video->activateMLST(vm->getCard()->getMovie(mlstID));
-			VideoEntryPtr video = vm->_video->playMovieRiven(mlstID);
-
-			timerTime = video->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000;
-		}
-
-		sunnerTime = timerTime + vm->getTotalPlayTime();
-	}
-
-	vm->installTimer(&sunnersBeachTimer, timerTime);
-}
-
-void MohawkEngine_Riven::installCardTimer() {
-	switch (_stack->getCurrentCardGlobalId()) {
-	case 0x3a85: // Top of elevator on prison island
-		// Handle Catherine hardcoded videos
-		installTimer(&catherineIdleTimer, _rnd->getRandomNumberRng(1, 33) * 1000);
-		break;
-	case 0x77d6: // Sunners, top of stairs
-		installTimer(&sunnersTopStairsTimer, 500);
-		break;
-	case 0x79bd: // Sunners, middle of stairs
-		installTimer(&sunnersMidStairsTimer, 500);
-		break;
-	case 0x7beb: // Sunners, bottom of stairs
-		installTimer(&sunnersLowerStairsTimer, 500);
-		break;
-	case 0xb6ca: // Sunners, shoreline
-		installTimer(&sunnersBeachTimer, 500);
-		break;
-	}
-}
-
 void MohawkEngine_Riven::doVideoTimer(VideoHandle handle, bool force) {
 	assert(handle);
 
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 131d285..73245e9 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -107,7 +107,10 @@ public:
 	Common::Error saveGameState(int slot, const Common::String &desc);
 	bool hasFeature(EngineFeature f) const;
 
-	typedef void (*TimerProc)(MohawkEngine_Riven *vm);
+	typedef Common::Functor0<void> TimerProc;
+
+#define TIMER(cls, method) \
+		new Common::Functor0Mem<void, cls>(this, &cls::method)
 
 	void doVideoTimer(VideoHandle handle, bool force);
 
@@ -131,7 +134,7 @@ private:
 	void initVars();
 
 	// Timer
-	TimerProc _timerProc;
+	Common::SharedPtr<TimerProc> _timerProc;
 	uint32 _timerTime;
 
 	// Miscellaneous
@@ -165,8 +168,7 @@ public:
 	void delayAndUpdate(uint32 ms);
 
 	// Timer
-	void installTimer(TimerProc proc, uint32 time);
-	void installCardTimer();
+	void installTimer(TimerProc *proc, uint32 time);
 	void checkTimer();
 	void removeTimer();
 
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index ab79b78..c375097 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -219,6 +219,10 @@ void RivenStack::runCredits(uint16 video, uint32 delay) {
 	_vm->setGameOver();
 }
 
+void RivenStack::installCardTimer() {
+
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index d8098f7..e1e5455 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -107,6 +107,9 @@ public:
 	/** Write all of the stack's data including its cards to standard output */
 	void dump() const;
 
+	/** Install a timer for the current card if one is defined */
+	virtual void installCardTimer();
+
 	// Common external commands
 	void xflies(uint16 argc, uint16 *argv); // Start the "flies" effect
 
diff --git a/engines/mohawk/riven_stacks/bspit.cpp b/engines/mohawk/riven_stacks/bspit.cpp
index f94098a..acc24f2 100644
--- a/engines/mohawk/riven_stacks/bspit.cpp
+++ b/engines/mohawk/riven_stacks/bspit.cpp
@@ -221,19 +221,12 @@ void BSpit::xbupdateboiler(uint16 argc, uint16 *argv) {
 	}
 }
 
-static void ytramTrapTimer(MohawkEngine_Riven *vm) {
+void BSpit::ytramTrapTimer() {
 	// Remove this timer
-	vm->removeTimer();
-
-	// FIXME: Improve the timer system (use a functor ?)
+	_vm->removeTimer();
 
 	// Check if we've caught a Ytram
-	BSpit *bspit = dynamic_cast<BSpit *>(vm->getStack());
-	if (!bspit) {
-		error("Unexpected stack type in 'ytramTrapTimer'");
-	}
-
-	bspit->checkYtramCatch(true);
+	checkYtramCatch(true);
 }
 
 void BSpit::xbsettrap(uint16 argc, uint16 *argv) {
@@ -244,7 +237,7 @@ void BSpit::xbsettrap(uint16 argc, uint16 *argv) {
 	_vm->_vars["bytramtime"] = timeUntilCatch + _vm->getTotalPlayTime();
 
 	// And set the timer too
-	_vm->installTimer(&ytramTrapTimer, timeUntilCatch);
+	_vm->installTimer(TIMER(BSpit, ytramTrapTimer), timeUntilCatch);
 }
 
 void BSpit::checkYtramCatch(bool playSound) {
@@ -255,7 +248,7 @@ void BSpit::checkYtramCatch(bool playSound) {
 	// If the trap still has not gone off, reinstall our timer
 	// This is in case you set the trap, walked away, and returned
 	if (_vm->getTotalPlayTime() < ytramTime) {
-		_vm->installTimer(&ytramTrapTimer, ytramTime - _vm->getTotalPlayTime());
+		_vm->installTimer(TIMER(BSpit, ytramTrapTimer), ytramTime - _vm->getTotalPlayTime());
 		return;
 	}
 
diff --git a/engines/mohawk/riven_stacks/bspit.h b/engines/mohawk/riven_stacks/bspit.h
index 7e810f2..135776e 100644
--- a/engines/mohawk/riven_stacks/bspit.h
+++ b/engines/mohawk/riven_stacks/bspit.h
@@ -67,6 +67,7 @@ public:
 	void xbchipper(uint16 argc, uint16 *argv);
 
 	// Time callback
+	void ytramTrapTimer();
 	void checkYtramCatch(bool playSound);
 };
 
diff --git a/engines/mohawk/riven_stacks/gspit.cpp b/engines/mohawk/riven_stacks/gspit.cpp
index b70a12c..a53d0a1 100644
--- a/engines/mohawk/riven_stacks/gspit.cpp
+++ b/engines/mohawk/riven_stacks/gspit.cpp
@@ -368,20 +368,20 @@ void GSpit::xglview_villageoff(uint16 argc, uint16 *argv) {
 	_vm->getCard()->drawPicture(1);
 }
 
-static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
-	uint32 &cathState = vm->_vars["gcathstate"];
+void GSpit::catherineViewerIdleTimer() {
+	uint32 &cathState = _vm->_vars["gcathstate"];
 	uint16 movie;
 
 	// Choose a new movie
 	if (cathState == 1) {
 		static const int movieList[] = { 9, 10, 19, 19, 21, 21 };
-		movie = movieList[vm->_rnd->getRandomNumber(5)];
+		movie = movieList[_vm->_rnd->getRandomNumber(5)];
 	} else if (cathState == 2) {
 		static const int movieList[] = { 18, 20, 22 };
-		movie = movieList[vm->_rnd->getRandomNumber(2)];
+		movie = movieList[_vm->_rnd->getRandomNumber(2)];
 	} else {
 		static const int movieList[] = { 11, 11, 12, 17, 17, 17, 17, 23 };
-		movie = movieList[vm->_rnd->getRandomNumber(7)];
+		movie = movieList[_vm->_rnd->getRandomNumber(7)];
 	}
 
 	// Update Catherine's state
@@ -393,11 +393,11 @@ static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) {
 		cathState = 3;
 
 	// Begin playing the new movie
-	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
-	VideoEntryPtr video = vm->_video->playMovieRiven(30);
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(movie));
+	VideoEntryPtr video = _vm->_video->playMovieRiven(30);
 
 	// Reset the timer
-	vm->installTimer(&catherineViewerIdleTimer, video->getDuration().msecs() + vm->_rnd->getRandomNumber(60) * 1000);
+	_vm->installTimer(TIMER(GSpit, catherineViewerIdleTimer), video->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000);
 }
 
 void GSpit::xglview_prisonon(uint16 argc, uint16 *argv) {
@@ -445,7 +445,7 @@ void GSpit::xglview_prisonon(uint16 argc, uint16 *argv) {
 	}
 
 	// Create the timer for the next video
-	_vm->installTimer(&catherineViewerIdleTimer, timeUntilNextMovie);
+	_vm->installTimer(TIMER(GSpit, catherineViewerIdleTimer), timeUntilNextMovie);
 }
 
 void GSpit::xglview_prisonoff(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_stacks/gspit.h b/engines/mohawk/riven_stacks/gspit.h
index 045d47a..e7f2169 100644
--- a/engines/mohawk/riven_stacks/gspit.h
+++ b/engines/mohawk/riven_stacks/gspit.h
@@ -65,6 +65,9 @@ public:
 	void xglview_prisonon(uint16 argc, uint16 *argv);
 	void xglview_villageon(uint16 argc, uint16 *argv);
 
+	// Timer handlers
+	void catherineViewerIdleTimer();
+
 private:
 	void lowerPins();
 };
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
index 582c1d6..5ac65b3 100644
--- a/engines/mohawk/riven_stacks/jspit.cpp
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -451,6 +451,138 @@ void JSpit::xjlagoon1500_alert(uint16 argc, uint16 *argv) {
 	}
 }
 
+void JSpit::sunnersTopStairsTimer() {
+	// If the sunners are gone, we have no video to play
+	if (_vm->_vars["jsunners"] != 0) {
+		_vm->removeTimer();
+		return;
+	}
+
+	// Play a random sunners video if the script one is not playing already
+	// and then set a new timer for when the new video should be played
+
+	VideoEntryPtr oldVideo = _vm->_video->findVideoRiven(1);
+	uint32 timerTime = 500;
+
+	if (!oldVideo || oldVideo->endOfVideo()) {
+		uint32 &sunnerTime = _vm->_vars["jsunnertime"];
+
+		if (sunnerTime == 0) {
+			timerTime = _vm->_rnd->getRandomNumberRng(2, 15) * 1000;
+		} else if (sunnerTime < _vm->getTotalPlayTime()) {
+			VideoEntryPtr video = _vm->_video->playMovieRiven(_vm->_rnd->getRandomNumberRng(1, 3));
+
+			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(2, 15) * 1000;
+		}
+
+		sunnerTime = timerTime + _vm->getTotalPlayTime();
+	}
+
+	_vm->installTimer(TIMER(JSpit, sunnersTopStairsTimer), timerTime);
+}
+
+void JSpit::sunnersMidStairsTimer() {
+	// If the sunners are gone, we have no video to play
+	if (_vm->_vars["jsunners"] != 0) {
+		_vm->removeTimer();
+		return;
+	}
+
+	// Play a random sunners video if the script one is not playing already
+	// and then set a new timer for when the new video should be played
+
+	VideoEntryPtr oldVideo = _vm->_video->findVideoRiven(1);
+	uint32 timerTime = 500;
+
+	if (!oldVideo || oldVideo->endOfVideo()) {
+		uint32 &sunnerTime = _vm->_vars["jsunnertime"];
+
+		if (sunnerTime == 0) {
+			timerTime = _vm->_rnd->getRandomNumberRng(1, 10) * 1000;
+		} else if (sunnerTime < _vm->getTotalPlayTime()) {
+			// Randomize the video
+			int randValue = _vm->_rnd->getRandomNumber(5);
+			uint16 movie = 4;
+			if (randValue == 4)
+				movie = 2;
+			else if (randValue == 5)
+				movie = 3;
+
+			VideoEntryPtr video = _vm->_video->playMovieRiven(movie);
+
+			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(1, 10) * 1000;
+		}
+
+		sunnerTime = timerTime + _vm->getTotalPlayTime();
+	}
+
+	_vm->installTimer(TIMER(JSpit, sunnersMidStairsTimer), timerTime);
+}
+
+void JSpit::sunnersLowerStairsTimer() {
+	// If the sunners are gone, we have no video to play
+	if (_vm->_vars["jsunners"] != 0) {
+		_vm->removeTimer();
+		return;
+	}
+
+	// Play a random sunners video if the script one is not playing already
+	// and then set a new timer for when the new video should be played
+
+	VideoEntryPtr oldVideo = _vm->_video->findVideoRiven(1);
+	uint32 timerTime = 500;
+
+	if (!oldVideo || oldVideo->endOfVideo()) {
+		uint32 &sunnerTime = _vm->_vars["jsunnertime"];
+
+		if (sunnerTime == 0) {
+			timerTime = _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+		} else if (sunnerTime < _vm->getTotalPlayTime()) {
+			VideoEntryPtr video = _vm->_video->playMovieRiven(_vm->_rnd->getRandomNumberRng(3, 5));
+
+			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+		}
+
+		sunnerTime = timerTime + _vm->getTotalPlayTime();
+	}
+
+	_vm->installTimer(TIMER(JSpit, sunnersLowerStairsTimer), timerTime);
+}
+
+void JSpit::sunnersBeachTimer() {
+	// If the sunners are gone, we have no video to play
+	if (_vm->_vars["jsunners"] != 0) {
+		_vm->removeTimer();
+		return;
+	}
+
+	// Play a random sunners video if the script one is not playing already
+	// and then set a new timer for when the new video should be played
+
+	VideoEntryPtr oldvideo = _vm->_video->findVideoRiven(3);
+	uint32 timerTime = 500;
+
+	if (!oldvideo || oldvideo->endOfVideo()) {
+		uint32 &sunnerTime = _vm->_vars["jsunnertime"];
+
+		if (sunnerTime == 0) {
+			timerTime = _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+		} else if (sunnerTime < _vm->getTotalPlayTime()) {
+			// Unlike the other cards' scripts which automatically
+			// activate the MLST, we have to set it manually here.
+			uint16 mlstID = _vm->_rnd->getRandomNumberRng(3, 8);
+			_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstID));
+			VideoEntryPtr video = _vm->_video->playMovieRiven(mlstID);
+
+			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+		}
+
+		sunnerTime = timerTime + _vm->getTotalPlayTime();
+	}
+
+	_vm->installTimer(TIMER(JSpit, sunnersBeachTimer), timerTime);
+}
+
 void JSpit::xjschool280_resetleft(uint16 argc, uint16 *argv) {
 	// Dummy function. This resets the unneeded video timing variable (dropLeftStart) in
 	// the DVD version.
@@ -533,5 +665,24 @@ void JSpit::xjatboundary(uint16 argc, uint16 *argv) {
 	runDemoBoundaryDialog();
 }
 
+void JSpit::installCardTimer() {
+	switch (getCurrentCardGlobalId()) {
+		case 0x77d6: // Sunners, top of stairs
+			_vm->installTimer(TIMER(JSpit, sunnersTopStairsTimer), 500);
+			break;
+		case 0x79bd: // Sunners, middle of stairs
+			_vm->installTimer(TIMER(JSpit, sunnersMidStairsTimer), 500);
+			break;
+		case 0x7beb: // Sunners, bottom of stairs
+			_vm->installTimer(TIMER(JSpit, sunnersLowerStairsTimer), 500);
+			break;
+		case 0xb6ca: // Sunners, shoreline
+			_vm->installTimer(TIMER(JSpit, sunnersBeachTimer), 500);
+			break;
+		default:
+			RivenStack::installCardTimer();
+	}
+}
+
 } // End of namespace RivenStacks
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/jspit.h b/engines/mohawk/riven_stacks/jspit.h
index d9f5ead..bf91588 100644
--- a/engines/mohawk/riven_stacks/jspit.h
+++ b/engines/mohawk/riven_stacks/jspit.h
@@ -35,6 +35,9 @@ class JSpit : public DomeSpit {
 public:
 	JSpit(MohawkEngine_Riven *vm);
 
+	// RivenStack API
+	virtual void installCardTimer() override;
+
 	// External commands - Rebel Tunnel Puzzle
 	void xreseticons(uint16 argc, uint16 *argv);
 	void xicon(uint16 argc, uint16 *argv);
@@ -80,6 +83,12 @@ public:
 	// External commands - Demo-specific
 	void xjatboundary(uint16 argc, uint16 *argv);
 
+	// Timer callbacks
+	void sunnersTopStairsTimer();
+	void sunnersMidStairsTimer();
+	void sunnersLowerStairsTimer();
+	void sunnersBeachTimer();
+
 private:
 	int jspitElevatorLoop();
 	void redrawWharkNumberPuzzle(uint16 overlay, uint16 number);
diff --git a/engines/mohawk/riven_stacks/pspit.cpp b/engines/mohawk/riven_stacks/pspit.cpp
index ae10bd0..4c80849 100644
--- a/engines/mohawk/riven_stacks/pspit.cpp
+++ b/engines/mohawk/riven_stacks/pspit.cpp
@@ -22,7 +22,9 @@
 
 #include "mohawk/riven_stacks/pspit.h"
 
+#include "mohawk/cursors.h"
 #include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/riven_sound.h"
 
 namespace Mohawk {
@@ -40,6 +42,45 @@ PSpit::PSpit(MohawkEngine_Riven *vm) :
 	REGISTER_COMMAND(PSpit, xpisland25_slidermw);
 }
 
+void PSpit::catherineIdleTimer() {
+	uint32 &cathCheck = _vm->_vars["pcathcheck"];
+	uint32 &cathState = _vm->_vars["acathstate"];
+	uint16 movie;
+
+	// Choose a random movie based on where Catherine is
+	if (cathCheck == 0) {
+		static const int movieList[] = { 5, 6, 7, 8 };
+		cathCheck = 1;
+		movie = movieList[_vm->_rnd->getRandomNumber(3)];
+	} else if (cathState == 1) {
+		static const int movieList[] = { 11, 14 };
+		movie = movieList[_vm->_rnd->getRandomBit()];
+	} else {
+		static const int movieList[] = { 9, 10, 12, 13 };
+		movie = movieList[_vm->_rnd->getRandomNumber(3)];
+	}
+
+	// Update her state if she moves from left/right or right/left, resp.
+	if (movie == 5 || movie == 7 || movie == 11 || movie == 14)
+		cathState = 2;
+	else
+		cathState = 1;
+
+	// Play the movie, blocking
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(movie));
+	_vm->_cursor->hideCursor();
+	_vm->_video->playMovieBlockingRiven(movie);
+	_vm->_cursor->showCursor();
+	_vm->_system->updateScreen();
+
+	// Install the next timer for the next video
+	uint32 timeUntilNextMovie = _vm->_rnd->getRandomNumber(120) * 1000;
+
+	_vm->_vars["pcathtime"] = timeUntilNextMovie + _vm->getTotalPlayTime();
+
+	_vm->installTimer(TIMER(PSpit, catherineIdleTimer), timeUntilNextMovie);
+}
+
 void PSpit::xpisland990_elevcombo(uint16 argc, uint16 *argv) {
 	// Play button sound based on argv[0]
 	_vm->_sound->playSound(argv[0] + 5);
@@ -84,5 +125,15 @@ void PSpit::xpisland25_slidermw(uint16 argc, uint16 *argv) {
 	checkSliderCursorChange(14);
 }
 
+void PSpit::installCardTimer() {
+	if (getCurrentCardGlobalId() == 0x3a85) {
+		// Top of elevator on prison island
+		// Handle Catherine hardcoded videos
+		_vm->installTimer(TIMER(PSpit, catherineIdleTimer), _vm->_rnd->getRandomNumberRng(1, 33) * 1000);
+	} else {
+		RivenStack::installCardTimer();
+	}
+}
+
 } // End of namespace RivenStacks
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_stacks/pspit.h b/engines/mohawk/riven_stacks/pspit.h
index ca09417..ec0186a 100644
--- a/engines/mohawk/riven_stacks/pspit.h
+++ b/engines/mohawk/riven_stacks/pspit.h
@@ -35,6 +35,9 @@ class PSpit : public DomeSpit {
 public:
 	PSpit(MohawkEngine_Riven *vm);
 
+	// RivenStack API
+	virtual void installCardTimer() override;
+
 	// External commands - Prison Elevator
 	void xpisland990_elevcombo(uint16 argc, uint16 *argv);	// Param1: button
 
@@ -46,6 +49,8 @@ public:
 	void xpisland25_slidermd(uint16 argc, uint16 *argv);
 	void xpisland25_slidermw(uint16 argc, uint16 *argv);
 
+	// Timer callbacks
+	void catherineIdleTimer();
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/rspit.cpp b/engines/mohawk/riven_stacks/rspit.cpp
index 992319d..a7431a6 100644
--- a/engines/mohawk/riven_stacks/rspit.cpp
+++ b/engines/mohawk/riven_stacks/rspit.cpp
@@ -60,20 +60,20 @@ void RSpit::xrhideinventory(uint16 argc, uint16 *argv) {
 	_vm->_gfx->hideInventory();
 }
 
-static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) {
+void RSpit::rebelPrisonWindowTimer() {
 	// Randomize a video out in the middle of Tay
-	uint16 movie = vm->_rnd->getRandomNumberRng(2, 13);
-	vm->_video->activateMLST(vm->getCard()->getMovie(movie));
-	VideoEntryPtr handle = vm->_video->playMovieRiven(movie);
+	uint16 movie = _vm->_rnd->getRandomNumberRng(2, 13);
+	_vm->_video->activateMLST(_vm->getCard()->getMovie(movie));
+	VideoEntryPtr handle = _vm->_video->playMovieRiven(movie);
 
 	// Ensure the next video starts after this one ends
-	uint32 timeUntilNextVideo = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(38, 58) * 1000;
+	uint32 timeUntilNextVideo = handle->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(38, 58) * 1000;
 
 	// Save the time in case we leave the card and return
-	vm->_vars["rvillagetime"] = timeUntilNextVideo + vm->getTotalPlayTime();
+	_vm->_vars["rvillagetime"] = timeUntilNextVideo + _vm->getTotalPlayTime();
 
 	// Reinstall this timer with the new time
-	vm->installTimer(&rebelPrisonWindowTimer, timeUntilNextVideo);
+	_vm->installTimer(TIMER(RSpit, rebelPrisonWindowTimer), timeUntilNextVideo);
 }
 
 void RSpit::xrwindowsetup(uint16 argc, uint16 *argv) {
@@ -83,7 +83,7 @@ void RSpit::xrwindowsetup(uint16 argc, uint16 *argv) {
 
 	// If we have time leftover from a previous run, set up the timer again
 	if (_vm->getTotalPlayTime() < villageTime) {
-		_vm->installTimer(&rebelPrisonWindowTimer, villageTime - _vm->getTotalPlayTime());
+		_vm->installTimer(TIMER(RSpit, rebelPrisonWindowTimer), villageTime - _vm->getTotalPlayTime());
 		return;
 	}
 
@@ -106,7 +106,7 @@ void RSpit::xrwindowsetup(uint16 argc, uint16 *argv) {
 	// the timer to reinstall itself...
 
 	// Install our timer and we're on our way
-	_vm->installTimer(&rebelPrisonWindowTimer, timeUntilNextVideo);
+	_vm->installTimer(TIMER(RSpit, rebelPrisonWindowTimer), timeUntilNextVideo);
 }
 
 
diff --git a/engines/mohawk/riven_stacks/rspit.h b/engines/mohawk/riven_stacks/rspit.h
index c46537c..fe21944 100644
--- a/engines/mohawk/riven_stacks/rspit.h
+++ b/engines/mohawk/riven_stacks/rspit.h
@@ -41,6 +41,8 @@ public:
 	void xrhideinventory(uint16 argc, uint16 *argv);
 	void xrwindowsetup(uint16 argc, uint16 *argv);
 
+	// Timer callbacks
+	void rebelPrisonWindowTimer();
 };
 
 } // End of namespace RivenStacks


Commit: 0ba035eea6e36c3d0bd9058d0b623eb7aad23f82
    https://github.com/scummvm/scummvm/commit/0ba035eea6e36c3d0bd9058d0b623eb7aad23f82
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move Riven inventory code to a new class

Changed paths:
  A engines/mohawk/riven_inventory.cpp
  A engines/mohawk/riven_inventory.h
    engines/mohawk/module.mk
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stacks/aspit.cpp
    engines/mohawk/riven_stacks/aspit.h
    engines/mohawk/riven_stacks/rspit.cpp
    engines/mohawk/riven_stacks/tspit.cpp


diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 292310e..7022c23 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -55,6 +55,7 @@ MODULE_OBJS += \
 	riven.o \
 	riven_card.o \
 	riven_graphics.o \
+	riven_inventory.o \
 	riven_saveload.o \
 	riven_scripts.o \
 	riven_sound.o \
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 7951d0f..c387772 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -33,6 +33,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_inventory.h"
 #include "mohawk/riven_saveload.h"
 #include "mohawk/riven_sound.h"
 #include "mohawk/riven_stack.h"
@@ -50,18 +51,9 @@
 
 namespace Mohawk {
 
-Common::Rect *g_atrusJournalRect1;
-Common::Rect *g_atrusJournalRect2;
-Common::Rect *g_cathJournalRect2;
-Common::Rect *g_atrusJournalRect3;
-Common::Rect *g_cathJournalRect3;
-Common::Rect *g_trapBookRect3;
-Common::Rect *g_demoExitRect;
-
 MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) :
 		MohawkEngine(syst, gamedesc) {
 	_showHotspots = false;
-	_gameOver = false;
 	_activatedPLST = false;
 	_activatedSLST = false;
 	_extrasFile = nullptr;
@@ -74,6 +66,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_saveLoad = nullptr;
 	_optionsDialog = nullptr;
 	_card = nullptr;
+	_inventory = nullptr;
 	removeTimer();
 
 	// NOTE: We can never really support CD swapping. All of the music files
@@ -88,14 +81,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	SearchMan.addSubDirectoryMatching(gameDataDir, "exe");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "assets1");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "program");
-
-	g_atrusJournalRect1 = new Common::Rect(295, 402, 313, 426);
-	g_atrusJournalRect2 = new Common::Rect(259, 402, 278, 426);
-	g_cathJournalRect2 = new Common::Rect(328, 408, 348, 419);
-	g_atrusJournalRect3 = new Common::Rect(222, 402, 240, 426);
-	g_cathJournalRect3 = new Common::Rect(291, 408, 311, 419);
-	g_trapBookRect3 = new Common::Rect(363, 396, 386, 432);
-	g_demoExitRect = new Common::Rect(291, 408, 317, 419);
 }
 
 MohawkEngine_Riven::~MohawkEngine_Riven() {
@@ -108,14 +93,8 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
 	delete _saveLoad;
 	delete _scriptMan;
 	delete _optionsDialog;
+	delete _inventory;
 	delete _rnd;
-	delete g_atrusJournalRect1;
-	delete g_atrusJournalRect2;
-	delete g_cathJournalRect2;
-	delete g_atrusJournalRect3;
-	delete g_cathJournalRect3;
-	delete g_trapBookRect3;
-	delete g_demoExitRect;
 }
 
 GUI::Debugger *MohawkEngine_Riven::getDebugger() {
@@ -136,6 +115,7 @@ Common::Error MohawkEngine_Riven::run() {
 	_saveLoad = new RivenSaveLoad(this, _saveFileMan);
 	_optionsDialog = new RivenOptionsDialog(this);
 	_scriptMan = new RivenScriptManager(this);
+	_inventory = new RivenInventory(this);
 
 	_rnd = new Common::RandomSource("riven");
 
@@ -198,7 +178,7 @@ Common::Error MohawkEngine_Riven::run() {
 	}
 
 
-	while (!_gameOver && !shouldQuit())
+	while (!shouldQuit())
 		handleEvents();
 
 	return Common::kNoError;
@@ -222,9 +202,9 @@ void MohawkEngine_Riven::handleEvents() {
 			if (!(getFeatures() & GF_DEMO)) {
 				// Check to show the inventory, but it is always "showing" in the demo
 				if (_eventMan->getMousePos().y >= 392)
-					_gfx->showInventory();
+					_inventory->show();
 				else
-					_gfx->hideInventory();
+					_inventory->hide();
 			}
 
 			needsUpdate = true;
@@ -237,7 +217,7 @@ void MohawkEngine_Riven::handleEvents() {
 			break;
 		case Common::EVENT_LBUTTONUP:
 			_card->onMouseUp(_eventMan->getMousePos());
-			checkInventoryClick();
+			_inventory->checkClick(_eventMan->getMousePos());
 			break;
 		case Common::EVENT_KEYDOWN:
 			switch (event.kbd.keycode) {
@@ -441,79 +421,6 @@ void MohawkEngine_Riven::updateCurrentHotspot() {
 	_card->onMouseMove(_eventMan->getMousePos());
 }
 
-void MohawkEngine_Riven::checkInventoryClick() {
-	Common::Point mousePos = _eventMan->getMousePos();
-
-	// Don't even bother. We're not in the inventory portion of the screen.
-	if (mousePos.y < 392)
-		return;
-
-	// In the demo, check if we've clicked the exit button
-	if (getFeatures() & GF_DEMO) {
-		if (g_demoExitRect->contains(mousePos)) {
-			if (_stack->getId() == kStackAspit && _card->getId() == 1) {
-				// From the main menu, go to the "quit" screen
-				changeToCard(12);
-			} else if (_stack->getId() == kStackAspit && _card->getId() == 12) {
-				// From the "quit" screen, just quit
-				_gameOver = true;
-			} else {
-				// Otherwise, return to the main menu
-				if (_stack->getId() != kStackAspit)
-					changeToStack(kStackAspit);
-				changeToCard(1);
-			}
-		}
-		return;
-	}
-
-	// No inventory shown on aspit
-	if (_stack->getId() == kStackAspit)
-		return;
-
-	// Set the return stack/card id's.
-	_vars["returnstackid"] = _stack->getId();
-	_vars["returncardid"] = _stack->getCardGlobalId(_card->getId());
-
-	// See RivenGraphics::showInventory() for an explanation
-	// of the variables' meanings.
-	bool hasCathBook = _vars["acathbook"] != 0;
-	bool hasTrapBook = _vars["atrapbook"] != 0;
-
-	// Go to the book if a hotspot contains the mouse
-	if (!hasCathBook) {
-		if (g_atrusJournalRect1->contains(mousePos)) {
-			_gfx->hideInventory();
-			changeToStack(kStackAspit);
-			changeToCard(5);
-		}
-	} else if (!hasTrapBook) {
-		if (g_atrusJournalRect2->contains(mousePos)) {
-			_gfx->hideInventory();
-			changeToStack(kStackAspit);
-			changeToCard(5);
-		} else if (g_cathJournalRect2->contains(mousePos)) {
-			_gfx->hideInventory();
-			changeToStack(kStackAspit);
-			changeToCard(6);
-		}
-	} else {
-		if (g_atrusJournalRect3->contains(mousePos)) {
-			_gfx->hideInventory();
-			changeToStack(kStackAspit);
-			changeToCard(5);
-		} else if (g_cathJournalRect3->contains(mousePos)) {
-			_gfx->hideInventory();
-			changeToStack(kStackAspit);
-			changeToCard(6);
-		} else if (g_trapBookRect3->contains(mousePos)) {
-			_gfx->hideInventory();
-			changeToStack(kStackAspit);
-			changeToCard(7);
-		}
-	}
-}
-
 Common::SeekableReadStream *MohawkEngine_Riven::getExtrasResource(uint32 tag, uint16 id) {
 	return _extrasFile->getResource(tag, id);
 }
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 73245e9..9e9f2b2 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -44,6 +44,7 @@ class RivenStack;
 class RivenCard;
 class RivenHotspot;
 class RivenSoundManager;
+class RivenInventory;
 
 // Riven Stack Types
 enum {
@@ -68,16 +69,6 @@ enum RivenTransitionSpeed {
 	kRivenTransitionSpeedBest = 5003
 };
 
-// Rects for the inventory object positions (initialized in
-// MohawkEngine_Riven's constructor).
-extern Common::Rect *g_atrusJournalRect1;
-extern Common::Rect *g_atrusJournalRect2;
-extern Common::Rect *g_cathJournalRect2;
-extern Common::Rect *g_atrusJournalRect3;
-extern Common::Rect *g_cathJournalRect3;
-extern Common::Rect *g_trapBookRect3;
-extern Common::Rect *g_demoExitRect;
-
 struct ZipMode {
 	Common::String name;
 	uint16 id;
@@ -98,6 +89,7 @@ public:
 	RivenGraphics *_gfx;
 	Common::RandomSource *_rnd;
 	RivenScriptManager *_scriptMan;
+	RivenInventory *_inventory;
 
 	GUI::Debugger *getDebugger();
 
@@ -127,7 +119,6 @@ private:
 	void handleEvents();
 
 	// Hotspot related functions and variables
-	void checkInventoryClick();
 	bool _showHotspots;
 
 	// Variables
@@ -138,7 +129,6 @@ private:
 	uint32 _timerTime;
 
 	// Miscellaneous
-	bool _gameOver;
 	void checkSunnerAlertClick();
 
 public:
@@ -160,7 +150,6 @@ public:
 	uint32 &getStackVar(uint32 index);
 
 	// Miscellaneous
-	void setGameOver() { _gameOver = true; }
 	Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id);
 	bool _activatedPLST;
 	bool _activatedSLST;
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 573eddf..7fa35dd 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -51,7 +51,6 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 	_screenUpdateRunning = false;
 	_scheduledTransition = -1;	// no transition
 	_dirtyScreen = false;
-	_inventoryDrawn = false;
 
 	_creditsImage = 302;
 	_creditsPos = 0;
@@ -256,79 +255,12 @@ void RivenGraphics::fadeToBlack() {
 	runScheduledTransition();
 }
 
-void RivenGraphics::showInventory() {
-	// Don't redraw the inventory
-	if (_inventoryDrawn)
-		return;
-
-	// Clear the inventory area
-	clearInventoryArea();
-
-	// Draw the demo's exit button
-	if (_vm->getFeatures() & GF_DEMO) {
-		// extras.mhk tBMP 101 contains "EXIT" instead of Atrus' journal in the demo!
-		// The demo's extras.mhk contains all the other inventory/marble/credits image
-		// but has hacked tBMP 101 with "EXIT". *sigh*
-		drawInventoryImage(101, g_demoExitRect);
-	} else {
-		// We don't want to show the inventory on setup screens or in other journals.
-		if (_vm->getStack()->getId() == kStackAspit)
-			return;
-
-		// There are three books and three vars. We have three different
-		// combinations. At the start you have just Atrus' journal. Later,
-		// you get Catherine's journal and the trap book. Near the end,
-		// you lose the trap book and have just the two journals.
-
-		bool hasCathBook = _vm->_vars["acathbook"] != 0;
-		bool hasTrapBook = _vm->_vars["atrapbook"] != 0;
-
-		if (!hasCathBook) {
-			drawInventoryImage(101, g_atrusJournalRect1);
-		} else if (!hasTrapBook) {
-			drawInventoryImage(101, g_atrusJournalRect2);
-			drawInventoryImage(102, g_cathJournalRect2);
-		} else {
-			drawInventoryImage(101, g_atrusJournalRect3);
-			drawInventoryImage(102, g_cathJournalRect3);
-			drawInventoryImage(100, g_trapBookRect3);
-		}
-	}
-
-	_vm->_system->updateScreen();
-	_inventoryDrawn = true;
-}
-
-void RivenGraphics::hideInventory() {
-	// Don't hide the inventory twice
-	if (!_inventoryDrawn)
-		return;
-
-	// Clear the area
-	clearInventoryArea();
-
-	_inventoryDrawn = false;
-}
-
-void RivenGraphics::clearInventoryArea() {
-	// Clear the inventory area
-	static const Common::Rect inventoryRect = Common::Rect(0, 392, 608, 436);
-
-	// Lock the screen
-	Graphics::Surface *screen = _vm->_system->lockScreen();
-
-	// Fill the inventory area with black
-	screen->fillRect(inventoryRect, _pixelFormat.RGBToColor(0, 0, 0));
-
-	_vm->_system->unlockScreen();
-}
-
-void RivenGraphics::drawInventoryImage(uint16 id, const Common::Rect *rect) {
+void RivenGraphics::drawExtrasImageToScreen(uint16 id, const Common::Rect &rect) {
 	MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
 	mhkSurface->convertToTrueColor();
 	Graphics::Surface *surface = mhkSurface->getSurface();
 
-	_vm->_system->copyRectToScreen(surface->getPixels(), surface->pitch, rect->left, rect->top, surface->w, surface->h);
+	_vm->_system->copyRectToScreen(surface->getPixels(), surface->pitch, rect.left, rect.top, surface->w, surface->h);
 
 	delete mhkSurface;
 }
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 8120879..76bcae5 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -44,6 +44,7 @@ public:
 	void drawRect(Common::Rect rect, bool active);
 	void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect);
 	void drawExtrasImage(uint16 id, Common::Rect dstRect);
+	void drawExtrasImageToScreen(uint16 id, const Common::Rect &rect);
 
 	Graphics::Surface *getEffectScreen();
 	Graphics::Surface *getBackScreen();
@@ -64,10 +65,6 @@ public:
 	void fadeToBlack();
 	void setTransitionSpeed(uint32 speed) { _transitionSpeed = speed; }
 
-	// Inventory
-	void showInventory();
-	void hideInventory();
-
 	// Credits
 	void beginCredits();
 	void updateCredits();
@@ -105,11 +102,6 @@ private:
 	Common::Rect _transitionRect;
 	uint32 _transitionSpeed;
 
-	// Inventory
-	void clearInventoryArea();
-	void drawInventoryImage(uint16 id, const Common::Rect *rect);
-	bool _inventoryDrawn;
-
 	// Screen Related
 	Graphics::Surface *_mainScreen;
 	Graphics::Surface *_effectScreen;
diff --git a/engines/mohawk/riven_inventory.cpp b/engines/mohawk/riven_inventory.cpp
new file mode 100644
index 0000000..11a42f0
--- /dev/null
+++ b/engines/mohawk/riven_inventory.cpp
@@ -0,0 +1,202 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mohawk/riven_inventory.h"
+
+#include "mohawk/resource.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
+#include "mohawk/riven_graphics.h"
+#include "mohawk/riven_stack.h"
+
+namespace Mohawk {
+
+RivenInventory::RivenInventory(MohawkEngine_Riven *vm) :
+		_vm(vm) {
+
+	_inventoryDrawn = false;
+
+	_atrusJournalRect1 = Common::Rect(295, 402, 313, 426);
+	_atrusJournalRect2 = Common::Rect(259, 402, 278, 426);
+	_cathJournalRect2 = Common::Rect(328, 408, 348, 419);
+	_atrusJournalRect3 = Common::Rect(222, 402, 240, 426);
+	_cathJournalRect3 = Common::Rect(291, 408, 311, 419);
+	_trapBookRect3 = Common::Rect(363, 396, 386, 432);
+	_demoExitRect = Common::Rect(291, 408, 317, 419);
+}
+
+RivenInventory::~RivenInventory() {
+
+}
+
+void RivenInventory::show() {
+	// Don't redraw the inventory
+	if (_inventoryDrawn)
+		return;
+
+	// Clear the inventory area
+	clearArea();
+
+	// Draw the demo's exit button
+	if (_vm->getFeatures() & GF_DEMO) {
+		// extras.mhk tBMP 101 contains "EXIT" instead of Atrus' journal in the demo!
+		// The demo's extras.mhk contains all the other inventory/marble/credits image
+		// but has hacked tBMP 101 with "EXIT". *sigh*
+		_vm->_gfx->drawExtrasImageToScreen(101, _demoExitRect);
+	} else {
+		// We don't want to show the inventory on setup screens or in other journals.
+		if (_vm->getStack()->getId() == kStackAspit)
+			return;
+
+		// There are three books and three vars. We have three different
+		// combinations. At the start you have just Atrus' journal. Later,
+		// you get Catherine's journal and the trap book. Near the end,
+		// you lose the trap book and have just the two journals.
+
+		bool hasCathBook = _vm->_vars["acathbook"] != 0;
+		bool hasTrapBook = _vm->_vars["atrapbook"] != 0;
+
+		if (!hasCathBook) {
+			_vm->_gfx->drawExtrasImageToScreen(101, _atrusJournalRect1);
+		} else if (!hasTrapBook) {
+			_vm->_gfx->drawExtrasImageToScreen(101, _atrusJournalRect2);
+			_vm->_gfx->drawExtrasImageToScreen(102, _cathJournalRect2);
+		} else {
+			_vm->_gfx->drawExtrasImageToScreen(101, _atrusJournalRect3);
+			_vm->_gfx->drawExtrasImageToScreen(102, _cathJournalRect3);
+			_vm->_gfx->drawExtrasImageToScreen(100, _trapBookRect3);
+		}
+	}
+
+	_vm->_system->updateScreen();
+	_inventoryDrawn = true;
+}
+
+void RivenInventory::hide() {
+	// Don't hide the inventory twice
+	if (!_inventoryDrawn)
+		return;
+
+	// Clear the area
+	clearArea();
+
+	_inventoryDrawn = false;
+}
+
+void RivenInventory::clearArea() {
+	// Clear the inventory area
+	static const Common::Rect inventoryRect = Common::Rect(0, 392, 608, 436);
+
+	// Lock the screen
+	Graphics::Surface *screen = _vm->_system->lockScreen();
+
+	// Fill the inventory area with black
+	screen->fillRect(inventoryRect, screen->format.RGBToColor(0, 0, 0));
+
+	_vm->_system->unlockScreen();
+}
+
+void RivenInventory::checkClick(const Common::Point &mousePos) {
+	// Don't even bother. We're not in the inventory portion of the screen.
+	if (mousePos.y < 392)
+		return;
+
+	// In the demo, check if we've clicked the exit button
+	if (_vm->getFeatures() & GF_DEMO) {
+		if (_demoExitRect.contains(mousePos)) {
+			if (_vm->getStack()->getId() == kStackAspit && _vm->getCard()->getId() == 1) {
+				// From the main menu, go to the "quit" screen
+				_vm->changeToCard(12);
+			} else if (_vm->getStack()->getId() == kStackAspit && _vm->getCard()->getId() == 12) {
+				// From the "quit" screen, just quit
+				_vm->quitGame();
+			} else {
+				// Otherwise, return to the main menu
+				if (_vm->getStack()->getId() != kStackAspit)
+					_vm->changeToStack(kStackAspit);
+				_vm->changeToCard(1);
+			}
+		}
+		return;
+	}
+
+	// No inventory shown on aspit
+	if (_vm->getStack()->getId() == kStackAspit)
+		return;
+
+	// Set the return stack/card id's.
+	_vm->_vars["returnstackid"] = _vm->getStack()->getId();
+	_vm->_vars["returncardid"] = _vm->getStack()->getCardGlobalId(_vm->getCard()->getId());
+
+	// See RivenGraphics::show() for an explanation
+	// of the variables' meanings.
+	bool hasCathBook = _vm->_vars["acathbook"] != 0;
+	bool hasTrapBook = _vm->_vars["atrapbook"] != 0;
+
+	// Go to the book if a hotspot contains the mouse
+	if (!hasCathBook) {
+		if (_atrusJournalRect1.contains(mousePos)) {
+			hide();
+			_vm->changeToStack(kStackAspit);
+			_vm->changeToCard(5);
+		}
+	} else if (!hasTrapBook) {
+		if (_atrusJournalRect2.contains(mousePos)) {
+			hide();
+			_vm->changeToStack(kStackAspit);
+			_vm->changeToCard(5);
+		} else if (_cathJournalRect2.contains(mousePos)) {
+			hide();
+			_vm->changeToStack(kStackAspit);
+			_vm->changeToCard(6);
+		}
+	} else {
+		if (_atrusJournalRect3.contains(mousePos)) {
+			hide();
+			_vm->changeToStack(kStackAspit);
+			_vm->changeToCard(5);
+		} else if (_cathJournalRect3.contains(mousePos)) {
+			hide();
+			_vm->changeToStack(kStackAspit);
+			_vm->changeToCard(6);
+		} else if (_trapBookRect3.contains(mousePos)) {
+			hide();
+			_vm->changeToStack(kStackAspit);
+			_vm->changeToCard(7);
+		}
+	}
+}
+
+void RivenInventory::backFromItemScript() const {
+	RivenScriptPtr stopSoundScript = _vm->_scriptMan->createScriptFromData(1, 12, 1, 1);
+	_vm->_scriptMan->runScript(stopSoundScript, false);
+
+	uint16 backStackId = _vm->_vars["returnstackid"];
+	uint32 backCardId = _vm->_vars["returncardid"];
+
+	// Return to where we were before entering the book
+	RivenCommand *back = new RivenStackChangeCommand(_vm, backStackId, backCardId, true);
+	RivenScriptPtr backScript = _vm->_scriptMan->createScriptWithCommand(back);
+	_vm->_scriptMan->runScript(backScript, false);
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_inventory.h b/engines/mohawk/riven_inventory.h
new file mode 100644
index 0000000..e1b7ae5
--- /dev/null
+++ b/engines/mohawk/riven_inventory.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef RIVEN_INVENTORY_H
+#define RIVEN_INVENTORY_H
+
+#include "common/rect.h"
+
+namespace Mohawk {
+
+class MohawkEngine_Riven;
+
+/**
+ * The player's inventory
+ *
+ * Is responsible for drawing the bottom part of the screen.
+ */
+class RivenInventory {
+public:
+	RivenInventory(MohawkEngine_Riven *vm);
+	virtual ~RivenInventory();
+
+	/** Make the inventory visible */
+	void show();
+
+	/** Make the inventory invisible */
+	void hide();
+
+	/** Handle a click event in the inventory area */
+	void checkClick(const Common::Point &mousePos);
+
+	/** Go back to the game from an inventory item detail view */
+	void backFromItemScript() const;
+
+private:
+	void clearArea();
+
+	MohawkEngine_Riven *_vm;
+
+	bool _inventoryDrawn;
+
+	// Rects for the inventory object positions
+	Common::Rect _atrusJournalRect1;
+	Common::Rect _atrusJournalRect2;
+	Common::Rect _cathJournalRect2;
+	Common::Rect _atrusJournalRect3;
+	Common::Rect _cathJournalRect3;
+	Common::Rect _trapBookRect3;
+	Common::Rect _demoExitRect;
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index c375097..c394645 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -216,7 +216,7 @@ void RivenStack::runCredits(uint16 video, uint32 delay) {
 		_vm->_system->delayMillis(10);
 	}
 
-	_vm->setGameOver();
+	_vm->quitGame();
 }
 
 void RivenStack::installCardTimer() {
diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp
index 4c88b19..aabd7db 100644
--- a/engines/mohawk/riven_stacks/aspit.cpp
+++ b/engines/mohawk/riven_stacks/aspit.cpp
@@ -25,6 +25,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_inventory.h"
 #include "mohawk/riven_sound.h"
 
 #include "common/translation.h"
@@ -95,20 +96,7 @@ void ASpit::xaatrusopenbook(uint16 argc, uint16 *argv) {
 }
 
 void ASpit::xaatrusbookback(uint16 argc, uint16 *argv) {
-	inventoryBackFromItemScript();
-}
-
-void ASpit::inventoryBackFromItemScript() const {
-	RivenScriptPtr stopSoundScript = _vm->_scriptMan->createScriptFromData(1, 12, 1, 1);
-	_vm->_scriptMan->runScript(stopSoundScript, false);
-
-	uint16 backStackId = _vm->_vars["returnstackid"];
-	uint32 backCardId = _vm->_vars["returncardid"];
-
-	// Return to where we were before entering the book
-	RivenCommand *back = new RivenStackChangeCommand(_vm, backStackId, backCardId, true);
-	RivenScriptPtr backScript = _vm->_scriptMan->createScriptWithCommand(back);
-	_vm->_scriptMan->runScript(backScript, false);
+	_vm->_inventory->backFromItemScript();
 }
 
 void ASpit::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
@@ -198,7 +186,7 @@ void ASpit::xacathopenbook(uint16 argc, uint16 *argv) {
 }
 
 void ASpit::xacathbookback(uint16 argc, uint16 *argv) {
-	inventoryBackFromItemScript();
+	_vm->_inventory->backFromItemScript();
 }
 
 void ASpit::xacathbookprevpage(uint16 argc, uint16 *argv) {
@@ -238,7 +226,7 @@ void ASpit::xacathbooknextpage(uint16 argc, uint16 *argv) {
 void ASpit::xtrapbookback(uint16 argc, uint16 *argv) {
 	// Return to where we were before entering the book
 	_vm->_vars["atrap"] = 0;
-	inventoryBackFromItemScript();
+	_vm->_inventory->backFromItemScript();
 }
 
 void ASpit::xatrapbookclose(uint16 argc, uint16 *argv) {
@@ -309,7 +297,7 @@ void ASpit::xadisablemenuintro(uint16 argc, uint16 *argv) {
 	// The original also had this shortcut.
 
 	// Hide the "exit" button here
-	_vm->_gfx->hideInventory();
+	_vm->_inventory->hide();
 }
 
 void ASpit::xaenablemenuintro(uint16 argc, uint16 *argv) {
@@ -318,12 +306,12 @@ void ASpit::xaenablemenuintro(uint16 argc, uint16 *argv) {
 	// The original also had this shortcut.
 
 	// Show the "exit" button here
-	_vm->_gfx->showInventory();
+	_vm->_inventory->show();
 }
 
 void ASpit::xademoquit(uint16 argc, uint16 *argv) {
 	// Exactly as it says on the tin. In the demo, this function quits.
-	_vm->setGameOver();
+	_vm->quitGame();
 }
 
 void ASpit::xaexittomain(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_stacks/aspit.h b/engines/mohawk/riven_stacks/aspit.h
index 3aa4002..60400dd 100644
--- a/engines/mohawk/riven_stacks/aspit.h
+++ b/engines/mohawk/riven_stacks/aspit.h
@@ -67,8 +67,6 @@ public:
 	void xaenablemenuintro(uint16 argc, uint16 *argv);
 	void xademoquit(uint16 argc, uint16 *argv);
 	void xaexittomain(uint16 argc, uint16 *argv);
-
-	void inventoryBackFromItemScript() const;
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/rspit.cpp b/engines/mohawk/riven_stacks/rspit.cpp
index a7431a6..49fac73 100644
--- a/engines/mohawk/riven_stacks/rspit.cpp
+++ b/engines/mohawk/riven_stacks/rspit.cpp
@@ -25,6 +25,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_inventory.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -53,11 +54,11 @@ void RSpit::xrshowinventory(uint16 argc, uint16 *argv) {
 	// Give the trap book and Catherine's journal to the player
 	_vm->_vars["atrapbook"] = 1;
 	_vm->_vars["acathbook"] = 1;
-	_vm->_gfx->showInventory();
+	_vm->_inventory->show();
 }
 
 void RSpit::xrhideinventory(uint16 argc, uint16 *argv) {
-	_vm->_gfx->hideInventory();
+	_vm->_inventory->hide();
 }
 
 void RSpit::rebelPrisonWindowTimer() {
diff --git a/engines/mohawk/riven_stacks/tspit.cpp b/engines/mohawk/riven_stacks/tspit.cpp
index 763f535..e4d5179 100644
--- a/engines/mohawk/riven_stacks/tspit.cpp
+++ b/engines/mohawk/riven_stacks/tspit.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_inventory.h"
 
 #include "common/events.h"
 
@@ -181,7 +182,7 @@ void TSpit::xtchotakesbook(uint16 argc, uint16 *argv) {
 }
 
 void TSpit::xthideinventory(uint16 argc, uint16 *argv) {
-	_vm->_gfx->hideInventory();
+	_vm->_inventory->hide();
 }
 
 // Marble Puzzle related constants


Commit: 79e086ba7b4e15b8281c88f52fd7a68e85dc7579
    https://github.com/scummvm/scummvm/commit/79e086ba7b4e15b8281c88f52fd7a68e85dc7579
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Turn script commands into SharedPtrs

Commands can be shared between scripts when adding commands
from one script to another.

Changed paths:
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 1445731..331f226 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -56,23 +56,23 @@ RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream) {
 	uint16 commandCount = stream->readUint16BE();
 
 	for (uint16 i = 0; i < commandCount; i++) {
-		RivenCommand *command = readCommand(stream);
+		RivenCommandPtr command = readCommand(stream);
 		script->addCommand(command);
 	}
 
 	return script;
 }
 
-RivenCommand *RivenScriptManager::readCommand(Common::ReadStream *stream) {
+RivenCommandPtr RivenScriptManager::readCommand(Common::ReadStream *stream) {
 	uint16 type = stream->readUint16BE();
 
 	switch (type) {
 		case 8:
-			return RivenSwitchCommand::createFromStream(_vm, type, stream);
+			return RivenCommandPtr(RivenSwitchCommand::createFromStream(_vm, type, stream));
 		case 27:
-			return RivenStackChangeCommand::createFromStream(_vm, type, stream);
+			return RivenCommandPtr(RivenStackChangeCommand::createFromStream(_vm, type, stream));
 		default:
-			return RivenSimpleCommand::createFromStream(_vm, type, stream);
+			return RivenCommandPtr(RivenSimpleCommand::createFromStream(_vm, type, stream));
 	}
 }
 
@@ -160,7 +160,7 @@ RivenScriptPtr RivenScriptManager::createScriptWithCommand(RivenCommand *command
 	assert(command);
 
 	RivenScriptPtr script = RivenScriptPtr(new RivenScript());
-	script->addCommand(command);
+	script->addCommand(RivenCommandPtr(command));
 	return script;
 }
 
@@ -169,9 +169,6 @@ RivenScript::RivenScript() {
 }
 
 RivenScript::~RivenScript() {
-	for (uint i = 0; i < _commands.size(); i ++) {
-		delete _commands[i];
-	}
 }
 
 void RivenScript::dumpScript(byte tabs) {
@@ -181,12 +178,12 @@ void RivenScript::dumpScript(byte tabs) {
 }
 
 void RivenScript::run() {
-	for (uint16 i = 0; i < _commands.size() && _continueRunning; i++) {
+	for (uint i = 0; i < _commands.size() && _continueRunning; i++) {
 		_commands[i]->execute();
 	}
 }
 
-void RivenScript::addCommand(RivenCommand *command) {
+void RivenScript::addCommand(RivenCommandPtr command) {
 	_commands.push_back(command);
 }
 
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 13c415b..a5c1ccb 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -55,6 +55,7 @@ class RivenCommand;
 class RivenScript;
 
 typedef Common::SharedPtr<RivenScript> RivenScriptPtr;
+typedef Common::SharedPtr<RivenCommand> RivenCommandPtr;
 
 /**
  * Scripts in Riven are a list of Commands
@@ -68,7 +69,7 @@ public:
 	~RivenScript();
 
 	/** Append a command to the script */
-	void addCommand(RivenCommand *command);
+	void addCommand(RivenCommandPtr command);
 
 	/** True if the script does not contain any command */
 	bool empty() const;
@@ -94,7 +95,7 @@ public:
 	static const char *getTypeName(uint16 type);
 
 private:
-	Common::Array<RivenCommand *> _commands;
+	Common::Array<RivenCommandPtr> _commands;
 	bool _continueRunning;
 };
 
@@ -163,7 +164,7 @@ private:
 	Common::Array<RivenScriptPtr> _queue;
 	StoredMovieOpcode _storedMovieOpcode;
 
-	RivenCommand *readCommand(Common::ReadStream *stream);
+	RivenCommandPtr readCommand(Common::ReadStream *stream);
 };
 
 /**


Commit: 92e03a7b684c019d7670a56feb8e3a6cfd2e85b2
    https://github.com/scummvm/scummvm/commit/92e03a7b684c019d7670a56feb8e3a6cfd2e85b2
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add a command to check if background scripts are running

Changed paths:
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 331f226..22651fc 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -124,6 +124,10 @@ void RivenScriptManager::runScript(const RivenScriptPtr &script, bool queue) {
 	}
 }
 
+bool RivenScriptManager::hasQueuedScripts() const {
+	return !_queue.empty();
+}
+
 RivenScriptPtr RivenScriptManager::createScriptFromData(uint16 commandCount, ...) {
 	va_list args;
 	va_start(args, commandCount);
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index a5c1ccb..665ff46 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -144,6 +144,9 @@ public:
 	/** Run a script */
 	void runScript(const RivenScriptPtr &script, bool queue);
 
+	/** Are scripts running in the background */
+	bool hasQueuedScripts() const;
+
 	void stopAllScripts();
 
 	struct StoredMovieOpcode {


Commit: 286cdef658dbd18a6bb3b71fdfa9b133efb6a8e4
    https://github.com/scummvm/scummvm/commit/286cdef658dbd18a6bb3b71fdfa9b133efb6a8e4
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add basic mouse handling to RivenStack

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index c387772..e1581b0 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -197,7 +197,7 @@ void MohawkEngine_Riven::handleEvents() {
 	while (_eventMan->pollEvent(event)) {
 		switch (event.type) {
 		case Common::EVENT_MOUSEMOVE:
-			_card->onMouseMove(event.mouse);
+			_stack->onMouseMove(event.mouse);
 
 			if (!(getFeatures() & GF_DEMO)) {
 				// Check to show the inventory, but it is always "showing" in the demo
@@ -213,10 +213,10 @@ void MohawkEngine_Riven::handleEvents() {
 			if (_card->getCurHotspot()) {
 				checkSunnerAlertClick();
 			}
-			_card->onMouseDown(_eventMan->getMousePos());
+			_stack->onMouseDown(_eventMan->getMousePos());
 			break;
 		case Common::EVENT_LBUTTONUP:
-			_card->onMouseUp(_eventMan->getMousePos());
+			_stack->onMouseUp(_eventMan->getMousePos());
 			_inventory->checkClick(_eventMan->getMousePos());
 			break;
 		case Common::EVENT_KEYDOWN:
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 894cb4c..93f194c 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -346,37 +346,31 @@ RivenHotspot *RivenCard::getCurHotspot() const {
 	return _hoveredHotspot;
 }
 
-void RivenCard::onMouseDown(const Common::Point &mouse) {
-	onMouseMove(mouse);
+RivenScriptPtr RivenCard::onMouseDown(const Common::Point &mouse) {
+	RivenScriptPtr script = onMouseMove(mouse);
 
 	_pressedHotspot = _hoveredHotspot;
 
-	RivenScriptPtr script;
 	if (_pressedHotspot) {
-		script = _pressedHotspot->getScript(kMouseDownScript);
+		script += _pressedHotspot->getScript(kMouseDownScript);
 	}
 
-	if (script) {
-		_vm->_scriptMan->runScript(script, false);
-	}
+	return script;
 }
 
-void RivenCard::onMouseUp(const Common::Point &mouse) {
-	onMouseMove(mouse);
+RivenScriptPtr RivenCard::onMouseUp(const Common::Point &mouse) {
+	RivenScriptPtr script = onMouseMove(mouse);
 
-	RivenScriptPtr script;
 	if (_pressedHotspot && _pressedHotspot == _hoveredHotspot) {
-		script = _pressedHotspot->getScript(kMouseUpScript);
+		script += _pressedHotspot->getScript(kMouseUpScript);
 	}
 
 	_pressedHotspot = nullptr;
 
-	if (script) {
-		_vm->_scriptMan->runScript(script, false);
-	}
+	return script;
 }
 
-void RivenCard::onMouseMove(const Common::Point &mouse) {
+RivenScriptPtr RivenCard::onMouseMove(const Common::Point &mouse) {
 	RivenHotspot *hotspot = getHotspotContainingPoint(mouse);
 
 	RivenScriptPtr script = RivenScriptPtr(new RivenScript());
@@ -396,7 +390,7 @@ void RivenCard::onMouseMove(const Common::Point &mouse) {
 		_hoveredHotspot = nullptr;
 	}
 
-	_vm->_scriptMan->runScript(script, false);
+	return script;
 }
 
 void RivenCard::onMouseDragUpdate() {
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index b02f5f7..73c47b6 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -110,13 +110,13 @@ public:
 	void activateWaterEffect(uint16 index);
 
 	/** Handle a mouse down event */
-	void onMouseDown(const Common::Point &mouse);
+	RivenScriptPtr onMouseDown(const Common::Point &mouse);
 
 	/** Handle a mouse up event */
-	void onMouseUp(const Common::Point &mouse);
+	RivenScriptPtr onMouseUp(const Common::Point &mouse);
 
 	/** Handle a mouse move event */
-	void onMouseMove(const Common::Point &mouse);
+	RivenScriptPtr onMouseMove(const Common::Point &mouse);
 
 	/** Frame update handler for the mouse cursor */
 	void onMouseUpdate();
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index c394645..6fa1b15 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -36,7 +36,8 @@ namespace Mohawk {
 
 RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
 		_vm(vm),
-		_id(id) {
+		_id(id),
+		_mouseIsDown(false) {
 	loadResourceNames();
 	loadCardIdMap();
 	setCurrentStackVariable();
@@ -223,6 +224,46 @@ void RivenStack::installCardTimer() {
 
 }
 
+void RivenStack::onMouseDown(const Common::Point &mouse) {
+	_mouseIsDown = true;
+	_mousePosition = mouse;
+
+	if (_vm->getCard() && !_vm->_scriptMan->hasQueuedScripts()) {
+		_mouseDragStartPosition = mouse;
+
+		RivenScriptPtr script = _vm->getCard()->onMouseDown(mouse);
+
+		if (!script->empty()) {
+			_vm->_scriptMan->runScript(script, false);
+		}
+	}
+}
+
+void RivenStack::onMouseUp(const Common::Point &mouse) {
+	_mouseIsDown = false;
+	_mousePosition = mouse;
+
+	if (_vm->getCard() && !_vm->_scriptMan->hasQueuedScripts()) {
+		RivenScriptPtr script = _vm->getCard()->onMouseUp(mouse);
+
+		if (!script->empty()) {
+			_vm->_scriptMan->runScript(script, false);
+		}
+	}
+}
+
+void RivenStack::onMouseMove(const Common::Point &mouse) {
+	_mousePosition = mouse;
+
+	if (_vm->getCard() && !_vm->_scriptMan->hasQueuedScripts()) {
+		RivenScriptPtr script = _vm->getCard()->onMouseMove(mouse);
+
+		if (!script->empty()) {
+			_vm->_scriptMan->runScript(script, false);
+		}
+	}
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index e1e5455..b08052e 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -25,6 +25,7 @@
 
 #include "common/hash-str.h"
 #include "common/ptr.h"
+#include "common/rect.h"
 #include "common/str-array.h"
 
 namespace Mohawk {
@@ -110,6 +111,15 @@ public:
 	/** Install a timer for the current card if one is defined */
 	virtual void installCardTimer();
 
+	/** Handle a mouse down event */
+	void onMouseDown(const Common::Point &mouse);
+
+	/** Handle a mouse up event */
+	void onMouseUp(const Common::Point &mouse);
+
+	/** Handle a mouse move event */
+	void onMouseMove(const Common::Point &mouse);
+
 	// Common external commands
 	void xflies(uint16 argc, uint16 *argv); // Start the "flies" effect
 
@@ -139,7 +149,6 @@ private:
 	void loadCardIdMap();
 	void setCurrentStackVariable();
 
-
 	uint16 _id;
 
 	// Stack resource names
@@ -152,6 +161,10 @@ private:
 	Common::Array<uint32> _cardIdMap;
 
 	CommandsMap _commands;
+
+	bool _mouseIsDown;
+	Common::Point _mousePosition;
+	Common::Point _mouseDragStartPosition;
 };
 
 namespace RivenStacks {


Commit: 7a9b91dfcd786502f173e56b1d19e9d42acce5f3
    https://github.com/scummvm/scummvm/commit/7a9b91dfcd786502f173e56b1d19e9d42acce5f3
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Improve script debug output

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index e1581b0..116b1e8 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "common/config-manager.h"
+#include "common/debug-channels.h"
 #include "common/events.h"
 #include "common/keyboard.h"
 #include "common/translation.h"
@@ -67,6 +68,9 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_optionsDialog = nullptr;
 	_card = nullptr;
 	_inventory = nullptr;
+
+	DebugMan.addDebugChannel(kRivenDebugScript, "Script", "Track Script Execution");
+
 	removeTimer();
 
 	// NOTE: We can never really support CD swapping. All of the music files
@@ -95,6 +99,8 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
 	delete _optionsDialog;
 	delete _inventory;
 	delete _rnd;
+
+	DebugMan.clearAllDebugChannels();
 }
 
 GUI::Debugger *MohawkEngine_Riven::getDebugger() {
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 9e9f2b2..d44a340 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -69,6 +69,11 @@ enum RivenTransitionSpeed {
 	kRivenTransitionSpeedBest = 5003
 };
 
+// Engine Debug Flags
+enum {
+	kRivenDebugScript   = (1 << 0)
+};
+
 struct ZipMode {
 	Common::String name;
 	uint16 id;
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 22651fc..d77dbf5b2 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -28,8 +28,9 @@
 #include "mohawk/riven_sound.h"
 #include "mohawk/riven_stack.h"
 #include "mohawk/video.h"
-
 #include "common/memstream.h"
+
+#include "common/debug-channels.h"
 #include "common/stream.h"
 #include "common/system.h"
 
@@ -436,14 +437,12 @@ void RivenSimpleCommand::stopSound(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 13: set mouse cursor (cursor_id)
 void RivenSimpleCommand::changeCursor(uint16 op, uint16 argc, uint16 *argv) {
-	debug(2, "Change to cursor %d", argv[0]);
 	_vm->_cursor->setCursor(argv[0]);
 	_vm->_system->updateScreen();
 }
 
 // Command 14: pause script execution (delay in ms, u1)
 void RivenSimpleCommand::delay(uint16 op, uint16 argc, uint16 *argv) {
-	debug(2, "Delay %dms", argv[0]);
 	if (argv[0] > 0)
 		_vm->delayAndUpdate(argv[0]);
 }
@@ -466,19 +465,16 @@ void RivenSimpleCommand::transition(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 19: reload card
 void RivenSimpleCommand::refreshCard(uint16 op, uint16 argc, uint16 *argv) {
-	debug(2, "Refreshing card");
 	_vm->refreshCard();
 }
 
 // Command 20: begin screen update
 void RivenSimpleCommand::beginScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
-	debug(2, "Screen update disabled");
 	_vm->_gfx->beginScreenUpdate();
 }
 
 // Command 21: apply screen update
 void RivenSimpleCommand::applyScreenUpdate(uint16 op, uint16 argc, uint16 *argv) {
-	debug(2, "Screen update enabled");
 	_vm->_gfx->applyScreenUpdate();
 }
 
@@ -525,7 +521,6 @@ void RivenSimpleCommand::stopMovie(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 36: unknown
 void RivenSimpleCommand::unk_36(uint16 op, uint16 argc, uint16 *argv) {
-	debug(0, "unk_36: Ignoring");
 }
 
 // Command 37: fade ambient sounds
@@ -632,43 +627,53 @@ void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
 	_vm->_video->activateMLST(_vm->getCard()->getMovie(argv[0]));
 }
 
-void RivenSimpleCommand::dump(byte tabs) {
-	printTabs(tabs);
+Common::String RivenSimpleCommand::describe() const {
+	Common::String desc;
 
 	if (_type == 7) { // Use the variable name
 		Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
-		debugN("%s = %d;\n", varName.c_str(), _arguments[1]);
+		desc = Common::String::format("%s = %d", varName.c_str(), _arguments[1]);
 	} else if (_type == 17) { // Use the external command name
 		Common::String externalCommandName = _vm->getStack()->getName(kExternalCommandNames, _arguments[0]);
-		debugN("%s(", externalCommandName.c_str());
+		desc = Common::String::format("%s(", externalCommandName.c_str());
 		uint16 varCount = _arguments[1];
 		for (uint16 j = 0; j < varCount; j++) {
-			debugN("%d", _arguments[2 + j]);
+			desc += Common::String::format("%d", _arguments[2 + j]);
 			if (j != varCount - 1)
-				debugN(", ");
+				desc += ", ";
 		}
-		debugN(");\n");
+		desc += ")";
 	} else if (_type == 24) { // Use the variable name
 		Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
-		debugN("%s += %d;\n", varName.c_str(), _arguments[1]);
+		desc = Common::String::format("%s += %d", varName.c_str(), _arguments[1]);
 	} else {
-		debugN("%s(", _opcodes[_type].desc);
+		desc = Common::String::format("%s(", _opcodes[_type].desc);
 		for (uint16 j = 0; j < _arguments.size(); j++) {
-			debugN("%d", _arguments[j]);
+			desc += Common::String::format("%d", _arguments[j]);
 			if (j != _arguments.size() - 1)
-				debugN(", ");
+				desc += ", ";
 		}
-		debugN(");\n");
+		desc += ")";
 	}
+
+	return desc;
+}
+
+void RivenSimpleCommand::dump(byte tabs) {
+	printTabs(tabs);
+	debugN("%s;\n", describe().c_str());
 }
 
 void RivenSimpleCommand::execute() {
+	if (DebugMan.isDebugChannelEnabled(kRivenDebugScript)) {
+		debugC(kRivenDebugScript, "Running opcode: %s", describe().c_str());
+	}
+
 	uint16 *argValues = new uint16[_arguments.size()];
 
 	for (uint16 k = 0; k < _arguments.size(); k++)
 		argValues[k] = _arguments[k];
 
-	debug (4, "Running opcode %04x, argument count %d", _type, _arguments.size());
 	(this->*(_opcodes[_type].proc)) (_type, _arguments.size(), argValues);
 
 	delete[] argValues;
@@ -726,6 +731,11 @@ void RivenSwitchCommand::dump(byte tabs) {
 }
 
 void RivenSwitchCommand::execute() {
+	if (DebugMan.isDebugChannelEnabled(kRivenDebugScript)) {
+		Common::String varName = _vm->getStack()->getName(kVariableNames, _variableId);
+		debugC(kRivenDebugScript, "Running opcode: switch(%s)", varName.c_str());
+	}
+
 	// Get the switch variable value
 	uint32 value = _vm->getStackVar(_variableId);
 
@@ -767,6 +777,8 @@ RivenStackChangeCommand *RivenStackChangeCommand::createFromStream(MohawkEngine_
 }
 
 void RivenStackChangeCommand::execute() {
+	debugC(kRivenDebugScript, "Running opcode: changeStack(%d, %d)", _stackId, _cardId);
+
 	uint16 stackID;
 	if (_byStackId) {
 		stackID = _stackId;
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 665ff46..7eb8674 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -217,6 +217,7 @@ private:
 	RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments);
 
 	void setupOpcodes();
+	Common::String describe() const;
 
 	DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); }
 


Commit: 313d53234bb0036951b9e5b6353a294aef917e3a
    https://github.com/scummvm/scummvm/commit/313d53234bb0036951b9e5b6353a294aef917e3a
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Be case insensitive when matching resource names

Changed paths:
    engines/mohawk/resource.cpp


diff --git a/engines/mohawk/resource.cpp b/engines/mohawk/resource.cpp
index 4a4b78e..0ef0a6a 100644
--- a/engines/mohawk/resource.cpp
+++ b/engines/mohawk/resource.cpp
@@ -74,7 +74,7 @@ bool Archive::hasResource(uint32 tag, const Common::String &resName) const {
 	const ResourceMap &resMap = _types[tag];
 
 	for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++)
-		if (it->_value.name.matchString(resName))
+		if (it->_value.name.matchString(resName, true))
 			return true;
 
 	return false;
@@ -113,7 +113,7 @@ uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const
 	const ResourceMap &resMap = _types[tag];
 
 	for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++)
-		if (it->_value.name.matchString(resName))
+		if (it->_value.name.matchString(resName, true))
 			return it->_key;
 
 	return 0xFFFF;


Commit: 14990dc91b868816914fd731586e60bb297caeea
    https://github.com/scummvm/scummvm/commit/14990dc91b868816914fd731586e60bb297caeea
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Remove a hack that should not be needed anymore

The script execution order should now be accurate

Changed paths:
    engines/mohawk/riven_scripts.cpp


diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index d77dbf5b2..27f0022 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -579,12 +579,8 @@ void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 40: activate SLST record (card ambient sound lists)
 void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
-	// WORKAROUND: Disable the SLST that is played during Riven's intro.
-	// Riven X does this too (spoke this over with Jeff)
-	if (_vm->getStack()->getId() == kStackTspit && _vm->getStack()->getCurrentCardGlobalId() == 0x6e9a && argv[0] == 2)
-		return;
-
 	_vm->_activatedSLST = true;
+
 	SLSTRecord slstRecord = _vm->getCard()->getSound(argv[0]);
 	_vm->_sound->playSLST(slstRecord);
 }


Commit: f334e6e38ae4bc65d398fa174a8be892cb403063
    https://github.com/scummvm/scummvm/commit/f334e6e38ae4bc65d398fa174a8be892cb403063
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add sound effect related methods

Changed paths:
    engines/mohawk/riven_sound.cpp
    engines/mohawk/riven_sound.h


diff --git a/engines/mohawk/riven_sound.cpp b/engines/mohawk/riven_sound.cpp
index 569bbb4..354ba2d 100644
--- a/engines/mohawk/riven_sound.cpp
+++ b/engines/mohawk/riven_sound.cpp
@@ -68,6 +68,11 @@ void RivenSoundManager::playSound(uint16 id, uint16 volume, bool playOnDraw) {
 	}
 }
 
+void RivenSoundManager::playSound(const Common::String &name, uint16 volume, bool playOnDraw) {
+	uint16 id =_vm->findResourceID(ID_TWAV, name);
+	playSound(id, volume, playOnDraw);
+}
+
 void RivenSoundManager::playSLST(const SLSTRecord &slstRecord) {
 	if (slstRecord.soundIds.empty()) {
 		return;
@@ -298,6 +303,10 @@ bool RivenSoundManager::fadeBalance(RivenSoundManager::AmbientSound &ambientSoun
 	}
 }
 
+bool RivenSoundManager::isEffectPlaying() const {
+	return _effect != nullptr && _effect->isPlaying();
+}
+
 RivenSound::RivenSound(MohawkEngine *vm, Audio::RewindableAudioStream *rewindStream) :
 		_vm(vm),
 		_volume(Audio::Mixer::kMaxChannelVolume),
diff --git a/engines/mohawk/riven_sound.h b/engines/mohawk/riven_sound.h
index d4e7872..bd9237d 100644
--- a/engines/mohawk/riven_sound.h
+++ b/engines/mohawk/riven_sound.h
@@ -73,13 +73,19 @@ public:
 	 *
 	 * @param id Sound ID in the stack
 	 * @param volume Playback volume, between 0 and 255
-	 * @param playOnDraw Start playing when the current card is drawn instead of immediatly
+	 * @param playOnDraw Start playing when the current card is drawn instead of immediately
 	 */
 	void playSound(uint16 id, uint16 volume = 255, bool playOnDraw = false);
 
+	/** Play an effect sound by its resource name */
+	void playSound(const Common::String &name, uint16 volume = 255, bool playOnDraw = false);
+
 	/** Start playing the scheduled on-draw effect sound, if any. Called by the GraphicsManager. */
 	void triggerDrawSound();
 
+	/** Is an effect sound currently playing? */
+	bool isEffectPlaying() const;
+
 	/** Stop playing the current effect sound, if any */
 	void stopSound();
 


Commit: 7609ec0de81d7be4ed6d0e3e6e0303f42d54ad52
    https://github.com/scummvm/scummvm/commit/7609ec0de81d7be4ed6d0e3e6e0303f42d54ad52
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Use explicit bitmap names for the dome sliders

Changed paths:
    engines/mohawk/riven_stacks/bspit.cpp
    engines/mohawk/riven_stacks/domespit.cpp
    engines/mohawk/riven_stacks/domespit.h
    engines/mohawk/riven_stacks/gspit.cpp
    engines/mohawk/riven_stacks/jspit.cpp
    engines/mohawk/riven_stacks/pspit.cpp
    engines/mohawk/riven_stacks/tspit.cpp


diff --git a/engines/mohawk/riven_stacks/bspit.cpp b/engines/mohawk/riven_stacks/bspit.cpp
index acc24f2..7a8ac69 100644
--- a/engines/mohawk/riven_stacks/bspit.cpp
+++ b/engines/mohawk/riven_stacks/bspit.cpp
@@ -33,7 +33,7 @@ namespace Mohawk {
 namespace RivenStacks {
 
 BSpit::BSpit(MohawkEngine_Riven *vm) :
-		DomeSpit(vm, kStackBspit) {
+		DomeSpit(vm, kStackBspit, "bSliders.190", "bSliderBG.190") {
 
 	REGISTER_COMMAND(BSpit, xblabopenbook);
 	REGISTER_COMMAND(BSpit, xblabbookprevpage);
diff --git a/engines/mohawk/riven_stacks/domespit.cpp b/engines/mohawk/riven_stacks/domespit.cpp
index 71a82ab..6a1d26b 100644
--- a/engines/mohawk/riven_stacks/domespit.cpp
+++ b/engines/mohawk/riven_stacks/domespit.cpp
@@ -35,8 +35,10 @@ namespace RivenStacks {
 static const uint32 kDomeSliderDefaultState = 0x01F00000;
 static const uint32 kDomeSliderSlotCount = 25;
 
-DomeSpit::DomeSpit(MohawkEngine_Riven *vm, uint16 id) :
-		RivenStack(vm, id) {
+DomeSpit::DomeSpit(MohawkEngine_Riven *vm, uint16 id, const char *sliderBmpName, const char *sliderBgBmpName) :
+		RivenStack(vm, id),
+		_sliderBmpName(sliderBmpName),
+		_sliderBgBmpName(sliderBgBmpName) {
 	_sliderState = kDomeSliderDefaultState;
 }
 
@@ -204,7 +206,8 @@ void DomeSpit::drawDomeSliders(uint16 startHotspot) {
 		dstAreaRect.translate(-2, 0);
 
 	// Find out bitmap id
-	uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
+	uint16 bitmapId = _vm->findResourceID(ID_TBMP, buildCardResourceName(_sliderBmpName));
+	uint16 bgBitmapId = _vm->findResourceID(ID_TBMP, buildCardResourceName(_sliderBgBmpName));
 
 	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
 		RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(startHotspot + i);
@@ -217,12 +220,16 @@ void DomeSpit::drawDomeSliders(uint16 startHotspot) {
 		if (_sliderState & (1 << (24 - i)))
 			_vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect);
 		else
-			_vm->_gfx->drawImageRect(bitmapId + 1, srcRect, dstRect);
+			_vm->_gfx->drawImageRect(bgBitmapId, srcRect, dstRect);
 	}
 
 	_vm->_gfx->updateScreen();
 }
 
+Common::String DomeSpit::buildCardResourceName(const Common::String &name) const {
+	return Common::String::format("%d_%s", _vm->getCard()->getId(), name.c_str());
+}
+
 void DomeSpit::setDomeSliderState(uint32 sliderState) {
 	_sliderState = sliderState;
 }
diff --git a/engines/mohawk/riven_stacks/domespit.h b/engines/mohawk/riven_stacks/domespit.h
index 7ca513c..98cf37c 100644
--- a/engines/mohawk/riven_stacks/domespit.h
+++ b/engines/mohawk/riven_stacks/domespit.h
@@ -30,7 +30,7 @@ namespace RivenStacks {
 
 class DomeSpit : public RivenStack {
 public:
-	DomeSpit(MohawkEngine_Riven *vm, uint16 id);
+	DomeSpit(MohawkEngine_Riven *vm, uint16 id, const char *sliderBmpName, const char *sliderBgBmpName);
 
 	uint32 getDomeSliderState() const;
 	void setDomeSliderState(uint32 sliderState);
@@ -43,8 +43,11 @@ protected:
 	void checkSliderCursorChange(uint16 startHotspot);
 	void dragDomeSlider(uint16 soundId, uint16 startHotspot);
 	void drawDomeSliders(uint16 startHotspot);
+	Common::String buildCardResourceName(const Common::String &name) const;
 
 	uint32 _sliderState;
+	Common::String _sliderBmpName;
+	Common::String _sliderBgBmpName;
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/gspit.cpp b/engines/mohawk/riven_stacks/gspit.cpp
index a53d0a1..836dd27 100644
--- a/engines/mohawk/riven_stacks/gspit.cpp
+++ b/engines/mohawk/riven_stacks/gspit.cpp
@@ -33,7 +33,7 @@ namespace Mohawk {
 namespace RivenStacks {
 
 GSpit::GSpit(MohawkEngine_Riven *vm) :
-		DomeSpit(vm, kStackGspit) {
+		DomeSpit(vm, kStackGspit, "gsliders.190", "gsliderbg.190") {
 
 	REGISTER_COMMAND(GSpit, xgresetpins);
 	REGISTER_COMMAND(GSpit, xgrotatepins);
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
index 5ac65b3..7dc5210 100644
--- a/engines/mohawk/riven_stacks/jspit.cpp
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -33,7 +33,7 @@ namespace Mohawk {
 namespace RivenStacks {
 
 JSpit::JSpit(MohawkEngine_Riven *vm) :
-		DomeSpit(vm, kStackJspit) {
+		DomeSpit(vm, kStackJspit, "jsliders.190", "jsliderbg.190") {
 
 	REGISTER_COMMAND(JSpit, xreseticons);
 	REGISTER_COMMAND(JSpit, xicon);
diff --git a/engines/mohawk/riven_stacks/pspit.cpp b/engines/mohawk/riven_stacks/pspit.cpp
index 4c80849..e2bfec0 100644
--- a/engines/mohawk/riven_stacks/pspit.cpp
+++ b/engines/mohawk/riven_stacks/pspit.cpp
@@ -31,7 +31,7 @@ namespace Mohawk {
 namespace RivenStacks {
 
 PSpit::PSpit(MohawkEngine_Riven *vm) :
-		DomeSpit(vm, kStackPspit) {
+		DomeSpit(vm, kStackPspit, "psliders.25", "psliderbg.25") {
 
 	REGISTER_COMMAND(PSpit, xpisland990_elevcombo);
 	REGISTER_COMMAND(PSpit, xpscpbtn);
diff --git a/engines/mohawk/riven_stacks/tspit.cpp b/engines/mohawk/riven_stacks/tspit.cpp
index e4d5179..1903a89 100644
--- a/engines/mohawk/riven_stacks/tspit.cpp
+++ b/engines/mohawk/riven_stacks/tspit.cpp
@@ -34,7 +34,7 @@ namespace Mohawk {
 namespace RivenStacks {
 
 TSpit::TSpit(MohawkEngine_Riven *vm) :
-		DomeSpit(vm, kStackTspit) {
+		DomeSpit(vm, kStackTspit, "tsliders.190", "tsliderbg.190") {
 
 	REGISTER_COMMAND(TSpit, xtexterior300_telescopedown);
 	REGISTER_COMMAND(TSpit, xtexterior300_telescopeup);


Commit: f0267d542f860a2f67f519a1dc5343e020927c81
    https://github.com/scummvm/scummvm/commit/f0267d542f860a2f67f519a1dc5343e020927c81
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Keep turning pages while the mouse is pressed in Atrus' book

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_inventory.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h
    engines/mohawk/riven_stacks/aspit.cpp
    engines/mohawk/riven_stacks/aspit.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 116b1e8..1c40acd 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -185,12 +185,12 @@ Common::Error MohawkEngine_Riven::run() {
 
 
 	while (!shouldQuit())
-		handleEvents();
+		doFrame();
 
 	return Common::kNoError;
 }
 
-void MohawkEngine_Riven::handleEvents() {
+void MohawkEngine_Riven::doFrame() {
 	// Update background running things
 	checkTimer();
 	_sound->updateSLST();
@@ -277,6 +277,12 @@ void MohawkEngine_Riven::handleEvents() {
 
 	_card->onMouseUpdate();
 
+	if (!_scriptMan->runningQueuedScripts()) {
+		// Don't run queued scripts if we are calling from a queued script
+		// otherwise infinite looping will happen.
+		_scriptMan->runQueuedScripts();
+	}
+
 	// Update the screen if we need to
 	if (needsUpdate)
 		_system->updateScreen();
@@ -553,6 +559,22 @@ bool MohawkEngine_Riven::isZipVisitedCard(const Common::String &hotspotName) con
 	return foundMatch;
 }
 
+bool MohawkEngine_Riven::canLoadGameStateCurrently() {
+	return !(getFeatures() & GF_DEMO);
+}
+
+bool MohawkEngine_Riven::canSaveGameStateCurrently() {
+	if (getFeatures() & GF_DEMO) {
+		return false;
+	}
+
+	if (_scriptMan->hasQueuedScripts()) {
+		return false;
+	}
+
+	return true;
+}
+
 bool ZipMode::operator== (const ZipMode &z) const {
 	return z.name == name && z.id == id;
 }
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index d44a340..0dfba15 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -98,8 +98,8 @@ public:
 
 	GUI::Debugger *getDebugger();
 
-	bool canLoadGameStateCurrently() { return !(getFeatures() & GF_DEMO); }
-	bool canSaveGameStateCurrently() { return !(getFeatures() & GF_DEMO); }
+	bool canLoadGameStateCurrently();
+	bool canSaveGameStateCurrently();
 	Common::Error loadGameState(int slot);
 	Common::Error saveGameState(int slot, const Common::String &desc);
 	bool hasFeature(EngineFeature f) const;
@@ -110,6 +110,7 @@ public:
 		new Common::Functor0Mem<void, cls>(this, &cls::method)
 
 	void doVideoTimer(VideoHandle handle, bool force);
+	void doFrame();
 
 private:
 	MohawkArchive *_extrasFile; // We need a separate handle for the extra data
@@ -121,7 +122,6 @@ private:
 	// Stack/Card-related functions and variables
 	RivenCard *_card;
 	RivenStack *_stack;
-	void handleEvents();
 
 	// Hotspot related functions and variables
 	bool _showHotspots;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 93f194c..09c72d5 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -396,7 +396,7 @@ RivenScriptPtr RivenCard::onMouseMove(const Common::Point &mouse) {
 void RivenCard::onMouseDragUpdate() {
 	if (_pressedHotspot) {
 		RivenScriptPtr script = _pressedHotspot->getScript(kMouseDragScript);
-		_vm->_scriptMan->runScript(script, false);
+		_vm->_scriptMan->runScript(script, true);
 	}
 }
 
@@ -407,7 +407,7 @@ void RivenCard::onMouseUpdate() {
 	}
 
 	if (!script->empty()) {
-		_vm->_scriptMan->runScript(script, false);
+		_vm->_scriptMan->runScript(script, true);
 	} else {
 		updateMouseCursor();
 	}
diff --git a/engines/mohawk/riven_inventory.cpp b/engines/mohawk/riven_inventory.cpp
index 11a42f0..f4f3df0 100644
--- a/engines/mohawk/riven_inventory.cpp
+++ b/engines/mohawk/riven_inventory.cpp
@@ -196,7 +196,7 @@ void RivenInventory::backFromItemScript() const {
 	// Return to where we were before entering the book
 	RivenCommand *back = new RivenStackChangeCommand(_vm, backStackId, backCardId, true);
 	RivenScriptPtr backScript = _vm->_scriptMan->createScriptWithCommand(back);
-	_vm->_scriptMan->runScript(backScript, false);
+	_vm->_scriptMan->runScript(backScript, true);
 }
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 27f0022..7b95abb 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -41,8 +41,10 @@ static void printTabs(byte tabs) {
 		debugN("\t");
 }
 
-RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) {
-	_vm = vm;
+RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) :
+		_vm(vm),
+		_runningQueuedScripts(false) {
+
 	_storedMovieOpcode.time = 0;
 	_storedMovieOpcode.id = 0;
 }
@@ -129,6 +131,18 @@ bool RivenScriptManager::hasQueuedScripts() const {
 	return !_queue.empty();
 }
 
+void RivenScriptManager::runQueuedScripts() {
+	_runningQueuedScripts = true;
+
+	for (uint i = 0; i < _queue.size(); i++) {
+		_queue[i]->run();
+	}
+
+	_queue.clear();
+
+	_runningQueuedScripts = false;
+}
+
 RivenScriptPtr RivenScriptManager::createScriptFromData(uint16 commandCount, ...) {
 	va_list args;
 	va_start(args, commandCount);
@@ -169,6 +183,10 @@ RivenScriptPtr RivenScriptManager::createScriptWithCommand(RivenCommand *command
 	return script;
 }
 
+bool RivenScriptManager::runningQueuedScripts() const {
+	return _runningQueuedScripts;
+}
+
 RivenScript::RivenScript() {
 	_continueRunning = true;
 }
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 7eb8674..1a990c4 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -118,7 +118,7 @@ typedef Common::Array<RivenTypedScript> RivenScriptList;
  * Script manager
  *
  * Reads scripts from raw data.
- * Can run scripts immediatly, or store them for future execution.
+ * Can run scripts immediately, or store them for future execution.
  */
 class RivenScriptManager {
 public:
@@ -147,6 +147,11 @@ public:
 	/** Are scripts running in the background */
 	bool hasQueuedScripts() const;
 
+	/** Run queued scripts */
+	void runQueuedScripts();
+
+	bool runningQueuedScripts() const;
+
 	void stopAllScripts();
 
 	struct StoredMovieOpcode {
@@ -165,6 +170,8 @@ private:
 	MohawkEngine_Riven *_vm;
 
 	Common::Array<RivenScriptPtr> _queue;
+	bool _runningQueuedScripts;
+
 	StoredMovieOpcode _storedMovieOpcode;
 
 	RivenCommandPtr readCommand(Common::ReadStream *stream);
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index 6fa1b15..d842bca 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -234,7 +234,7 @@ void RivenStack::onMouseDown(const Common::Point &mouse) {
 		RivenScriptPtr script = _vm->getCard()->onMouseDown(mouse);
 
 		if (!script->empty()) {
-			_vm->_scriptMan->runScript(script, false);
+			_vm->_scriptMan->runScript(script, true);
 		}
 	}
 }
@@ -247,7 +247,7 @@ void RivenStack::onMouseUp(const Common::Point &mouse) {
 		RivenScriptPtr script = _vm->getCard()->onMouseUp(mouse);
 
 		if (!script->empty()) {
-			_vm->_scriptMan->runScript(script, false);
+			_vm->_scriptMan->runScript(script, true);
 		}
 	}
 }
@@ -259,11 +259,15 @@ void RivenStack::onMouseMove(const Common::Point &mouse) {
 		RivenScriptPtr script = _vm->getCard()->onMouseMove(mouse);
 
 		if (!script->empty()) {
-			_vm->_scriptMan->runScript(script, false);
+			_vm->_scriptMan->runScript(script, true);
 		}
 	}
 }
 
+bool RivenStack::mouseIsDown() const {
+	return _mouseIsDown;
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index b08052e..d30702e 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -120,6 +120,8 @@ public:
 	/** Handle a mouse move event */
 	void onMouseMove(const Common::Point &mouse);
 
+	bool mouseIsDown() const;
+
 	// Common external commands
 	void xflies(uint16 argc, uint16 *argv); // Start the "flies" effect
 
diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp
index aabd7db..f930dfc 100644
--- a/engines/mohawk/riven_stacks/aspit.cpp
+++ b/engines/mohawk/riven_stacks/aspit.cpp
@@ -99,44 +99,79 @@ void ASpit::xaatrusbookback(uint16 argc, uint16 *argv) {
 	_vm->_inventory->backFromItemScript();
 }
 
-void ASpit::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
-	// Get the page variable
-	uint32 &page = _vm->_vars["aatruspage"];
+bool ASpit::pageTurn(int16 transition) {
+	// Wait until the previous page turn sound completes
+	while (_vm->_sound->isEffectPlaying() && !_vm->shouldQuit()) {
+		if (!mouseIsDown()) {
+			return false;
+		}
 
-	// Decrement the page if it's not the first page
-	if (page == 1)
-		return;
-	page--;
+		_vm->doFrame();
+	}
 
 	// Play the page turning sound
-	if (_vm->getFeatures() & GF_DEMO)
-		_vm->_sound->playSound(4);
+	const char *soundName = nullptr;
+	if (_vm->_rnd->getRandomBit())
+		soundName = "aPage1";
 	else
-		_vm->_sound->playSound(3);
+		soundName = "aPage2";
+
+	Common::String fullSoundName = Common::String::format("%d_%s_1", _vm->getCard()->getId(), soundName);
+
+	_vm->_sound->playSound(fullSoundName, 51, true);
 
 	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(1);
-	_vm->getCard()->drawPicture(page);
+	_vm->_gfx->scheduleTransition(transition);
+
+	return true;
+}
+
+void ASpit::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
+	// Get the page variable
+	uint32 &page = _vm->_vars["aatruspage"];
+
+	// Keep turning pages while the mouse is pressed
+	bool firstPageTurn = true;
+	while (mouseIsDown() || firstPageTurn) {
+		// Check for the first page
+		if (page == 1)
+			return;
+
+		if (!pageTurn(1)) {
+			return;
+		}
+
+		// Update the page number
+		page--;
+		firstPageTurn = false;
+
+		_vm->getCard()->drawPicture(page);
+		_vm->doFrame();
+	}
 }
 
 void ASpit::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
 	// Get the page variable
 	uint32 &page = _vm->_vars["aatruspage"];
 
-	// Increment the page if it's not the last page
-	if (((_vm->getFeatures() & GF_DEMO) && page == 6) || page == 10)
-		return;
-	page++;
+	// Keep turning pages while the mouse is pressed
+	bool firstPageTurn = true;
+	while ((mouseIsDown() || firstPageTurn) && !_vm->shouldQuit()) {
+		// Check for the last page
+		if (((_vm->getFeatures() & GF_DEMO) && page == 6) || page == 10)
+			return;
 
-	// Play the page turning sound
-	if (_vm->getFeatures() & GF_DEMO)
-		_vm->_sound->playSound(5);
-	else
-		_vm->_sound->playSound(4);
+		if (!pageTurn(0)) {
+			return;
+		}
 
-	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(0);
-	_vm->getCard()->drawPicture(page);
+		// Update the page number
+		page++;
+		firstPageTurn = false;
+
+		_vm->getCard()->drawPicture(page);
+		_vm->doFrame();
+	}
 }
 
 void ASpit::xacathopenbook(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_stacks/aspit.h b/engines/mohawk/riven_stacks/aspit.h
index 60400dd..d64214d 100644
--- a/engines/mohawk/riven_stacks/aspit.h
+++ b/engines/mohawk/riven_stacks/aspit.h
@@ -67,6 +67,9 @@ public:
 	void xaenablemenuintro(uint16 argc, uint16 *argv);
 	void xademoquit(uint16 argc, uint16 *argv);
 	void xaexittomain(uint16 argc, uint16 *argv);
+
+private:
+	bool pageTurn(int16 transition);
 };
 
 } // End of namespace RivenStacks


Commit: ae6f248616a144050337e9687033402c4868d558
    https://github.com/scummvm/scummvm/commit/ae6f248616a144050337e9687033402c4868d558
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Move Riven's sunner alert handling to the jungle stack

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h
    engines/mohawk/riven_stacks/jspit.cpp
    engines/mohawk/riven_stacks/jspit.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 1c40acd..197065b 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -216,9 +216,6 @@ void MohawkEngine_Riven::doFrame() {
 			needsUpdate = true;
 			break;
 		case Common::EVENT_LBUTTONDOWN:
-			if (_card->getCurHotspot()) {
-				checkSunnerAlertClick();
-			}
 			_stack->onMouseDown(_eventMan->getMousePos());
 			break;
 		case Common::EVENT_LBUTTONUP:
@@ -507,34 +504,6 @@ void MohawkEngine_Riven::doVideoTimer(VideoHandle handle, bool force) {
 		_scriptMan->runStoredMovieOpcode();
 }
 
-void MohawkEngine_Riven::checkSunnerAlertClick() {
-	// We need to do a manual hardcoded check for the sunners'
-	// alert movies.
-
-	uint32 &sunners = _vars["jsunners"];
-
-	// If the sunners are gone, there's nothing for us to do
-	if (sunners != 0)
-		return;
-
-	uint32 rmapCode = _stack->getCurrentCardGlobalId();
-
-	// This is only for the mid/lower staircase sections
-	if (rmapCode != 0x79bd && rmapCode != 0x7beb)
-		return;
-
-	// Only set the sunners variable on the forward hotspot
-	if (_card->getCurHotspot()->getBlstId() != 3)
-		return;
-
-	// If the alert video is no longer playing, we have nothing left to do
-	VideoEntryPtr video = _video->findVideoRiven(1);
-	if (!video || video->endOfVideo())
-		return;
-
-	sunners = 1;
-}
-
 void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
 	Common::String cardName = getStack()->getName(kCardNames, cardNameId);
 	if (cardName.empty())
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 0dfba15..a4c4db4 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -133,9 +133,6 @@ private:
 	Common::SharedPtr<TimerProc> _timerProc;
 	uint32 _timerTime;
 
-	// Miscellaneous
-	void checkSunnerAlertClick();
-
 public:
 	// Stack/card/script funtions
 	void changeToCard(uint16 dest);
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index d842bca..f8dd6a3 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -268,6 +268,10 @@ bool RivenStack::mouseIsDown() const {
 	return _mouseIsDown;
 }
 
+void RivenStack::mouseForceUp() {
+	_mouseIsDown = false;
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index d30702e..2365859 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -120,8 +120,12 @@ public:
 	/** Handle a mouse move event */
 	void onMouseMove(const Common::Point &mouse);
 
+	/** Is the left mouse button currently pressed? */
 	bool mouseIsDown() const;
 
+	/** Force the left mouse button to be considered unpressed until the next mouse click */
+	void mouseForceUp();
+
 	// Common external commands
 	void xflies(uint16 argc, uint16 *argv); // Start the "flies" effect
 
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
index 7dc5210..f4b0c0f 100644
--- a/engines/mohawk/riven_stacks/jspit.cpp
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -413,9 +413,17 @@ void JSpit::xjplaybeetle_1450(uint16 argc, uint16 *argv) {
 
 void JSpit::xjlagoon700_alert(uint16 argc, uint16 *argv) {
 	// Handle sunner reactions (mid-staircase)
+	uint32 sunners = _vm->_vars["jsunners"];
 
-	if (_vm->_vars["jsunners"] == 0)
-		_vm->_video->playMovieRiven(1);
+	// If the sunners are gone, there's nothing for us to do
+	if (sunners != 0) {
+		return;
+	}
+
+	VideoEntryPtr sunnerAlertVideo = _vm->_video->playMovieRiven(1);
+
+	// Wait for a click while the alert video is playing
+	sunnersPlayVideo(sunnerAlertVideo, 0x7BEB);
 }
 
 void JSpit::xjlagoon800_alert(uint16 argc, uint16 *argv) {
@@ -425,7 +433,10 @@ void JSpit::xjlagoon800_alert(uint16 argc, uint16 *argv) {
 
 	if (sunners == 0) {
 		// Show the sunners alert video
-		_vm->_video->playMovieRiven(1);
+		VideoEntryPtr sunnerAlertVideo = _vm->_video->playMovieRiven(1);
+
+		// Wait for a click while the alert video is playing
+		sunnersPlayVideo(sunnerAlertVideo, 0xB6CA);
 	} else if (sunners == 1) {
 		// Show the sunners leaving if you moved forward in their "alert" status
 		_vm->_video->playMovieBlockingRiven(2);
@@ -451,6 +462,25 @@ void JSpit::xjlagoon1500_alert(uint16 argc, uint16 *argv) {
 	}
 }
 
+void JSpit::sunnersPlayVideo(VideoEntryPtr &video, uint32 destCardGlobalId) {
+	uint32 &sunners = _vm->_vars["jsunners"];
+
+	mouseForceUp();
+	while (!video->endOfVideo() && !_vm->shouldQuit()) {
+		_vm->doFrame();
+
+		if (mouseIsDown()) {
+			video->stop();
+			sunners = 1;
+
+			uint16 destCardId = getCardStackId(destCardGlobalId);
+			RivenScriptPtr clickScript = _vm->_scriptMan->createScriptFromData(1, 2, 1, destCardId);
+			_vm->_scriptMan->runScript(clickScript, false);
+			break;
+		}
+	}
+}
+
 void JSpit::sunnersTopStairsTimer() {
 	// If the sunners are gone, we have no video to play
 	if (_vm->_vars["jsunners"] != 0) {
diff --git a/engines/mohawk/riven_stacks/jspit.h b/engines/mohawk/riven_stacks/jspit.h
index bf91588..9ce32bc 100644
--- a/engines/mohawk/riven_stacks/jspit.h
+++ b/engines/mohawk/riven_stacks/jspit.h
@@ -24,6 +24,7 @@
 #define RIVEN_STACKS_JSPIT_H
 
 #include "mohawk/riven_stacks/domespit.h"
+#include "mohawk/video.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -92,6 +93,8 @@ public:
 private:
 	int jspitElevatorLoop();
 	void redrawWharkNumberPuzzle(uint16 overlay, uint16 number);
+
+	void sunnersPlayVideo(VideoEntryPtr &video, uint32 destCardGlobalId);
 };
 
 } // End of namespace RivenStacks


Commit: 1286e7fcf0d46dd2887fa466df74f3652f7af1df
    https://github.com/scummvm/scummvm/commit/1286e7fcf0d46dd2887fa466df74f3652f7af1df
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Use an enum for Riven's transition types

Changed paths:
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_stacks/aspit.cpp
    engines/mohawk/riven_stacks/aspit.h
    engines/mohawk/riven_stacks/bspit.cpp
    engines/mohawk/riven_stacks/jspit.cpp
    engines/mohawk/riven_stacks/ospit.cpp


diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 7fa35dd..60b8c5d 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -49,7 +49,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 
 	_screenUpdateNesting = 0;
 	_screenUpdateRunning = false;
-	_scheduledTransition = -1;	// no transition
+	_scheduledTransition = kRivenTransitionNone;
 	_dirtyScreen = false;
 
 	_creditsImage = 302;
@@ -201,7 +201,7 @@ bool RivenGraphics::runScheduledWaterEffects() {
 	return false;
 }
 
-void RivenGraphics::scheduleTransition(uint16 id, Common::Rect rect) {
+void RivenGraphics::scheduleTransition(RivenTransition id, Common::Rect rect) {
 	_scheduledTransition = id;
 	_transitionRect = rect;
 }
@@ -217,16 +217,16 @@ void RivenGraphics::runScheduledTransition() {
 	// transitions were found by hacking scripts.
 
 	switch (_scheduledTransition) {
-	case 0:  // Swipe Left
-	case 1:  // Swipe Right
-	case 2:  // Swipe Up
-	case 3:  // Swipe Down
-	case 12: // Pan Left
-	case 13: // Pan Right
-	case 14: // Pan Up
-	case 15: // Pan Down
-	case 16: // Dissolve
-	case 17: // Dissolve (tspit CARD 155)
+	case kRivenTransitionWipeLeft:
+	case kRivenTransitionWipeRight:
+	case kRivenTransitionWipeUp:
+	case kRivenTransitionWipeDown:
+	case kRivenTransitionPanLeft:
+	case kRivenTransitionPanRight:
+	case kRivenTransitionPanUp:
+	case kRivenTransitionPanDown:
+	case kRivenTransitionBlend:
+	case kRivenTransitionBlend2: // (tspit CARD 155)
 		break;
 	default:
 		if (_scheduledTransition >= 4 && _scheduledTransition <= 11)
@@ -240,7 +240,7 @@ void RivenGraphics::runScheduledTransition() {
 	_vm->_system->copyRectToScreen(_effectScreen->getBasePtr(0, 0), _effectScreen->pitch, 0, 0, _effectScreen->w, _effectScreen->h);
 	_vm->_system->updateScreen();
 
-	_scheduledTransition = -1; // Clear scheduled transition
+	_scheduledTransition = kRivenTransitionNone; // Clear scheduled transition
 }
 
 void RivenGraphics::clearMainScreen() {
@@ -250,7 +250,7 @@ void RivenGraphics::clearMainScreen() {
 void RivenGraphics::fadeToBlack() {
 	// The transition speed is forced to best here
 	setTransitionSpeed(kRivenTransitionSpeedBest);
-	scheduleTransition(16);
+	scheduleTransition(kRivenTransitionBlend);
 	clearMainScreen();
 	runScheduledTransition();
 }
@@ -324,7 +324,7 @@ void RivenGraphics::updateCredits() {
 
 	if (_creditsImage < 304) {
 		// For the first two credit images, they are faded from black to the image and then out again
-		scheduleTransition(16);
+		scheduleTransition(kRivenTransitionBlend);
 
 		Graphics::Surface *frame = findImage(_creditsImage++)->getSurface();
 
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 76bcae5..18b1e1d 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -30,6 +30,20 @@ namespace Mohawk {
 class MohawkEngine_Riven;
 class FliesEffect;
 
+enum RivenTransition {
+	kRivenTransitionNone      = -1,
+	kRivenTransitionWipeLeft  = 0,
+	kRivenTransitionWipeRight = 1,
+	kRivenTransitionWipeUp    = 2,
+	kRivenTransitionWipeDown  = 3,
+	kRivenTransitionPanLeft   = 12,
+	kRivenTransitionPanRight  = 13,
+	kRivenTransitionPanUp     = 14,
+	kRivenTransitionPanDown   = 15,
+	kRivenTransitionBlend     = 16,
+	kRivenTransitionBlend2    = 17
+};
+
 class RivenGraphics : public GraphicsManager {
 public:
 	RivenGraphics(MohawkEngine_Riven *vm);
@@ -60,7 +74,7 @@ public:
 	void runFliesEffect();
 
 	// Transitions
-	void scheduleTransition(uint16 id, Common::Rect rect = Common::Rect(0, 0, 608, 392));
+	void scheduleTransition(RivenTransition id, Common::Rect rect = Common::Rect(0, 0, 608, 392));
 	void runScheduledTransition();
 	void fadeToBlack();
 	void setTransitionSpeed(uint32 speed) { _transitionSpeed = speed; }
@@ -98,7 +112,7 @@ private:
 	FliesEffect *_fliesEffect;
 
 	// Transitions
-	int16 _scheduledTransition;
+	RivenTransition _scheduledTransition;
 	Common::Rect _transitionRect;
 	uint32 _transitionSpeed;
 
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 7b95abb..ad7196d 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -476,9 +476,9 @@ void RivenSimpleCommand::runExternalCommand(uint16 op, uint16 argc, uint16 *argv
 // Parameters 1-4: transition rectangle
 void RivenSimpleCommand::transition(uint16 op, uint16 argc, uint16 *argv) {
 	if (argc == 1)
-		_vm->_gfx->scheduleTransition(argv[0]);
+		_vm->_gfx->scheduleTransition((RivenTransition) argv[0]);
 	else
-		_vm->_gfx->scheduleTransition(argv[0], Common::Rect(argv[1], argv[2], argv[3], argv[4]));
+		_vm->_gfx->scheduleTransition((RivenTransition) argv[0], Common::Rect(argv[1], argv[2], argv[3], argv[4]));
 }
 
 // Command 19: reload card
diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp
index f930dfc..27b4c89 100644
--- a/engines/mohawk/riven_stacks/aspit.cpp
+++ b/engines/mohawk/riven_stacks/aspit.cpp
@@ -99,7 +99,7 @@ void ASpit::xaatrusbookback(uint16 argc, uint16 *argv) {
 	_vm->_inventory->backFromItemScript();
 }
 
-bool ASpit::pageTurn(int16 transition) {
+bool ASpit::pageTurn(RivenTransition transition) {
 	// Wait until the previous page turn sound completes
 	while (_vm->_sound->isEffectPlaying() && !_vm->shouldQuit()) {
 		if (!mouseIsDown()) {
@@ -137,7 +137,7 @@ void ASpit::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
 		if (page == 1)
 			return;
 
-		if (!pageTurn(1)) {
+		if (!pageTurn(kRivenTransitionWipeRight)) {
 			return;
 		}
 
@@ -161,7 +161,7 @@ void ASpit::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
 		if (((_vm->getFeatures() & GF_DEMO) && page == 6) || page == 10)
 			return;
 
-		if (!pageTurn(0)) {
+		if (!pageTurn(kRivenTransitionWipeLeft)) {
 			return;
 		}
 
@@ -237,7 +237,7 @@ void ASpit::xacathbookprevpage(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(5);
 
 	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(3);
+	_vm->_gfx->scheduleTransition(kRivenTransitionWipeDown);
 	_vm->getCard()->drawPicture(page);
 }
 
@@ -254,7 +254,7 @@ void ASpit::xacathbooknextpage(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(6);
 
 	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(2);
+	_vm->_gfx->scheduleTransition(kRivenTransitionWipeUp);
 	_vm->getCard()->drawPicture(page);
 }
 
diff --git a/engines/mohawk/riven_stacks/aspit.h b/engines/mohawk/riven_stacks/aspit.h
index d64214d..3ac3fa8 100644
--- a/engines/mohawk/riven_stacks/aspit.h
+++ b/engines/mohawk/riven_stacks/aspit.h
@@ -25,6 +25,8 @@
 
 #include "mohawk/riven_stack.h"
 
+#include "mohawk/riven_graphics.h"
+
 namespace Mohawk {
 namespace RivenStacks {
 
@@ -69,7 +71,7 @@ public:
 	void xaexittomain(uint16 argc, uint16 *argv);
 
 private:
-	bool pageTurn(int16 transition);
+	bool pageTurn(RivenTransition transition);
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/bspit.cpp b/engines/mohawk/riven_stacks/bspit.cpp
index 7a8ac69..c5fedd7 100644
--- a/engines/mohawk/riven_stacks/bspit.cpp
+++ b/engines/mohawk/riven_stacks/bspit.cpp
@@ -101,7 +101,7 @@ void BSpit::xblabbookprevpage(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(22);
 
 	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(1);
+	_vm->_gfx->scheduleTransition(kRivenTransitionWipeRight);
 	_vm->getCard()->drawPicture(page);
 }
 
@@ -118,7 +118,7 @@ void BSpit::xblabbooknextpage(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(23);
 
 	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(0);
+	_vm->_gfx->scheduleTransition(kRivenTransitionWipeLeft);
 	_vm->getCard()->drawPicture(page);
 }
 
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
index f4b0c0f..db2acfb 100644
--- a/engines/mohawk/riven_stacks/jspit.cpp
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -225,12 +225,12 @@ void JSpit::xvga1300_carriage(uint16 argc, uint16 *argv) {
 	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 	_vm->_system->updateScreen();                      // Update
 	_vm->_video->playMovieBlockingRiven(1);            // Play handle movie
-	_vm->_gfx->scheduleTransition(15);                 // Set pan down transition
+	_vm->_gfx->scheduleTransition(kRivenTransitionPanDown);
 	_vm->changeToCard(_vm->getStack()->getCardStackId(0x18e77));  // Change to card facing up
 	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor (again)
 	_vm->_system->updateScreen();                      // Update
 	_vm->_video->playMovieBlockingRiven(4);            // Play carriage beginning to drop
-	_vm->_gfx->scheduleTransition(14);                 // Set pan up transition
+	_vm->_gfx->scheduleTransition(kRivenTransitionPanUp);
 	_vm->changeToCard(_vm->getStack()->getCardStackId(0x183a9));  // Change to card looking straight again
 	_vm->_video->playMovieBlockingRiven(2);
 
@@ -265,12 +265,12 @@ void JSpit::xvga1300_carriage(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();                          // Update
 
 	if (gotClick) {
-		_vm->_gfx->scheduleTransition(16);                 // Schedule dissolve transition
+		_vm->_gfx->scheduleTransition(kRivenTransitionBlend);
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18d4d));  // Move forward
 		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 		_vm->_system->updateScreen();                      // Update
 		_vm->_system->delayMillis(500);                    // Delay a half second before changing again
-		_vm->_gfx->scheduleTransition(12);                 // Schedule pan left transition
+		_vm->_gfx->scheduleTransition(kRivenTransitionPanLeft);
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18ab5));  // Turn right
 		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 		_vm->_system->updateScreen();                      // Update
diff --git a/engines/mohawk/riven_stacks/ospit.cpp b/engines/mohawk/riven_stacks/ospit.cpp
index 7607a93..12328de 100644
--- a/engines/mohawk/riven_stacks/ospit.cpp
+++ b/engines/mohawk/riven_stacks/ospit.cpp
@@ -228,7 +228,7 @@ void OSpit::xogehnbookprevpage(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(12);
 
 	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(1);
+	_vm->_gfx->scheduleTransition(kRivenTransitionWipeRight);
 	_vm->getCard()->drawPicture(page);
 }
 
@@ -245,7 +245,7 @@ void OSpit::xogehnbooknextpage(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(13);
 
 	// Now update the screen :)
-	_vm->_gfx->scheduleTransition(0);
+	_vm->_gfx->scheduleTransition(kRivenTransitionWipeLeft);
 	_vm->getCard()->drawPicture(page);
 }
 


Commit: 3f58a795e724cde51966cb7e8b6fd8550d576b16
    https://github.com/scummvm/scummvm/commit/3f58a795e724cde51966cb7e8b6fd8550d576b16
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add an enum for Riven's command types

Changed paths:
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_inventory.cpp
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h
    engines/mohawk/riven_stacks/jspit.cpp


diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 09c72d5..cc816a8 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -149,7 +149,7 @@ void RivenCard::loadCardPictureList(uint16 id) {
 
 void RivenCard::drawPicture(uint16 index, bool queue) {
 	if (index > 0 && index <= _pictureList.size()) {
-		RivenScriptPtr script = _vm->_scriptMan->createScriptFromData(1, 39, 1, index);
+		RivenScriptPtr script = _vm->_scriptMan->createScriptFromData(1, kRivenCommandActivatePLST, 1, index);
 		_vm->_scriptMan->runScript(script, queue);
 	}
 }
@@ -216,7 +216,7 @@ void RivenCard::loadCardSoundList(uint16 id) {
 
 void RivenCard::playSound(uint16 index, bool queue) {
 	if (index > 0 && index <= _soundList.size()) {
-		RivenScriptPtr script = _vm->_scriptMan->createScriptFromData(1, 40, 1, index);
+		RivenScriptPtr script = _vm->_scriptMan->createScriptFromData(1, kRivenCommandActivateSLST, 1, index);
 		_vm->_scriptMan->runScript(script, queue);
 	}
 }
diff --git a/engines/mohawk/riven_inventory.cpp b/engines/mohawk/riven_inventory.cpp
index f4f3df0..3dcc590 100644
--- a/engines/mohawk/riven_inventory.cpp
+++ b/engines/mohawk/riven_inventory.cpp
@@ -187,7 +187,7 @@ void RivenInventory::checkClick(const Common::Point &mousePos) {
 }
 
 void RivenInventory::backFromItemScript() const {
-	RivenScriptPtr stopSoundScript = _vm->_scriptMan->createScriptFromData(1, 12, 1, 1);
+	RivenScriptPtr stopSoundScript = _vm->_scriptMan->createScriptFromData(1, kRivenCommandStopSound, 1, 1);
 	_vm->_scriptMan->runScript(stopSoundScript, false);
 
 	uint16 backStackId = _vm->_vars["returnstackid"];
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index ad7196d..99f1eec 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -67,13 +67,13 @@ RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream) {
 }
 
 RivenCommandPtr RivenScriptManager::readCommand(Common::ReadStream *stream) {
-	uint16 type = stream->readUint16BE();
+	RivenCommandType type = (RivenCommandType) stream->readUint16BE();
 
 	switch (type) {
-		case 8:
-			return RivenCommandPtr(RivenSwitchCommand::createFromStream(_vm, type, stream));
-		case 27:
-			return RivenCommandPtr(RivenStackChangeCommand::createFromStream(_vm, type, stream));
+		case kRivenCommandSwitch:
+			return RivenCommandPtr(RivenSwitchCommand::createFromStream(_vm, stream));
+		case kRivenCommandChangeStack:
+			return RivenCommandPtr(RivenStackChangeCommand::createFromStream(_vm, stream));
 		default:
 			return RivenCommandPtr(RivenSimpleCommand::createFromStream(_vm, type, stream));
 	}
@@ -155,7 +155,7 @@ RivenScriptPtr RivenScriptManager::createScriptFromData(uint16 commandCount, ...
 		uint16 command = va_arg(args, int);
 		writeStream.writeUint16BE(command);
 
-		if (command == 8) {
+		if (command == kRivenCommandSwitch) {
 			// The switch command has a different format that is not implemented
 			error("Cannot create a Switch command from data");
 		}
@@ -252,7 +252,7 @@ RivenCommand::~RivenCommand() {
 
 }
 
-RivenSimpleCommand::RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments) :
+RivenSimpleCommand::RivenSimpleCommand(MohawkEngine_Riven *vm, RivenCommandType type, const ArgumentArray &arguments) :
 		RivenCommand(vm),
 		_type(type),
 		_arguments(arguments) {
@@ -262,7 +262,7 @@ RivenSimpleCommand::RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const A
 RivenSimpleCommand::~RivenSimpleCommand() {
 }
 
-RivenSimpleCommand *RivenSimpleCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) {
+RivenSimpleCommand *RivenSimpleCommand::createFromStream(MohawkEngine_Riven *vm, RivenCommandType type, Common::ReadStream *stream) {
 	uint16 argCount = stream->readUint16BE();
 
 	Common::Array<uint16> arguments;
@@ -644,10 +644,10 @@ void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
 Common::String RivenSimpleCommand::describe() const {
 	Common::String desc;
 
-	if (_type == 7) { // Use the variable name
+	if (_type == kRivenCommandSwitch) { // Use the variable name
 		Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
 		desc = Common::String::format("%s = %d", varName.c_str(), _arguments[1]);
-	} else if (_type == 17) { // Use the external command name
+	} else if (_type == kRivenCommandRunExternal) { // Use the external command name
 		Common::String externalCommandName = _vm->getStack()->getName(kExternalCommandNames, _arguments[0]);
 		desc = Common::String::format("%s(", externalCommandName.c_str());
 		uint16 varCount = _arguments[1];
@@ -657,7 +657,7 @@ Common::String RivenSimpleCommand::describe() const {
 				desc += ", ";
 		}
 		desc += ")";
-	} else if (_type == 24) { // Use the variable name
+	} else if (_type == kRivenCommandIncrementVariable) { // Use the variable name
 		Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
 		desc = Common::String::format("%s += %d", varName.c_str(), _arguments[1]);
 	} else {
@@ -703,7 +703,7 @@ RivenSwitchCommand::~RivenSwitchCommand() {
 
 }
 
-RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) {
+RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm, Common::ReadStream *stream) {
 	RivenSwitchCommand *command = new RivenSwitchCommand(vm);
 
 	if (stream->readUint16BE() != 2) {
@@ -782,7 +782,7 @@ RivenStackChangeCommand::~RivenStackChangeCommand() {
 
 }
 
-RivenStackChangeCommand *RivenStackChangeCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) {
+RivenStackChangeCommand *RivenStackChangeCommand::createFromStream(MohawkEngine_Riven *vm, Common::ReadStream *stream) {
 	/* argumentsSize = */ stream->readUint16BE();
 	uint16 stackId = stream->readUint16BE();
 	uint32 globalCardId = stream->readUint32BE();
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 1a990c4..b47724d 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -37,17 +37,54 @@ namespace Mohawk {
 
 // Script Types
 enum {
-	kMouseDownScript = 0,
-	kMouseDragScript = 1,
-	kMouseUpScript = 2,
-	kMouseEnterScript = 3,
+	kMouseDownScript   = 0,
+	kMouseDragScript   = 1,
+	kMouseUpScript     = 2,
+	kMouseEnterScript  = 3,
 	kMouseInsideScript = 4,
-	kMouseLeaveScript = 5,
+	kMouseLeaveScript  = 5,
 
-	kCardLoadScript = 6,
-	kCardLeaveScript = 7,
-	kCardEnterScript = 9,
-	kCardUpdateScript = 10
+	kCardLoadScript    = 6,
+	kCardLeaveScript   = 7,
+	kCardEnterScript   = 9,
+	kCardUpdateScript  = 10
+};
+
+enum RivenCommandType {
+	kRivenCommandDrawBitmap          = 1,
+	kRivenCommandChangeCard          = 2,
+	kRivenCommandPlayScriptSLST      = 3,
+	kRivenCommandPlaySound           = 4,
+	kRivenCommandSetVariable         = 7,
+	kRivenCommandSwitch              = 8,
+	kRivenCommandEnableHotspot       = 9,
+	kRivenCommandDisableHotspot      = 10,
+	kRivenCommandStopSound           = 12,
+	kRivenCommandChangeCursor        = 13,
+	kRivenCommandDelay               = 14,
+	kRivenCommandRunExternal         = 17,
+	kRivenCommandTransition          = 18,
+	kRivenCommandRefreshCard         = 19,
+	kRivenCommandBeginScreenUpdate   = 20,
+	kRivenCommandApplyScreenUpdate   = 21,
+	kRivenCommandIncrementVariable   = 24,
+	kRivenCommandChangeStack         = 27,
+	kRivenCommandDisableMovie        = 28,
+	kRivenCommandDisableAllMovies    = 29,
+	kRivenCommandEnableMovie         = 31,
+	kRivenCommandlayMovieBlocking    = 32,
+	kRivenCommandPlayMovie           = 33,
+	kRivenCommandStopMovie           = 34,
+	kRivenCommandUnk36               = 36,
+	kRivenCommandFadeAmbientSounds   = 37,
+	kRivenCommandStoreMovieOpcode    = 38,
+	kRivenCommandActivatePLST        = 39,
+	kRivenCommandActivateSLST        = 40,
+	kRivenCommandActivateMLSTAndPlay = 41,
+	kRivenCommandActivateBLST        = 43,
+	kRivenCommandActivateFLST        = 44,
+	kRivenCommandZipMode             = 45,
+	kRivenCommandActivateMLST        = 46
 };
 
 class MohawkEngine_Riven;
@@ -206,7 +243,7 @@ protected:
  */
 class RivenSimpleCommand : public RivenCommand {
 public:
-	static RivenSimpleCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);
+	static RivenSimpleCommand *createFromStream(MohawkEngine_Riven *vm, RivenCommandType type, Common::ReadStream *stream);
 	virtual ~RivenSimpleCommand();
 
 	// RivenCommand API
@@ -221,7 +258,7 @@ private:
 		const char *desc;
 	};
 
-	RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments);
+	RivenSimpleCommand(MohawkEngine_Riven *vm, RivenCommandType type, const ArgumentArray &arguments);
 
 	void setupOpcodes();
 	Common::String describe() const;
@@ -264,7 +301,7 @@ private:
 
 	const RivenOpcode *_opcodes;
 
-	int _type;
+	RivenCommandType _type;
 	ArgumentArray _arguments;
 };
 
@@ -278,7 +315,7 @@ private:
  */
 class RivenSwitchCommand : public RivenCommand {
 public:
-	static RivenSwitchCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);
+	static RivenSwitchCommand *createFromStream(MohawkEngine_Riven *vm, Common::ReadStream *stream);
 	virtual ~RivenSwitchCommand();
 
 	// RivenCommand API
@@ -308,7 +345,7 @@ class RivenStackChangeCommand : public RivenCommand {
 public:
 	RivenStackChangeCommand(MohawkEngine_Riven *vm, uint16 stackId, uint32 globalCardId, bool byStackId);
 
-	static RivenStackChangeCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream);
+	static RivenStackChangeCommand *createFromStream(MohawkEngine_Riven *vm, Common::ReadStream *stream);
 	virtual ~RivenStackChangeCommand();
 
 	// RivenCommand API
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
index db2acfb..1df5501 100644
--- a/engines/mohawk/riven_stacks/jspit.cpp
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -474,7 +474,7 @@ void JSpit::sunnersPlayVideo(VideoEntryPtr &video, uint32 destCardGlobalId) {
 			sunners = 1;
 
 			uint16 destCardId = getCardStackId(destCardGlobalId);
-			RivenScriptPtr clickScript = _vm->_scriptMan->createScriptFromData(1, 2, 1, destCardId);
+			RivenScriptPtr clickScript = _vm->_scriptMan->createScriptFromData(1, kRivenCommandChangeCard, 1, destCardId);
 			_vm->_scriptMan->runScript(clickScript, false);
 			break;
 		}


Commit: 3900597996000ecb28f1c2cb366c8faf59495734
    https://github.com/scummvm/scummvm/commit/3900597996000ecb28f1c2cb366c8faf59495734
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Implement card transitions for Riven

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h
    engines/mohawk/riven_vars.cpp


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 197065b..16df6a6 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -156,7 +156,7 @@ Common::Error MohawkEngine_Riven::run() {
 	}
 
 	// Set the transition speed
-	_gfx->setTransitionSpeed(_vars["transitionmode"]);
+	_gfx->setTransitionMode((RivenTransitionMode) _vars["transitionmode"]);
 
 	// Start at main cursor
 	_cursor->setCursor(kRivenMainCursor);
@@ -195,8 +195,8 @@ void MohawkEngine_Riven::doFrame() {
 	checkTimer();
 	_sound->updateSLST();
 	_gfx->runFliesEffect();
-	bool needsUpdate = _gfx->runScheduledWaterEffects();
-	needsUpdate |= _video->updateMovies();
+	_gfx->runScheduledWaterEffects();
+	_video->updateMovies();
 
 	Common::Event event;
 
@@ -212,8 +212,6 @@ void MohawkEngine_Riven::doFrame() {
 				else
 					_inventory->hide();
 			}
-
-			needsUpdate = true;
 			break;
 		case Common::EVENT_LBUTTONDOWN:
 			_stack->onMouseDown(_eventMan->getMousePos());
@@ -237,7 +235,6 @@ void MohawkEngine_Riven::doFrame() {
 				_showHotspots = !_showHotspots;
 				if (_showHotspots) {
 					_card->drawHotspotRects();
-					needsUpdate = true;
 				} else
 					refreshCard();
 				break;
@@ -280,9 +277,8 @@ void MohawkEngine_Riven::doFrame() {
 		_scriptMan->runQueuedScripts();
 	}
 
-	// Update the screen if we need to
-	if (needsUpdate)
-		_system->updateScreen();
+	// Update the screen once per frame
+	_system->updateScreen();
 
 	// Cut down on CPU usage
 	_system->delayMillis(10);
@@ -440,15 +436,14 @@ void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
 	while (_system->getMillis() < startTime + ms && !shouldQuit()) {
 		_sound->updateSLST();
 		_gfx->runFliesEffect();
-		bool needsUpdate = _gfx->runScheduledWaterEffects();
-		needsUpdate |= _video->updateMovies();
+		_gfx->runScheduledWaterEffects();
+		_video->updateMovies();
 
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event))
 			;
 
-		if (needsUpdate)
-			_system->updateScreen();
+		_system->updateScreen();
 
 		_system->delayMillis(10); // Ease off the CPU
 	}
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index a4c4db4..f7ef95b 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -62,13 +62,6 @@ enum {
 	kStackLast = kStackAspit
 };
 
-enum RivenTransitionSpeed {
-	kRivenTransitionSpeedNone = 5000,
-	kRivenTransitionSpeedFastest = 5001,
-	kRivenTransitionSpeedNormal = 5002,
-	kRivenTransitionSpeedBest = 5003
-};
-
 // Engine Debug Flags
 enum {
 	kRivenDebugScript   = (1 << 0)
@@ -135,6 +128,7 @@ private:
 
 public:
 	// Stack/card/script funtions
+	RivenStack *constructStackById(uint16 id);
 	void changeToCard(uint16 dest);
 	void changeToStack(uint16);
 	void refreshCard();
@@ -162,8 +156,6 @@ public:
 	void installTimer(TimerProc *proc, uint32 time);
 	void checkTimer();
 	void removeTimer();
-
-	RivenStack *constructStackById(uint16 id);
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 60b8c5d..240144b 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -29,14 +29,247 @@
 
 #include "common/system.h"
 #include "engines/util.h"
+#include "graphics/colormasks.h"
 
 namespace Mohawk {
 
+class TransitionEffect {
+public:
+	TransitionEffect(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+		                 RivenTransition type, uint duration, const Common::Rect &rect) :
+			_system(system),
+			_mainScreen(mainScreen),
+			_effectScreen(effectScreen),
+			_type(type),
+			_duration(duration),
+			_timeBased(false),
+			_rect(rect) {
+	}
+
+	virtual ~TransitionEffect() {}
+
+	bool isTimeBased() const { return _timeBased; }
+
+	virtual void drawFrame(uint32 elapsed) = 0;
+
+protected:
+	Common::Rect makeDirectionalInitalArea() const {
+		Common::Rect initialArea = _rect;
+		switch (_type) {
+			case kRivenTransitionWipeLeft:
+			case kRivenTransitionPanLeft:
+				initialArea.left = _rect.right;
+				break;
+			case kRivenTransitionWipeRight:
+			case kRivenTransitionPanRight:
+				initialArea.right = _rect.left;
+				break;
+			case kRivenTransitionWipeUp:
+			case kRivenTransitionPanUp:
+				initialArea.top = _rect.bottom;
+				break;
+			case kRivenTransitionWipeDown:
+			case kRivenTransitionPanDown:
+				initialArea.bottom = _rect.top;
+				break;
+			default:
+				error("Unhandled transition type: %d", _type);
+		}
+
+		return initialArea;
+	}
+
+	OSystem *_system;
+
+	RivenTransition _type;
+	uint _duration;
+	Common::Rect _rect;
+	bool _timeBased;
+
+	Graphics::Surface *_mainScreen;
+	Graphics::Surface *_effectScreen;
+};
+
+class TransitionEffectWipe : public TransitionEffect {
+public:
+	TransitionEffectWipe(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+		                     RivenTransition type, uint duration, const Common::Rect &rect) :
+			TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
+
+		_timeBased = true;
+		_lastCopyArea = makeDirectionalInitalArea();
+	}
+
+	virtual void drawFrame(uint32 elapsed) override {
+		Common::Rect copyArea;
+		switch (_type) {
+			case kRivenTransitionWipeLeft:
+				copyArea.top = _lastCopyArea.top;
+				copyArea.bottom = _lastCopyArea.bottom;
+				copyArea.right = _lastCopyArea.left;
+				copyArea.left = _rect.width() - elapsed * _rect.width() / _duration;
+				break;
+			case kRivenTransitionWipeRight:
+				copyArea.top = _lastCopyArea.top;
+				copyArea.bottom = _lastCopyArea.bottom;
+				copyArea.left = _lastCopyArea.right;
+				copyArea.right = elapsed * _rect.width() / _duration;
+				break;
+			case kRivenTransitionWipeUp:
+				copyArea.left = _lastCopyArea.left;
+				copyArea.right = _lastCopyArea.right;
+				copyArea.bottom = _lastCopyArea.top;
+				copyArea.top = _rect.height() - elapsed * _rect.height() / _duration;
+				break;
+			case kRivenTransitionWipeDown:
+				copyArea.left = _lastCopyArea.left;
+				copyArea.right = _lastCopyArea.right;
+				copyArea.top = _lastCopyArea.bottom;
+				copyArea.bottom = elapsed * _rect.height() / _duration;
+				break;
+			default:
+				error("Unhandled transition type: %d", _type);
+		}
+
+		_lastCopyArea = copyArea;
+
+		if (copyArea.isEmpty()) {
+			// Nothing to draw
+			return;
+		}
+
+		_effectScreen->copyRectToSurface(*_mainScreen, copyArea.left, copyArea.top, copyArea);
+		_system->copyRectToScreen(_effectScreen->getBasePtr(copyArea.left, copyArea.top), _effectScreen->pitch,
+		                          copyArea.left, copyArea.top, copyArea.width(), copyArea.height());
+	}
+
+private:
+	Common::Rect _lastCopyArea;
+};
+
+class TransitionEffectPan : public TransitionEffect {
+public:
+	TransitionEffectPan(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+	                    RivenTransition type, uint duration, const Common::Rect &rect) :
+			TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
+
+		_timeBased = true;
+		_initialArea = makeDirectionalInitalArea();
+	}
+
+	virtual void drawFrame(uint32 elapsed) override {
+		Common::Rect newArea;
+		switch (_type) {
+			case kRivenTransitionPanLeft:
+				newArea.top = _initialArea.top;
+				newArea.bottom = _initialArea.bottom;
+				newArea.right = _initialArea.right;
+				newArea.left = _rect.width() - elapsed * _rect.width() / _duration;
+				break;
+			case kRivenTransitionPanRight:
+				newArea.top = _initialArea.top;
+				newArea.bottom = _initialArea.bottom;
+				newArea.left = _initialArea.left;
+				newArea.right = elapsed * _rect.width() / _duration;
+				break;
+			case kRivenTransitionPanUp:
+				newArea.left = _initialArea.left;
+				newArea.right = _initialArea.right;
+				newArea.bottom = _initialArea.bottom;
+				newArea.top = _rect.height() - elapsed * _rect.height() / _duration;
+				break;
+			case kRivenTransitionPanDown:
+				newArea.left = _initialArea.left;
+				newArea.right = _initialArea.right;
+				newArea.top = _initialArea.top;
+				newArea.bottom = elapsed * _rect.height() / _duration;
+				break;
+			default:
+				error("Unhandled transition type: %d", _type);
+		}
+
+		if (newArea.isEmpty()) {
+			// Nothing to draw
+			return;
+		}
+
+		Common::Rect oldArea = Common::Rect(
+				newArea.right != _rect.right ? _rect.left + newArea.width() : _rect.left,
+				newArea.bottom != _rect.bottom ? _rect.top + newArea.height() : _rect.top,
+				newArea.left != _rect.left ? _rect.right - newArea.width() : _rect.right,
+				newArea.top != _rect.top ? _rect.bottom - newArea.height() : _rect.bottom
+		);
+
+		int oldX = newArea.left != _rect.left ? _rect.left + newArea.width() : _rect.left;
+		int oldY = newArea.top != _rect.top ? _rect.top + newArea.height() : _rect.top;
+		_system->copyRectToScreen(_effectScreen->getBasePtr(oldX, oldY), _effectScreen->pitch,
+		                          oldArea.left, oldArea.top, oldArea.width(), oldArea.height());
+
+		int newX = newArea.right != _rect.right ? _rect.left + oldArea.width() : _rect.left;
+		int newY = newArea.bottom != _rect.bottom ? _rect.top + oldArea.height() : _rect.top;
+		_system->copyRectToScreen(_mainScreen->getBasePtr(newX, newY), _mainScreen->pitch,
+		                          newArea.left, newArea.top, newArea.width(), newArea.height());
+
+		if (newArea == _rect) {
+			_effectScreen->copyRectToSurface(*_mainScreen, _rect.left, _rect.top, _rect);
+		}
+	}
+
+private:
+	Common::Rect _initialArea;
+};
+
+class TransitionEffectBlend : public TransitionEffect {
+public:
+	TransitionEffectBlend(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+	                    RivenTransition type, uint duration, const Common::Rect &rect) :
+			TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
+
+		_timeBased = false;
+	}
+
+	virtual void drawFrame(uint32 elapsed) override {
+		assert(_effectScreen->format == _mainScreen->format);
+		assert(_effectScreen->format == _system->getScreenFormat());
+
+		if (elapsed == _duration) {
+			_effectScreen->copyRectToSurface(*_mainScreen, 0, 0, Common::Rect(_mainScreen->w, _mainScreen->h));
+			_system->copyRectToScreen(_effectScreen->getBasePtr(0, 0), _effectScreen->pitch, 0, 0, _effectScreen->w, _effectScreen->h);
+		} else {
+			Graphics::Surface *screen = _system->lockScreen();
+
+			uint alpha = elapsed * 255 / _duration;
+			for (uint y = 0; y < _mainScreen->h; y++) {
+				uint16 *src1 = (uint16 *) _mainScreen->getBasePtr(0, y);
+				uint16 *src2 = (uint16 *) _effectScreen->getBasePtr(0, y);
+				uint16 *dst = (uint16 *) screen->getBasePtr(0, y);
+				for (uint x = 0; x < _mainScreen->w; x++) {
+					uint8 r1, g1, b1, r2, g2, b2;
+					Graphics::colorToRGB< Graphics::ColorMasks<565> >(*src1++, r1, g1, b1);
+					Graphics::colorToRGB< Graphics::ColorMasks<565> >(*src2++, r2, g2, b2);
+
+					uint r = r1 * alpha + r2 * (255 - alpha);
+					uint g = g1 * alpha + g2 * (255 - alpha);
+					uint b = b1 * alpha + b2 * (255 - alpha);
+
+					r /= 255;
+					g /= 255;
+					b /= 255;
+
+					*dst++ = (uint16) Graphics::RGBToColor< Graphics::ColorMasks<565> >(r, g, b);
+				}
+			}
+
+			_system->unlockScreen();
+		}
+	}
+};
+
 RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm) {
 	_bitmapDecoder = new MohawkBitmap();
 
 	// Restrict ourselves to a single pixel format to simplify the effects implementation
-	_pixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+	_pixelFormat = Graphics::createPixelFormat<565>();
 	initGraphics(608, 436, true, &_pixelFormat);
 
 	// The actual game graphics only take up the first 392 rows. The inventory
@@ -55,7 +288,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 	_creditsImage = 302;
 	_creditsPos = 0;
 
-	_transitionSpeed = 0;
+	_transitionMode = kRivenTransitionModeFastest;
 	_fliesEffect = nullptr;
 }
 
@@ -93,16 +326,20 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
 void RivenGraphics::updateScreen(Common::Rect updateRect) {
 	if (_dirtyScreen) {
 		// Copy to screen if there's no transition. Otherwise transition. ;)
-		if (_scheduledTransition < 0) {
+		if (_scheduledTransition == kRivenTransitionNone
+		    || _transitionMode == kRivenTransitionModeDisabled) {
 			// mainScreen -> effectScreen -> systemScreen
 			_effectScreen->copyRectToSurface(*_mainScreen, updateRect.left, updateRect.top, updateRect);
 			_vm->_system->copyRectToScreen(_effectScreen->getBasePtr(updateRect.left, updateRect.top), _effectScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
+
+			// Finally, update the screen.
+			_vm->_system->updateScreen();
+
+			_scheduledTransition = kRivenTransitionNone;
 		} else {
 			runScheduledTransition();
 		}
 
-		// Finally, update the screen.
-		_vm->_system->updateScreen();
 		_dirtyScreen = false;
 	}
 }
@@ -148,10 +385,10 @@ void RivenGraphics::clearWaterEffects() {
 	_waterEffects.clear();
 }
 
-bool RivenGraphics::runScheduledWaterEffects() {
+void RivenGraphics::runScheduledWaterEffects() {
 	// Don't run the effect if it's disabled
 	if (_vm->_vars["waterenabled"] == 0)
-		return false;
+		return;
 
 	Graphics::Surface *screen = NULL;
 
@@ -195,50 +432,92 @@ bool RivenGraphics::runScheduledWaterEffects() {
 	// Unlock the screen if it has been locked and return true to update the screen
 	if (screen) {
 		_vm->_system->unlockScreen();
-		return true;
 	}
+}
 
-	return false;
+void RivenGraphics::setTransitionMode(RivenTransitionMode mode) {
+	_transitionMode = mode;
+	switch (_transitionMode) {
+		case kRivenTransitionModeFastest:
+			_transitionFrames   = 8;
+			_transitionDuration = 300;
+			break;
+		case kRivenTransitionModeNormal:
+			_transitionFrames   = 16;
+			_transitionDuration = 500;
+			break;
+		case kRivenTransitionModeBest:
+			_transitionFrames   = 32;
+			_transitionDuration = 700;
+			break;
+		case kRivenTransitionModeDisabled:
+			_transitionFrames   = 0;
+			_transitionDuration = 0;
+			break;
+		default:
+			error("Unknown transition mode %d", _transitionMode);
+	}
 }
 
-void RivenGraphics::scheduleTransition(RivenTransition id, Common::Rect rect) {
+void RivenGraphics::scheduleTransition(RivenTransition id, const Common::Rect &rect) {
 	_scheduledTransition = id;
 	_transitionRect = rect;
 }
 
 void RivenGraphics::runScheduledTransition() {
-	if (_scheduledTransition < 0) // No transition is scheduled
+	if (_scheduledTransition == kRivenTransitionNone)
 		return;
 
-	// TODO: There's a lot to be done here...
-
 	// Note: Transitions 0-11 are actual transitions, but none are used in-game.
 	// There's no point in implementing them if they're not used. These extra
 	// transitions were found by hacking scripts.
 
+	TransitionEffect *effect = nullptr;
 	switch (_scheduledTransition) {
-	case kRivenTransitionWipeLeft:
-	case kRivenTransitionWipeRight:
-	case kRivenTransitionWipeUp:
-	case kRivenTransitionWipeDown:
-	case kRivenTransitionPanLeft:
-	case kRivenTransitionPanRight:
-	case kRivenTransitionPanUp:
-	case kRivenTransitionPanDown:
-	case kRivenTransitionBlend:
-	case kRivenTransitionBlend2: // (tspit CARD 155)
-		break;
-	default:
-		if (_scheduledTransition >= 4 && _scheduledTransition <= 11)
-			error("Found unused transition %d", _scheduledTransition);
-		else
-			error("Found unknown transition %d", _scheduledTransition);
-	}
-
-	// For now, just copy the image to screen without doing any transition.
-	_effectScreen->copyRectToSurface(*_mainScreen, 0, 0, Common::Rect(_mainScreen->w, _mainScreen->h));
-	_vm->_system->copyRectToScreen(_effectScreen->getBasePtr(0, 0), _effectScreen->pitch, 0, 0, _effectScreen->w, _effectScreen->h);
-	_vm->_system->updateScreen();
+		case kRivenTransitionWipeLeft:
+		case kRivenTransitionWipeRight:
+		case kRivenTransitionWipeUp:
+		case kRivenTransitionWipeDown: {
+			effect = new TransitionEffectWipe(_vm->_system, _mainScreen, _effectScreen,
+			                                  _scheduledTransition, _transitionDuration, _transitionRect);
+			break;
+		}
+		case kRivenTransitionPanLeft:
+		case kRivenTransitionPanRight:
+		case kRivenTransitionPanUp:
+		case kRivenTransitionPanDown: {
+			effect = new TransitionEffectPan(_vm->_system, _mainScreen, _effectScreen,
+			                                 _scheduledTransition, _transitionDuration, _transitionRect);
+			break;
+		}
+		case kRivenTransitionBlend:
+		case kRivenTransitionBlend2: // (tspit CARD 155)
+			effect = new TransitionEffectBlend(_vm->_system, _mainScreen, _effectScreen,
+			                                   _scheduledTransition, _transitionFrames, _transitionRect);
+			break;
+		default:
+			error("Unhandled transition type: %d", _scheduledTransition);
+	}
+
+	if (effect->isTimeBased()) {
+		uint32 startTime = _vm->_system->getMillis();
+		uint32 timeElapsed = 0;
+		while (timeElapsed < _transitionDuration && !_vm->shouldQuit()) {
+			effect->drawFrame(timeElapsed);
+
+			_vm->doFrame();
+			timeElapsed = _vm->_system->getMillis() - startTime;
+		}
+
+		effect->drawFrame(_transitionDuration);
+	} else {
+		for (uint frame = 1; frame <= _transitionFrames && !_vm->shouldQuit(); frame++) {
+			effect->drawFrame(frame);
+
+			_vm->doFrame();
+		}
+	}
+	delete effect;
 
 	_scheduledTransition = kRivenTransitionNone; // Clear scheduled transition
 }
@@ -249,7 +528,7 @@ void RivenGraphics::clearMainScreen() {
 
 void RivenGraphics::fadeToBlack() {
 	// The transition speed is forced to best here
-	setTransitionSpeed(kRivenTransitionSpeedBest);
+	setTransitionMode(kRivenTransitionModeBest);
 	scheduleTransition(kRivenTransitionBlend);
 	clearMainScreen();
 	runScheduledTransition();
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 18b1e1d..565a51d 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -44,6 +44,13 @@ enum RivenTransition {
 	kRivenTransitionBlend2    = 17
 };
 
+enum RivenTransitionMode {
+	kRivenTransitionModeDisabled = 5000,
+	kRivenTransitionModeFastest  = 5001,
+	kRivenTransitionModeNormal   = 5002,
+	kRivenTransitionModeBest     = 5003
+};
+
 class RivenGraphics : public GraphicsManager {
 public:
 	RivenGraphics(MohawkEngine_Riven *vm);
@@ -66,7 +73,7 @@ public:
 	// Water Effect
 	void scheduleWaterEffect(uint16);
 	void clearWaterEffects();
-	bool runScheduledWaterEffects();
+	void runScheduledWaterEffects();
 
 	// Flies Effect
 	void setFliesEffect(uint16 count, bool fireflies);
@@ -74,10 +81,10 @@ public:
 	void runFliesEffect();
 
 	// Transitions
-	void scheduleTransition(RivenTransition id, Common::Rect rect = Common::Rect(0, 0, 608, 392));
+	void scheduleTransition(RivenTransition id, const Common::Rect &rect = Common::Rect(0, 0, 608, 392));
 	void runScheduledTransition();
 	void fadeToBlack();
-	void setTransitionSpeed(uint32 speed) { _transitionSpeed = speed; }
+	void setTransitionMode(RivenTransitionMode mode);
 
 	// Credits
 	void beginCredits();
@@ -114,7 +121,9 @@ private:
 	// Transitions
 	RivenTransition _scheduledTransition;
 	Common::Rect _transitionRect;
-	uint32 _transitionSpeed;
+	RivenTransitionMode _transitionMode;
+	uint _transitionFrames;
+	uint _transitionDuration;
 
 	// Screen Related
 	Graphics::Surface *_mainScreen;
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index 7d17562..bbc39c2 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -24,6 +24,7 @@
 
 #include "mohawk/riven.h"
 #include "mohawk/riven_stack.h"
+#include "mohawk/riven_graphics.h"
 
 namespace Mohawk {
 
@@ -305,7 +306,7 @@ void MohawkEngine_Riven::initVars() {
 	_vars["bmagcar"] = 1;
 	_vars["gnmagcar"] = 1;
 	_vars["omusicplayer"] = 1;
-	_vars["transitionmode"] = kRivenTransitionSpeedFastest;
+	_vars["transitionmode"] = kRivenTransitionModeFastest;
 	_vars["tdomeelev"] = 1;
 
 	// Randomize the telescope combination


Commit: aa32c5e5848a92e2b3cb6b08257046767ef1b514
    https://github.com/scummvm/scummvm/commit/aa32c5e5848a92e2b3cb6b08257046767ef1b514
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Pass rects by const reference in Riven's graphics manager

Changed paths:
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h


diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 240144b..a07cb79 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -323,7 +323,7 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
 	applyScreenUpdate();
 }
 
-void RivenGraphics::updateScreen(Common::Rect updateRect) {
+void RivenGraphics::updateScreen(const Common::Rect &updateRect) {
 	if (_dirtyScreen) {
 		// Copy to screen if there's no transition. Otherwise transition. ;)
 		if (_scheduledTransition == kRivenTransitionNone
@@ -544,7 +544,7 @@ void RivenGraphics::drawExtrasImageToScreen(uint16 id, const Common::Rect &rect)
 	delete mhkSurface;
 }
 
-void RivenGraphics::drawRect(Common::Rect rect, bool active) {
+void RivenGraphics::drawRect(const Common::Rect &rect, bool active) {
 	// Useful with debugging. Shows where hotspots are on the screen and whether or not they're active.
 	Graphics::Surface *screen = _vm->_system->lockScreen();
 
@@ -556,7 +556,7 @@ void RivenGraphics::drawRect(Common::Rect rect, bool active) {
 	_vm->_system->unlockScreen();
 }
 
-void RivenGraphics::drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect) {
+void RivenGraphics::drawImageRect(uint16 id, const Common::Rect &srcRect, const Common::Rect &dstRect) {
 	// Draw tBMP id from srcRect to dstRect
 	Graphics::Surface *surface = findImage(id)->getSurface();
 
@@ -568,7 +568,7 @@ void RivenGraphics::drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect
 	_dirtyScreen = true;
 }
 
-void RivenGraphics::drawExtrasImage(uint16 id, Common::Rect dstRect) {
+void RivenGraphics::drawExtrasImage(uint16 id, const Common::Rect &dstRect) {
 	MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
 	mhkSurface->convertToTrueColor();
 	Graphics::Surface *surface = mhkSurface->getSurface();
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 565a51d..36225da 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -61,10 +61,10 @@ public:
 	void applyScreenUpdate(bool force = false);
 
 	void copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom);
-	void updateScreen(Common::Rect updateRect = Common::Rect(0, 0, 608, 392));
-	void drawRect(Common::Rect rect, bool active);
-	void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect);
-	void drawExtrasImage(uint16 id, Common::Rect dstRect);
+	void updateScreen(const Common::Rect &updateRect = Common::Rect(0, 0, 608, 392));
+	void drawRect(const Common::Rect &rect, bool active);
+	void drawImageRect(uint16 id, const Common::Rect &srcRect, const Common::Rect &dstRect);
+	void drawExtrasImage(uint16 id, const Common::Rect &dstRect);
 	void drawExtrasImageToScreen(uint16 id, const Common::Rect &rect);
 
 	Graphics::Surface *getEffectScreen();


Commit: ad7f94f10f5893ed2ee14f9fc7b48d8ebbce49de
    https://github.com/scummvm/scummvm/commit/ad7f94f10f5893ed2ee14f9fc7b48d8ebbce49de
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Add a transition speed widget to the settings dialog

Changed paths:
    engines/mohawk/dialogs.cpp
    engines/mohawk/dialogs.h
    engines/mohawk/riven.cpp


diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index 38be98d..df832c9 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -26,6 +26,7 @@
 #include "gui/gui-manager.h"
 #include "gui/saveload.h"
 #include "gui/widget.h"
+#include "gui/widgets/popup.h"
 #include "common/system.h"
 #include "common/translation.h"
 
@@ -35,6 +36,7 @@
 
 #ifdef ENABLE_RIVEN
 #include "mohawk/riven.h"
+#include "mohawk/riven_graphics.h"
 #endif
 
 namespace Mohawk {
@@ -266,6 +268,13 @@ RivenOptionsDialog::RivenOptionsDialog(MohawkEngine_Riven* vm) :
 		_vm(vm) {
 	_zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 220, 15, _("~Z~ip Mode Activated"), 0, kZipCmd);
 	_waterEffectCheckbox = new GUI::CheckboxWidget(this, 15, 30, 220, 15, _("~W~ater Effect Enabled"), 0, kWaterCmd);
+
+	_transitionModeCaption = new GUI::StaticTextWidget(this, 15, 50, 90, 20, _("Transitions:"), Graphics::kTextAlignRight);
+	_transitionModePopUp = new GUI::PopUpWidget(this, 115, 50, 120, 20);
+	_transitionModePopUp->appendEntry(_("Disabled"), kRivenTransitionModeDisabled);
+	_transitionModePopUp->appendEntry(_("Fastest"), kRivenTransitionModeFastest);
+	_transitionModePopUp->appendEntry(_("Normal"), kRivenTransitionModeNormal);
+	_transitionModePopUp->appendEntry(_("Best"), kRivenTransitionModeBest);
 }
 
 RivenOptionsDialog::~RivenOptionsDialog() {
@@ -276,6 +285,7 @@ void RivenOptionsDialog::open() {
 
 	_zipModeCheckbox->setState(_vm->_vars["azip"] != 0);
 	_waterEffectCheckbox->setState(_vm->_vars["waterenabled"] != 0);
+	_transitionModePopUp->setSelectedTag(_vm->_vars["transitionmode"]);
 }
 
 void RivenOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
@@ -283,6 +293,7 @@ void RivenOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, u
 	case GUI::kOKCmd:
 		_vm->_vars["azip"] = _zipModeCheckbox->getState() ? 1 : 0;
 		_vm->_vars["waterenabled"] = _waterEffectCheckbox->getState() ? 1 : 0;
+		_vm->_vars["transitionmode"] = _transitionModePopUp->getSelectedTag();
 		setResult(1);
 		close();
 		break;
diff --git a/engines/mohawk/dialogs.h b/engines/mohawk/dialogs.h
index 443f2fb..9cbf5ff 100644
--- a/engines/mohawk/dialogs.h
+++ b/engines/mohawk/dialogs.h
@@ -34,6 +34,7 @@ class SaveLoadChooser;
 class ButtonWidget;
 class CheckboxWidget;
 class CommandSender;
+class PopUpWidget;
 class StaticTextWidget;
 }
 
@@ -143,6 +144,8 @@ private:
 
 	GUI::CheckboxWidget *_zipModeCheckbox;
 	GUI::CheckboxWidget *_waterEffectCheckbox;
+	GUI::StaticTextWidget *_transitionModeCaption;
+	GUI::PopUpWidget *_transitionModePopUp;
 };
 
 #endif
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 16df6a6..fcc0f0b 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -242,6 +242,7 @@ void MohawkEngine_Riven::doFrame() {
 				runDialog(*_optionsDialog);
 				if (_optionsDialog->getLoadSlot() >= 0)
 					loadGameState(_optionsDialog->getLoadSlot());
+				_gfx->setTransitionMode((RivenTransitionMode) _vars["transitionmode"]);
 				_card->initializeZipMode();
 				break;
 			case Common::KEYCODE_r:


Commit: 1aa42338025543814ac0dbf41ed62c03ccf01ba8
    https://github.com/scummvm/scummvm/commit/1aa42338025543814ac0dbf41ed62c03ccf01ba8
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Rework stack frame updates to work like the original

Changed paths:
    engines/mohawk/riven.cpp
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h


diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index fcc0f0b..2a651cf 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -194,8 +194,6 @@ void MohawkEngine_Riven::doFrame() {
 	// Update background running things
 	checkTimer();
 	_sound->updateSLST();
-	_gfx->runFliesEffect();
-	_gfx->runScheduledWaterEffects();
 	_video->updateMovies();
 
 	Common::Event event;
@@ -270,7 +268,7 @@ void MohawkEngine_Riven::doFrame() {
 		}
 	}
 
-	_card->onMouseUpdate();
+	_stack->onFrame();
 
 	if (!_scriptMan->runningQueuedScripts()) {
 		// Don't run queued scripts if we are calling from a queued script
@@ -436,8 +434,7 @@ void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
 
 	while (_system->getMillis() < startTime + ms && !shouldQuit()) {
 		_sound->updateSLST();
-		_gfx->runFliesEffect();
-		_gfx->runScheduledWaterEffects();
+		_stack->onFrame();
 		_video->updateMovies();
 
 		Common::Event event;
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index cc816a8..99ee902 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -107,7 +107,7 @@ RivenScriptPtr RivenCard::getScript(uint16 scriptType) const {
 			return _scripts[i].script;
 		}
 
-	return RivenScriptPtr(new RivenScript());
+	return RivenScriptPtr();
 }
 
 void RivenCard::runScript(uint16 scriptType) {
@@ -393,24 +393,30 @@ RivenScriptPtr RivenCard::onMouseMove(const Common::Point &mouse) {
 	return script;
 }
 
-void RivenCard::onMouseDragUpdate() {
+RivenScriptPtr RivenCard::onMouseDragUpdate() {
+	RivenScriptPtr script;
 	if (_pressedHotspot) {
-		RivenScriptPtr script = _pressedHotspot->getScript(kMouseDragScript);
-		_vm->_scriptMan->runScript(script, true);
+		script = _pressedHotspot->getScript(kMouseDragScript);
 	}
+
+	return script;
 }
 
-void RivenCard::onMouseUpdate() {
-	RivenScriptPtr script(new RivenScript());
+RivenScriptPtr RivenCard::onFrame() {
+	return getScript(kCardFrameScript);
+}
+
+RivenScriptPtr RivenCard::onMouseUpdate() {
+	RivenScriptPtr script;
 	if (_hoveredHotspot) {
 		script = _hoveredHotspot->getScript(kMouseInsideScript);
 	}
 
-	if (!script->empty()) {
-		_vm->_scriptMan->runScript(script, true);
-	} else {
+	if (!script || script->empty()) {
 		updateMouseCursor();
 	}
+
+	return script;
 }
 
 void RivenCard::updateMouseCursor() {
@@ -608,7 +614,7 @@ RivenScriptPtr RivenHotspot::getScript(uint16 scriptType) const {
 			return _scripts[i].script;
 		}
 
-	return RivenScriptPtr(new RivenScript());
+	return RivenScriptPtr();
 }
 
 bool RivenHotspot::isEnabled() const {
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 73c47b6..27e6ec2 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -118,11 +118,14 @@ public:
 	/** Handle a mouse move event */
 	RivenScriptPtr onMouseMove(const Common::Point &mouse);
 
+	/** General frame update handler */
+	RivenScriptPtr onFrame();
+
 	/** Frame update handler for the mouse cursor */
-	void onMouseUpdate();
+	RivenScriptPtr onMouseUpdate();
 
 	/** Frame update handler for mouse dragging */
-	void onMouseDragUpdate();
+	RivenScriptPtr onMouseDragUpdate();
 
 	/** Write all of the card's data to standard output */
 	void dump() const;
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index a07cb79..04562f8 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -670,12 +670,6 @@ void RivenGraphics::clearFliesEffect() {
 	_fliesEffect = nullptr;
 }
 
-void RivenGraphics::runFliesEffect() {
-	if (_fliesEffect) {
-		_fliesEffect->update();
-	}
-}
-
 Graphics::Surface *RivenGraphics::getBackScreen() {
 	return _mainScreen;
 }
@@ -684,6 +678,14 @@ Graphics::Surface *RivenGraphics::getEffectScreen() {
 	return _effectScreen;
 }
 
+void RivenGraphics::updateEffects() {
+	runScheduledWaterEffects();
+
+	if (_fliesEffect) {
+		_fliesEffect->update();
+	}
+}
+
 const FliesEffect::FliesEffectData FliesEffect::_firefliesParameters = {
 		true,
 		true,
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 36225da..b09eefb 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -73,12 +73,13 @@ public:
 	// Water Effect
 	void scheduleWaterEffect(uint16);
 	void clearWaterEffects();
-	void runScheduledWaterEffects();
 
 	// Flies Effect
 	void setFliesEffect(uint16 count, bool fireflies);
 	void clearFliesEffect();
-	void runFliesEffect();
+
+	/** Update the screen with the water and fly effects */
+	void updateEffects();
 
 	// Transitions
 	void scheduleTransition(RivenTransition id, const Common::Rect &rect = Common::Rect(0, 0, 608, 392));
@@ -115,6 +116,8 @@ private:
 	};
 	Common::Array<SFXERecord> _waterEffects;
 
+	void runScheduledWaterEffects();
+
 	// Flies Effect
 	FliesEffect *_fliesEffect;
 
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 99f1eec..8c6c974 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -120,6 +120,10 @@ void RivenScriptManager::clearStoredMovieOpcode() {
 }
 
 void RivenScriptManager::runScript(const RivenScriptPtr &script, bool queue) {
+	if (!script || script->empty()) {
+		return;
+	}
+
 	if (!queue) {
 		script->run();
 	} else {
@@ -239,7 +243,9 @@ const char *RivenScript::getTypeName(uint16 type) {
 }
 
 RivenScriptPtr &operator+=(RivenScriptPtr &lhs, const RivenScriptPtr &rhs) {
-	*lhs += *rhs;
+	if (rhs) {
+		*lhs += *rhs;
+	}
 	return lhs;
 }
 
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index b47724d..d052a02 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -46,6 +46,7 @@ enum {
 
 	kCardLoadScript    = 6,
 	kCardLeaveScript   = 7,
+	kCardFrameScript   = 8,
 	kCardEnterScript   = 9,
 	kCardUpdateScript  = 10
 };
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index f8dd6a3..490e4b5 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -272,6 +272,24 @@ void RivenStack::mouseForceUp() {
 	_mouseIsDown = false;
 }
 
+void RivenStack::onFrame() {
+	if (!_vm->getCard() || _vm->_scriptMan->hasQueuedScripts()) {
+		return;
+	}
+
+	_vm->_gfx->updateEffects();
+
+	RivenScriptPtr script(new RivenScript());
+	if (_mouseIsDown) {
+		script += _vm->getCard()->onMouseDragUpdate();
+	} else {
+		script += _vm->getCard()->onFrame();
+		script += _vm->getCard()->onMouseUpdate();
+	}
+
+	_vm->_scriptMan->runScript(script, true);
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 2365859..0ef267e 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -120,6 +120,9 @@ public:
 	/** Handle a mouse move event */
 	void onMouseMove(const Common::Point &mouse);
 
+	/** Frame update handler */
+	void onFrame();
+
 	/** Is the left mouse button currently pressed? */
 	bool mouseIsDown() const;
 


Commit: 9153393219b398ce5a7a8122d9af38e32e128059
    https://github.com/scummvm/scummvm/commit/9153393219b398ce5a7a8122d9af38e32e128059
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Allow games to opt out of the default video manager

Changed paths:
    engines/mohawk/cstime.cpp
    engines/mohawk/cstime.h
    engines/mohawk/livingbooks.cpp
    engines/mohawk/livingbooks.h
    engines/mohawk/mohawk.cpp
    engines/mohawk/mohawk.h
    engines/mohawk/myst.cpp
    engines/mohawk/myst.h
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h


diff --git a/engines/mohawk/cstime.cpp b/engines/mohawk/cstime.cpp
index b2889be..f3760a5 100644
--- a/engines/mohawk/cstime.cpp
+++ b/engines/mohawk/cstime.cpp
@@ -54,6 +54,7 @@ MohawkEngine_CSTime::MohawkEngine_CSTime(OSystem *syst, const MohawkGameDescript
 
 	_console = 0;
 	_gfx = 0;
+	_video = 0;
 	_sound = 0;
 	_cursor = 0;
 	_interface = 0;
@@ -68,6 +69,7 @@ MohawkEngine_CSTime::~MohawkEngine_CSTime() {
 	delete _view;
 	delete _console;
 	delete _sound;
+	delete _video;
 	delete _gfx;
 	delete _rnd;
 }
@@ -77,6 +79,7 @@ Common::Error MohawkEngine_CSTime::run() {
 
 	_console = new CSTimeConsole(this);
 	_gfx = new CSTimeGraphics(this);
+	_video = new VideoManager(this);
 	_sound = new Sound(this);
 	_cursor = new DefaultCursorManager(this, ID_CURS);
 
@@ -184,6 +187,17 @@ void MohawkEngine_CSTime::update() {
 	_system->delayMillis(10);
 }
 
+void MohawkEngine_CSTime::pauseEngineIntern(bool pause) {
+	MohawkEngine::pauseEngineIntern(pause);
+
+	if (pause) {
+		_video->pauseVideos();
+	} else {
+		_video->resumeVideos();
+		_system->updateScreen();
+	}
+}
+
 void MohawkEngine_CSTime::initCase() {
 	_interface->openResFile();
 	_interface->install();
diff --git a/engines/mohawk/cstime.h b/engines/mohawk/cstime.h
index 393032a..1c39a86 100644
--- a/engines/mohawk/cstime.h
+++ b/engines/mohawk/cstime.h
@@ -35,6 +35,7 @@ namespace Mohawk {
 class CSTimeCase;
 class CSTimeInterface;
 class CSTimeView;
+class VideoManager;
 
 enum {
 	kCSTimeEventNothing = 0xffff,
@@ -136,6 +137,7 @@ public:
 
 	Common::RandomSource *_rnd;
 
+	VideoManager *_video;
 	Sound *_sound;
 	CSTimeGraphics *_gfx;
 	bool _needsUpdate;
@@ -181,6 +183,8 @@ private:
 
 	Common::List<CSTimeEvent> _events;
 	void triggerEvent(CSTimeEvent &event);
+
+	void pauseEngineIntern(bool) override;
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 6ee18d1..340dfd1 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -145,6 +145,7 @@ MohawkEngine_LivingBooks::MohawkEngine_LivingBooks(OSystem *syst, const MohawkGa
 	_rnd = new Common::RandomSource("livingbooks");
 
 	_sound = NULL;
+	_video = NULL;
 	_page = NULL;
 
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -160,6 +161,7 @@ MohawkEngine_LivingBooks::~MohawkEngine_LivingBooks() {
 
 	delete _console;
 	delete _sound;
+	delete _video;
 	delete _gfx;
 	delete _rnd;
 	_bookInfoFile.clear();
@@ -184,6 +186,7 @@ Common::Error MohawkEngine_LivingBooks::run() {
 		error("Could not find xRes/yRes variables");
 
 	_gfx = new LBGraphics(this, _screenWidth, _screenHeight);
+	_video = new VideoManager(this);
 	_sound = new Sound(this);
 
 	if (getGameType() != GType_LIVINGBOOKSV1)
@@ -287,6 +290,17 @@ Common::Error MohawkEngine_LivingBooks::run() {
 	return Common::kNoError;
 }
 
+void MohawkEngine_LivingBooks::pauseEngineIntern(bool pause) {
+	MohawkEngine::pauseEngineIntern(pause);
+
+	if (pause) {
+		_video->pauseVideos();
+	} else {
+		_video->resumeVideos();
+		_system->updateScreen();
+	}
+}
+
 void MohawkEngine_LivingBooks::loadBookInfo(const Common::String &filename) {
 	if (!_bookInfoFile.loadFromFile(filename))
 		error("Could not open %s as a config file", filename.c_str());
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index cf67c1e..e0f8635 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -714,6 +714,7 @@ public:
 
 	Common::RandomSource *_rnd;
 
+	VideoManager *_video;
 	Sound *_sound;
 	LBGraphics *_gfx;
 	bool _needsRedraw, _needsUpdate;
@@ -817,6 +818,8 @@ private:
 	Common::String getStringFromConfig(const Common::String &section, const Common::String &key);
 	Common::String getStringFromConfig(const Common::String &section, const Common::String &key, Common::String &leftover);
 	int getIntFromConfig(const Common::String &section, const Common::String &key);
+
+	void pauseEngineIntern(bool) override;
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/mohawk.cpp b/engines/mohawk/mohawk.cpp
index 40290d4..53481af 100644
--- a/engines/mohawk/mohawk.cpp
+++ b/engines/mohawk/mohawk.cpp
@@ -41,13 +41,11 @@ MohawkEngine::MohawkEngine(OSystem *syst, const MohawkGameDescription *gamedesc)
 	// Setup mixer
 	syncSoundSettings();
 
-	_video = 0;
 	_pauseDialog = 0;
 	_cursor = 0;
 }
 
 MohawkEngine::~MohawkEngine() {
-	delete _video;
 	delete _pauseDialog;
 	delete _cursor;
 
@@ -57,23 +55,11 @@ MohawkEngine::~MohawkEngine() {
 }
 
 Common::Error MohawkEngine::run() {
-	_video = new VideoManager(this);
 	_pauseDialog = new PauseDialog(this, _("The game is paused. Press any key to continue."));
 
 	return Common::kNoError;
 }
 
-void MohawkEngine::pauseEngineIntern(bool pause) {
-	Engine::pauseEngineIntern(pause);
-
-	if (pause) {
-		_video->pauseVideos();
-	} else {
-		_video->resumeVideos();
-		_system->updateScreen();
-	}
-}
-
 void MohawkEngine::pauseGame() {
 	runDialog(*_pauseDialog);
 }
diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h
index bc0d642..fe2c157 100644
--- a/engines/mohawk/mohawk.h
+++ b/engines/mohawk/mohawk.h
@@ -100,7 +100,6 @@ public:
 
 	bool hasFeature(EngineFeature f) const;
 
-	VideoManager *_video;
 	CursorManager *_cursor;
 
 	virtual Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
@@ -118,7 +117,6 @@ public:
 
 private:
 	PauseDialog *_pauseDialog;
-	void pauseEngineIntern(bool);
 
 protected:
 	// An array holding the main Mohawk archives require by the games
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index e887436..d1da36a 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -76,6 +76,7 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription
 	_hoverResource = nullptr;
 
 	_sound = nullptr;
+	_video = nullptr;
 	_gfx = nullptr;
 	_console = nullptr;
 	_scriptParser = nullptr;
@@ -89,6 +90,7 @@ MohawkEngine_Myst::~MohawkEngine_Myst() {
 	DebugMan.clearAllDebugChannels();
 
 	delete _gfx;
+	delete _video;
 	delete _sound;
 	delete _console;
 	delete _scriptParser;
@@ -222,6 +224,7 @@ Common::Error MohawkEngine_Myst::run() {
 	MohawkEngine::run();
 
 	_gfx = new MystGraphics(this);
+	_video = new VideoManager(this);
 	_sound = new Sound(this);
 	_console = new MystConsole(this);
 	_gameState = new MystGameState(this, _saveFileMan);
@@ -420,6 +423,17 @@ void MohawkEngine_Myst::pollAndDiscardEvents() {
 	}
 }
 
+void MohawkEngine_Myst::pauseEngineIntern(bool pause) {
+	MohawkEngine::pauseEngineIntern(pause);
+
+	if (pause) {
+		_video->pauseVideos();
+	} else {
+		_video->resumeVideos();
+		_system->updateScreen();
+	}
+}
+
 void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcSound, uint16 linkDstSound) {
 	debug(2, "changeToStack(%d)", stack);
 
diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h
index 2414b71..f313e38 100644
--- a/engines/mohawk/myst.h
+++ b/engines/mohawk/myst.h
@@ -201,6 +201,7 @@ public:
 
 	bool _showResourceRects;
 
+	VideoManager *_video;
 	Sound *_sound;
 	MystGraphics *_gfx;
 	MystGameState *_gameState;
@@ -270,6 +271,8 @@ private:
 	void loadCursorHints();
 	uint16 _currentCursor;
 	uint16 _mainCursor; // Also defines the current page being held (white, blue, red, or none)
+
+	void pauseEngineIntern(bool) override;
 };
 
 template<class T>
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 2a651cf..f8b302b 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -60,6 +60,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
 	_extrasFile = nullptr;
 	_stack = nullptr;
 	_gfx = nullptr;
+	_video = nullptr;
 	_sound = nullptr;
 	_rnd = nullptr;
 	_scriptMan = nullptr;
@@ -91,6 +92,7 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
 	delete _card;
 	delete _stack;
 	delete _sound;
+	delete _video;
 	delete _gfx;
 	delete _console;
 	delete _extrasFile;
@@ -116,6 +118,7 @@ Common::Error MohawkEngine_Riven::run() {
 		SearchMan.add("arcriven.z", &_installerArchive, 0, false);
 
 	_gfx = new RivenGraphics(this);
+	_video = new VideoManager(this);
 	_sound = new RivenSoundManager(this);
 	_console = new RivenConsole(this);
 	_saveLoad = new RivenSaveLoad(this, _saveFileMan);
@@ -283,6 +286,17 @@ void MohawkEngine_Riven::doFrame() {
 	_system->delayMillis(10);
 }
 
+void MohawkEngine_Riven::pauseEngineIntern(bool pause) {
+	MohawkEngine::pauseEngineIntern(pause);
+
+	if (pause) {
+		_video->pauseVideos();
+	} else {
+		_video->resumeVideos();
+		_system->updateScreen();
+	}
+}
+
 // Stack/Card-Related Functions
 
 void MohawkEngine_Riven::changeToStack(uint16 n) {
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index f7ef95b..a8e9939 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -83,6 +83,7 @@ public:
 	MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc);
 	virtual ~MohawkEngine_Riven();
 
+	VideoManager *_video;
 	RivenSoundManager *_sound;
 	RivenGraphics *_gfx;
 	Common::RandomSource *_rnd;
@@ -126,6 +127,7 @@ private:
 	Common::SharedPtr<TimerProc> _timerProc;
 	uint32 _timerTime;
 
+	void pauseEngineIntern(bool) override;
 public:
 	// Stack/card/script funtions
 	RivenStack *constructStackById(uint16 id);


Commit: f977b5712328133b638c33992d4e111624d1881d
    https://github.com/scummvm/scummvm/commit/f977b5712328133b638c33992d4e111624d1881d
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2017-07-03T08:50:10+02:00

Commit Message:
MOHAWK: Rewrite the Riven movie manager

Changed paths:
  A engines/mohawk/riven_video.cpp
  A engines/mohawk/riven_video.h
    engines/mohawk/livingbooks.h
    engines/mohawk/module.mk
    engines/mohawk/mohawk.h
    engines/mohawk/riven.cpp
    engines/mohawk/riven.h
    engines/mohawk/riven_card.cpp
    engines/mohawk/riven_card.h
    engines/mohawk/riven_graphics.cpp
    engines/mohawk/riven_graphics.h
    engines/mohawk/riven_scripts.cpp
    engines/mohawk/riven_scripts.h
    engines/mohawk/riven_sound.cpp
    engines/mohawk/riven_sound.h
    engines/mohawk/riven_stack.cpp
    engines/mohawk/riven_stack.h
    engines/mohawk/riven_stacks/aspit.cpp
    engines/mohawk/riven_stacks/bspit.cpp
    engines/mohawk/riven_stacks/domespit.cpp
    engines/mohawk/riven_stacks/gspit.cpp
    engines/mohawk/riven_stacks/jspit.cpp
    engines/mohawk/riven_stacks/jspit.h
    engines/mohawk/riven_stacks/ospit.cpp
    engines/mohawk/riven_stacks/pspit.cpp
    engines/mohawk/riven_stacks/rspit.cpp
    engines/mohawk/riven_stacks/tspit.cpp
    engines/mohawk/riven_stacks/tspit.h
    engines/mohawk/video.cpp
    engines/mohawk/video.h


diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index e0f8635..4b87b64 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -27,6 +27,7 @@
 #include "mohawk/console.h"
 #include "mohawk/livingbooks_graphics.h"
 #include "mohawk/sound.h"
+#include "mohawk/video.h"
 
 #include "common/ini-file.h"
 #include "common/rect.h"
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 7022c23..de0f205 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -61,6 +61,7 @@ MODULE_OBJS += \
 	riven_sound.o \
 	riven_stack.o \
 	riven_vars.o \
+	riven_video.o \
 	riven_stacks/aspit.o \
 	riven_stacks/bspit.o \
 	riven_stacks/domespit.o \
diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h
index fe2c157..c6781ae 100644
--- a/engines/mohawk/mohawk.h
+++ b/engines/mohawk/mohawk.h
@@ -28,8 +28,6 @@
 
 #include "engines/engine.h"
 
-#include "mohawk/video.h"
-
 class OSystem;
 
 namespace Common {
@@ -111,10 +109,6 @@ public:
 
 	void pauseGame();
 
-	// Check if events should be done based on a video's current time
-	// (currently only used for Riven's storeMovieOpcode function)
-	virtual void doVideoTimer(VideoHandle handle, bool force) {}
-
 private:
 	PauseDialog *_pauseDialog;
 
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index f8b302b..52784e5 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -46,8 +46,8 @@
 #include "mohawk/riven_stacks/pspit.h"
 #include "mohawk/riven_stacks/rspit.h"
 #include "mohawk/riven_stacks/tspit.h"
+#include "mohawk/riven_video.h"
 #include "mohawk/dialogs.h"
-#include "mohawk/video.h"
 #include "mohawk/console.h"
 
 namespace Mohawk {
@@ -118,7 +118,7 @@ Common::Error MohawkEngine_Riven::run() {
 		SearchMan.add("arcriven.z", &_installerArchive, 0, false);
 
 	_gfx = new RivenGraphics(this);
-	_video = new VideoManager(this);
+	_video = new RivenVideoManager(this);
 	_sound = new RivenSoundManager(this);
 	_console = new RivenConsole(this);
 	_saveLoad = new RivenSaveLoad(this, _saveFileMan);
@@ -221,6 +221,9 @@ void MohawkEngine_Riven::doFrame() {
 			_stack->onMouseUp(_eventMan->getMousePos());
 			_inventory->checkClick(_eventMan->getMousePos());
 			break;
+		case Common::EVENT_KEYUP:
+			_stack->keyForceUp();
+			break;
 		case Common::EVENT_KEYDOWN:
 			switch (event.kbd.keycode) {
 			case Common::KEYCODE_d:
@@ -263,6 +266,8 @@ void MohawkEngine_Riven::doFrame() {
 				}
 				break;
 			default:
+				// TODO: Pass the keypress to the game only if it was not consumed by the engine
+				_stack->onKeyPressed(event.kbd.keycode);
 				break;
 			}
 			break;
@@ -310,8 +315,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
 		return;
 
 	// Stop any videos playing
-	_video->stopVideos();
-	_video->clearMLST();
+	_video->removeVideos();
 
 	// Clear the graphics cache; images aren't used across stack boundaries
 	_gfx->clearCache();
@@ -423,7 +427,7 @@ void MohawkEngine_Riven::refreshCard() {
 	// Clear any timer still floating around
 	removeTimer();
 
-	_card->enter();
+	_card->enter(true);
 
 	if (_showHotspots)
 		_card->drawHotspotRects();
@@ -498,19 +502,6 @@ void MohawkEngine_Riven::removeTimer() {
 	_timerTime = 0;
 }
 
-void MohawkEngine_Riven::doVideoTimer(VideoHandle handle, bool force) {
-	assert(handle);
-
-	uint16 id = _scriptMan->getStoredMovieOpcodeID();
-
-	if (handle != _video->findVideoRiven(id)) // Check if we've got a video match
-		return;
-
-	// Run the opcode if we can at this point
-	if (force || handle->getTime() >= _scriptMan->getStoredMovieOpcodeTime())
-		_scriptMan->runStoredMovieOpcode();
-}
-
 void MohawkEngine_Riven::addZipVisitedCard(uint16 cardId, uint16 cardNameId) {
 	Common::String cardName = getStack()->getName(kCardNames, cardNameId);
 	if (cardName.empty())
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index a8e9939..1cb8d9d 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -25,7 +25,6 @@
 
 #include "mohawk/installer_archive.h"
 #include "mohawk/mohawk.h"
-#include "mohawk/riven_scripts.h"
 
 #include "common/hashmap.h"
 #include "common/hash-str.h"
@@ -43,8 +42,10 @@ class RivenOptionsDialog;
 class RivenStack;
 class RivenCard;
 class RivenHotspot;
+class RivenScriptManager;
 class RivenSoundManager;
 class RivenInventory;
+class RivenVideoManager;
 
 // Riven Stack Types
 enum {
@@ -83,7 +84,7 @@ public:
 	MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc);
 	virtual ~MohawkEngine_Riven();
 
-	VideoManager *_video;
+	RivenVideoManager *_video;
 	RivenSoundManager *_sound;
 	RivenGraphics *_gfx;
 	Common::RandomSource *_rnd;
@@ -103,7 +104,6 @@ public:
 #define TIMER(cls, method) \
 		new Common::Functor0Mem<void, cls>(this, &cls::method)
 
-	void doVideoTimer(VideoHandle handle, bool force);
 	void doFrame();
 
 private:
diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp
index 99ee902..cc9e812 100644
--- a/engines/mohawk/riven_card.cpp
+++ b/engines/mohawk/riven_card.cpp
@@ -25,6 +25,7 @@
 #include "mohawk/cursors.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_stack.h"
+#include "mohawk/riven_video.h"
 
 #include "mohawk/resource.h"
 #include "mohawk/riven.h"
@@ -52,7 +53,7 @@ RivenCard::~RivenCard() {
 
 	_vm->_gfx->clearWaterEffects();
 	_vm->_gfx->clearFliesEffect();
-	_vm->_video->stopVideos();
+	_vm->_video->closeVideos();
 }
 
 void RivenCard::loadCardResource(uint16 id) {
@@ -65,7 +66,7 @@ void RivenCard::loadCardResource(uint16 id) {
 	delete inStream;
 }
 
-void RivenCard::enter() {
+void RivenCard::enter(bool unkMovies) {
 	setCurrentCardVariable();
 
 	_vm->_activatedPLST = false;
@@ -516,7 +517,7 @@ void RivenCard::dump() const {
 	for (uint i = 0; i < _movieList.size(); i++) {
 		debug("== Movie %d ==", _movieList[i].index);
 		debug("movieID: %d", _movieList[i].movieID);
-		debug("code: %d", _movieList[i].code);
+		debug("playbackSlot: %d", _movieList[i].playbackSlot);
 		debug("left: %d", _movieList[i].left);
 		debug("top: %d", _movieList[i].top);
 		debug("u0[0]: %d", _movieList[i].u0[0]);
@@ -539,7 +540,7 @@ void RivenCard::loadCardMovieList(uint16 id) {
 		MLSTRecord &mlstRecord = _movieList[i];
 		mlstRecord.index = mlstStream->readUint16BE();
 		mlstRecord.movieID = mlstStream->readUint16BE();
-		mlstRecord.code = mlstStream->readUint16BE();
+		mlstRecord.playbackSlot = mlstStream->readUint16BE();
 		mlstRecord.left = mlstStream->readUint16BE();
 		mlstRecord.top = mlstStream->readUint16BE();
 
@@ -571,6 +572,13 @@ MLSTRecord RivenCard::getMovie(uint16 index) const {
 	error("Could not find movie %d in card %d", index, _id);
 }
 
+void RivenCard::playMovie(uint16 index, bool queue) {
+	if (index > 0 && index <= _movieList.size()) {
+		RivenScriptPtr script = _vm->_scriptMan->createScriptFromData(1, kRivenCommandActivateMLSTAndPlay, 1, index);
+		_vm->_scriptMan->runScript(script, queue);
+	}
+}
+
 RivenHotspot::RivenHotspot(MohawkEngine_Riven *vm, Common::ReadStream *stream) :
 		_vm(vm) {
 	loadFromStream(stream);
diff --git a/engines/mohawk/riven_card.h b/engines/mohawk/riven_card.h
index 27e6ec2..a736a60 100644
--- a/engines/mohawk/riven_card.h
+++ b/engines/mohawk/riven_card.h
@@ -25,7 +25,6 @@
 
 #include "mohawk/riven_scripts.h"
 #include "mohawk/riven_sound.h"
-#include "mohawk/video.h"
 
 #include "common/rect.h"
 #include "common/system.h"
@@ -33,6 +32,7 @@
 namespace Mohawk {
 
 class RivenHotspot;
+struct MLSTRecord;
 
 /**
  * A game view
@@ -57,7 +57,7 @@ public:
 	};
 
 	/** Initialization routine used to draw a card for the first time or to refresh it */
-	void enter();
+	void enter(bool unkMovies);
 
 	/** Run the card's leave scripts */
 	void leave();
@@ -80,6 +80,9 @@ public:
 	/** Get the card's sound description with the specified index */
 	SLSTRecord getSound(uint16 index) const;
 
+	/** Play the card's movie with the specified index */
+	void playMovie(uint16 index, bool queue = false);
+
 	/** Get the card's movie description with the specified index */
 	MLSTRecord getMovie(uint16 index) const;
 
@@ -177,6 +180,18 @@ private:
 	Common::Array<WaterEffectRecord> _waterEffectList;
 };
 
+struct MLSTRecord {
+	uint16 index;
+	uint16 movieID;
+	uint16 playbackSlot;
+	uint16 left;
+	uint16 top;
+	uint16 u0[3];
+	uint16 loop;
+	uint16 volume;
+	uint16 u1;
+};
+
 /**
  * A Card Hotspot
  *
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 04562f8..a4fd6d0 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -282,6 +282,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
 
 	_screenUpdateNesting = 0;
 	_screenUpdateRunning = false;
+	_enableCardUpdateScript = true;
 	_scheduledTransition = kRivenTransitionNone;
 	_dirtyScreen = false;
 
@@ -651,7 +652,9 @@ void RivenGraphics::applyScreenUpdate(bool force) {
 	if (_screenUpdateNesting <= 0 && !_screenUpdateRunning) {
 		_screenUpdateRunning = true;
 
-		_vm->getCard()->runScript(kCardUpdateScript);
+		if (_enableCardUpdateScript) {
+			_vm->getCard()->runScript(kCardUpdateScript);
+		}
 		_vm->_sound->triggerDrawSound();
 		updateScreen();
 
@@ -686,6 +689,17 @@ void RivenGraphics::updateEffects() {
 	}
 }
 
+void RivenGraphics::copySystemRectToScreen(const Common::Rect &rect) {
+	Graphics::Surface *screen = _vm->_system->lockScreen();
+	_mainScreen->copyRectToSurface(*screen, rect.left, rect.top, rect);
+	_effectScreen->copyRectToSurface(*screen, rect.left, rect.top, rect);
+	_vm->_system->unlockScreen();
+}
+
+void RivenGraphics::enableCardUpdateScript(bool enable) {
+	_enableCardUpdateScript = enable;
+}
+
 const FliesEffect::FliesEffectData FliesEffect::_firefliesParameters = {
 		true,
 		true,
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index b09eefb..6b63a86 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -59,6 +59,7 @@ public:
 	// Screen updates
 	void beginScreenUpdate();
 	void applyScreenUpdate(bool force = false);
+	void enableCardUpdateScript(bool enable);
 
 	void copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom);
 	void updateScreen(const Common::Rect &updateRect = Common::Rect(0, 0, 608, 392));
@@ -67,6 +68,9 @@ public:
 	void drawExtrasImage(uint16 id, const Common::Rect &dstRect);
 	void drawExtrasImageToScreen(uint16 id, const Common::Rect &rect);
 
+	/** Copy a rect from the system screen to the game screen */
+	void copySystemRectToScreen(const Common::Rect &rect);
+
 	Graphics::Surface *getEffectScreen();
 	Graphics::Surface *getBackScreen();
 
@@ -101,6 +105,7 @@ private:
 	MohawkBitmap *_bitmapDecoder;
 	int _screenUpdateNesting;
 	bool _screenUpdateRunning;
+	bool _enableCardUpdateScript;
 
 	// Water Effects
 	struct SFXERecord {
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 8c6c974..e6ebd6e 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -27,7 +27,7 @@
 #include "mohawk/riven_scripts.h"
 #include "mohawk/riven_sound.h"
 #include "mohawk/riven_stack.h"
-#include "mohawk/video.h"
+#include "mohawk/riven_video.h"
 #include "common/memstream.h"
 
 #include "common/debug-channels.h"
@@ -509,9 +509,9 @@ void RivenSimpleCommand::incrementVariable(uint16 op, uint16 argc, uint16 *argv)
 
 // Command 28: disable a movie
 void RivenSimpleCommand::disableMovie(uint16 op, uint16 argc, uint16 *argv) {
-	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
+	RivenVideo *video = _vm->_video->openSlot(argv[0]);
 	if (video)
-		video->setEnabled(false);
+		video->disable();
 }
 
 // Command 29: disable all movies
@@ -521,26 +521,29 @@ void RivenSimpleCommand::disableAllMovies(uint16 op, uint16 argc, uint16 *argv)
 
 // Command 31: enable a movie
 void RivenSimpleCommand::enableMovie(uint16 op, uint16 argc, uint16 *argv) {
-	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
-	if (video)
-		video->setEnabled(true);
+	RivenVideo *video = _vm->_video->openSlot(argv[0]);
+	video->enable();
 }
 
 // Command 32: play foreground movie - blocking (movie_id)
 void RivenSimpleCommand::playMovieBlocking(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_cursor->hideCursor();
-	_vm->_video->playMovieBlockingRiven(argv[0]);
-	_vm->_cursor->showCursor();
+	RivenVideo *video = _vm->_video->openSlot(argv[0]);
+	video->setLooping(false);
+	video->enable();
+	video->playBlocking();
 }
 
 // Command 33: play background movie - nonblocking (movie_id)
 void RivenSimpleCommand::playMovie(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->playMovieRiven(argv[0]);
+	RivenVideo *video = _vm->_video->openSlot(argv[0]);
+	video->enable();
+	video->play();
 }
 
 // Command 34: stop a movie
 void RivenSimpleCommand::stopMovie(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->stopMovieRiven(argv[0]);
+	RivenVideo *video = _vm->_video->openSlot(argv[0]);
+	video->stop();
 }
 
 // Command 36: unknown
@@ -611,8 +614,12 @@ void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 41: activate MLST record and play
 void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(argv[0]));
-	_vm->_video->playMovieRiven(argv[0]);
+	MLSTRecord mlstRecord = _vm->getCard()->getMovie(argv[0]);
+	activateMLST(mlstRecord);
+
+	RivenVideo *video = _vm->_video->openSlot(mlstRecord.playbackSlot);
+	video->enable();
+	video->play();
 }
 
 // Command 43: activate BLST record (card hotspot enabling lists)
@@ -644,7 +651,16 @@ void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) {
 
 // Command 46: activate MLST record (movie lists)
 void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(argv[0]));
+	MLSTRecord mlstRecord = _vm->getCard()->getMovie(argv[0]);
+	activateMLST(mlstRecord);
+}
+
+void RivenSimpleCommand::activateMLST(const MLSTRecord &mlstRecord) const {
+	RivenVideo *ptr = _vm->_video->openSlot(mlstRecord.playbackSlot);
+	ptr->load(mlstRecord.movieID);
+	ptr->moveTo(mlstRecord.left, mlstRecord.top);
+	ptr->setLooping(mlstRecord.loop != 0);
+	ptr->setVolume(mlstRecord.volume);
 }
 
 Common::String RivenSimpleCommand::describe() const {
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index d052a02..cac01fb 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -91,6 +91,7 @@ enum RivenCommandType {
 class MohawkEngine_Riven;
 class RivenCommand;
 class RivenScript;
+struct MLSTRecord;
 
 typedef Common::SharedPtr<RivenScript> RivenScriptPtr;
 typedef Common::SharedPtr<RivenCommand> RivenCommandPtr;
@@ -264,6 +265,8 @@ private:
 	void setupOpcodes();
 	Common::String describe() const;
 
+	void activateMLST(const MLSTRecord &mlst) const;
+
 	DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); }
 
 	// Opcodes
diff --git a/engines/mohawk/riven_sound.cpp b/engines/mohawk/riven_sound.cpp
index 354ba2d..36dbab6 100644
--- a/engines/mohawk/riven_sound.cpp
+++ b/engines/mohawk/riven_sound.cpp
@@ -26,11 +26,13 @@
 #include "audio/audiostream.h"
 
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_card.h"
 #include "mohawk/sound.h"
 
 namespace Mohawk {
 
-RivenSoundManager::RivenSoundManager(MohawkEngine *vm) :
+RivenSoundManager::RivenSoundManager(MohawkEngine_Riven *vm) :
 		_vm(vm),
 		_effect(nullptr),
 		_mainAmbientSoundId(-1),
@@ -68,8 +70,9 @@ void RivenSoundManager::playSound(uint16 id, uint16 volume, bool playOnDraw) {
 	}
 }
 
-void RivenSoundManager::playSound(const Common::String &name, uint16 volume, bool playOnDraw) {
-	uint16 id =_vm->findResourceID(ID_TWAV, name);
+void RivenSoundManager::playCardSound(const Common::String &name, uint16 volume, bool playOnDraw) {
+	Common::String fullName = Common::String::format("%d_%s_1", _vm->getCard()->getId(), name.c_str());
+	uint16 id =_vm->findResourceID(ID_TWAV, fullName);
 	playSound(id, volume, playOnDraw);
 }
 
@@ -307,7 +310,7 @@ bool RivenSoundManager::isEffectPlaying() const {
 	return _effect != nullptr && _effect->isPlaying();
 }
 
-RivenSound::RivenSound(MohawkEngine *vm, Audio::RewindableAudioStream *rewindStream) :
+RivenSound::RivenSound(MohawkEngine_Riven *vm, Audio::RewindableAudioStream *rewindStream) :
 		_vm(vm),
 		_volume(Audio::Mixer::kMaxChannelVolume),
 		_balance(0),
diff --git a/engines/mohawk/riven_sound.h b/engines/mohawk/riven_sound.h
index bd9237d..a929c43 100644
--- a/engines/mohawk/riven_sound.h
+++ b/engines/mohawk/riven_sound.h
@@ -34,7 +34,7 @@ class RewindableAudioStream;
 
 namespace Mohawk {
 
-class MohawkEngine;
+class MohawkEngine_Riven;
 class RivenSound;
 
 /**
@@ -65,7 +65,7 @@ struct SLSTRecord {
  */
 class RivenSoundManager {
 public:
-	RivenSoundManager(MohawkEngine *vm);
+	RivenSoundManager(MohawkEngine_Riven *vm);
 	~RivenSoundManager();
 
 	/**
@@ -78,7 +78,7 @@ public:
 	void playSound(uint16 id, uint16 volume = 255, bool playOnDraw = false);
 
 	/** Play an effect sound by its resource name */
-	void playSound(const Common::String &name, uint16 volume = 255, bool playOnDraw = false);
+	void playCardSound(const Common::String &name, uint16 volume = 255, bool playOnDraw = false);
 
 	/** Start playing the scheduled on-draw effect sound, if any. Called by the GraphicsManager. */
 	void triggerDrawSound();
@@ -120,7 +120,7 @@ private:
 		kFadeInNewSounds = 2
 	};
 
-	MohawkEngine *_vm;
+	MohawkEngine_Riven *_vm;
 
 	int16 _mainAmbientSoundId;
 	AmbientSoundList _ambientSounds;
@@ -154,7 +154,7 @@ private:
  */
 class RivenSound {
 public:
-	RivenSound(MohawkEngine *vm, Audio::RewindableAudioStream *rewindStream);
+	RivenSound(MohawkEngine_Riven *vm, Audio::RewindableAudioStream *rewindStream);
 	~RivenSound();
 
 	/** Start playing the sound stream passed to the constructor */
@@ -185,7 +185,7 @@ private:
 	static byte convertVolume(uint16 volume);
 	static int8 convertBalance(int16 balance);
 
-	MohawkEngine *_vm;
+	MohawkEngine_Riven *_vm;
 
 	Audio::SoundHandle _handle;
 	Audio::RewindableAudioStream *_stream;
diff --git a/engines/mohawk/riven_stack.cpp b/engines/mohawk/riven_stack.cpp
index 490e4b5..a7b348b 100644
--- a/engines/mohawk/riven_stack.cpp
+++ b/engines/mohawk/riven_stack.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_video.h"
 #include "mohawk/resource.h"
 
 #include "common/events.h"
@@ -37,7 +38,8 @@ namespace Mohawk {
 RivenStack::RivenStack(MohawkEngine_Riven *vm, uint16 id) :
 		_vm(vm),
 		_id(id),
-		_mouseIsDown(false) {
+		_mouseIsDown(false),
+		_keyPressed(Common::KEYCODE_INVALID) {
 	loadResourceNames();
 	loadCardIdMap();
 	setCurrentStackVariable();
@@ -178,10 +180,11 @@ void RivenStack::runDemoBoundaryDialog() {
 	dialog.runModal();
 }
 
-void RivenStack::runEndGame(uint16 video, uint32 delay) {
+void RivenStack::runEndGame(uint16 videoCode, uint32 delay) {
 	_vm->_sound->stopAllSLST();
-	_vm->_video->playMovieRiven(video);
-	runCredits(video, delay);
+	RivenVideo *video = _vm->_video->openSlot(videoCode);
+	video->play();
+	runCredits(videoCode, delay);
 }
 
 void RivenStack::runCredits(uint16 video, uint32 delay) {
@@ -190,7 +193,7 @@ void RivenStack::runCredits(uint16 video, uint32 delay) {
 	_vm->_gfx->beginCredits();
 	uint nextCreditsFrameStart = 0;
 
-	VideoEntryPtr videoPtr = _vm->_video->findVideoRiven(video);
+	RivenVideo *videoPtr = _vm->_video->getSlot(video);
 
 	while (!_vm->shouldQuit() && _vm->_gfx->getCurCreditsImage() <= 320) {
 		if (videoPtr->getCurFrame() >= (int32)videoPtr->getFrameCount() - 1) {
@@ -207,8 +210,10 @@ void RivenStack::runCredits(uint16 video, uint32 delay) {
 
 				_vm->_gfx->updateCredits();
 			}
-		} else if (_vm->_video->updateMovies())
+		} else {
+			_vm->_video->updateMovies();
 			_vm->_system->updateScreen();
+		}
 
 		Common::Event event;
 		while (_vm->_system->getEventManager()->pollEvent(event))
@@ -290,6 +295,18 @@ void RivenStack::onFrame() {
 	_vm->_scriptMan->runScript(script, true);
 }
 
+Common::KeyCode RivenStack::keyGetPressed() const {
+	return _keyPressed;
+}
+
+void RivenStack::keyForceUp() {
+	_keyPressed = Common::KEYCODE_INVALID;
+}
+
+void RivenStack::onKeyPressed(const Common::KeyCode keyCode) {
+	_keyPressed = keyCode;
+}
+
 RivenNameList::RivenNameList() {
 
 }
diff --git a/engines/mohawk/riven_stack.h b/engines/mohawk/riven_stack.h
index 0ef267e..e2bee44 100644
--- a/engines/mohawk/riven_stack.h
+++ b/engines/mohawk/riven_stack.h
@@ -23,6 +23,7 @@
 #ifndef RIVEN_STACK_H
 #define RIVEN_STACK_H
 
+#include "common/keyboard.h"
 #include "common/hash-str.h"
 #include "common/ptr.h"
 #include "common/rect.h"
@@ -129,13 +130,22 @@ public:
 	/** Force the left mouse button to be considered unpressed until the next mouse click */
 	void mouseForceUp();
 
+	/** Handle a key press event */
+	void onKeyPressed(const Common::KeyCode keyCode);
+
+	/** Get the pressed keyboard key if any */
+	Common::KeyCode keyGetPressed() const;
+
+	/** Force the keyboard to be considered unpressed until the next key press */
+	void keyForceUp();
+
 	// Common external commands
 	void xflies(uint16 argc, uint16 *argv); // Start the "flies" effect
 
 	// TODO: Misc stuff move elsewhere
 	uint16 getComboDigit(uint32 correctCombo, uint32 digit);
 	void runDemoBoundaryDialog();
-	void runEndGame(uint16 video, uint32 delay);
+	void runEndGame(uint16 videoCode, uint32 delay);
 	void runCredits(uint16 video, uint32 delay);
 
 protected:
@@ -171,6 +181,8 @@ private:
 
 	CommandsMap _commands;
 
+	Common::KeyCode _keyPressed;
+
 	bool _mouseIsDown;
 	Common::Point _mousePosition;
 	Common::Point _mouseDragStartPosition;
diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp
index 27b4c89..04173bb 100644
--- a/engines/mohawk/riven_stacks/aspit.cpp
+++ b/engines/mohawk/riven_stacks/aspit.cpp
@@ -27,6 +27,7 @@
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_inventory.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_video.h"
 
 #include "common/translation.h"
 
@@ -38,27 +39,27 @@ namespace RivenStacks {
 ASpit::ASpit(MohawkEngine_Riven *vm) :
 		RivenStack(vm, kStackAspit) {
 
-	REGISTER_COMMAND(ASpit, xastartupbtnhide);
-	REGISTER_COMMAND(ASpit, xasetupcomplete);
-	REGISTER_COMMAND(ASpit, xaatrusopenbook);
-	REGISTER_COMMAND(ASpit, xaatrusbookback);
-	REGISTER_COMMAND(ASpit, xaatrusbookprevpage);
-	REGISTER_COMMAND(ASpit, xaatrusbooknextpage);
-	REGISTER_COMMAND(ASpit, xacathopenbook);
-	REGISTER_COMMAND(ASpit, xacathbookback);
-	REGISTER_COMMAND(ASpit, xacathbookprevpage);
-	REGISTER_COMMAND(ASpit, xacathbooknextpage);
-	REGISTER_COMMAND(ASpit, xtrapbookback);
-	REGISTER_COMMAND(ASpit, xatrapbookclose);
-	REGISTER_COMMAND(ASpit, xatrapbookopen);
-	REGISTER_COMMAND(ASpit, xarestoregame);
-	REGISTER_COMMAND(ASpit, xadisablemenureturn);
-	REGISTER_COMMAND(ASpit, xaenablemenureturn);
-	REGISTER_COMMAND(ASpit, xalaunchbrowser);
-	REGISTER_COMMAND(ASpit, xadisablemenuintro);
-	REGISTER_COMMAND(ASpit, xaenablemenuintro);
-	REGISTER_COMMAND(ASpit, xademoquit);
-	REGISTER_COMMAND(ASpit, xaexittomain);
+	REGISTER_COMMAND(ASpit, xastartupbtnhide);    // Inaccurate but sufficient
+	REGISTER_COMMAND(ASpit, xasetupcomplete);     // Inaccurate but sufficient
+	REGISTER_COMMAND(ASpit, xaatrusopenbook);     // Done
+	REGISTER_COMMAND(ASpit, xaatrusbookback);     // Done
+	REGISTER_COMMAND(ASpit, xaatrusbookprevpage); // Done
+	REGISTER_COMMAND(ASpit, xaatrusbooknextpage); // Done
+//	REGISTER_COMMAND(ASpit, xacathopenbook);
+//	REGISTER_COMMAND(ASpit, xacathbookback);
+//	REGISTER_COMMAND(ASpit, xacathbookprevpage);
+//	REGISTER_COMMAND(ASpit, xacathbooknextpage);
+//	REGISTER_COMMAND(ASpit, xtrapbookback);
+//	REGISTER_COMMAND(ASpit, xatrapbookclose);
+//	REGISTER_COMMAND(ASpit, xatrapbookopen);
+	REGISTER_COMMAND(ASpit, xarestoregame);       // Done
+//	REGISTER_COMMAND(ASpit, xadisablemenureturn);
+//	REGISTER_COMMAND(ASpit, xaenablemenureturn);
+//	REGISTER_COMMAND(ASpit, xalaunchbrowser);
+//	REGISTER_COMMAND(ASpit, xadisablemenuintro);
+//	REGISTER_COMMAND(ASpit, xaenablemenuintro);
+//	REGISTER_COMMAND(ASpit, xademoquit);
+//	REGISTER_COMMAND(ASpit, xaexittomain);
 }
 
 void ASpit::xastartupbtnhide(uint16 argc, uint16 *argv) {
@@ -69,8 +70,9 @@ void ASpit::xastartupbtnhide(uint16 argc, uint16 *argv) {
 void ASpit::xasetupcomplete(uint16 argc, uint16 *argv) {
 	// The original game sets an ini entry to disable the setup button and use the
 	// start button only. It's safe to ignore this part of the command.
-	_vm->_sound->stopSound();
-	_vm->changeToCard(1);
+	uint16 menuCardId = getCardStackId(0xE2E);
+	RivenScriptPtr goToMenuScript = _vm->_scriptMan->createScriptFromData(1, kRivenCommandChangeCard, 1, menuCardId);
+	_vm->_scriptMan->runScript(goToMenuScript, false);
 }
 
 void ASpit::xaatrusopenbook(uint16 argc, uint16 *argv) {
@@ -116,9 +118,7 @@ bool ASpit::pageTurn(RivenTransition transition) {
 	else
 		soundName = "aPage2";
 
-	Common::String fullSoundName = Common::String::format("%d_%s_1", _vm->getCard()->getId(), soundName);
-
-	_vm->_sound->playSound(fullSoundName, 51, true);
+	_vm->_sound->playCardSound(soundName, 51, true);
 
 	// Now update the screen :)
 	_vm->_gfx->scheduleTransition(transition);
diff --git a/engines/mohawk/riven_stacks/bspit.cpp b/engines/mohawk/riven_stacks/bspit.cpp
index c5fedd7..72aa294 100644
--- a/engines/mohawk/riven_stacks/bspit.cpp
+++ b/engines/mohawk/riven_stacks/bspit.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_video.h"
 
 #include "common/events.h"
 
@@ -35,25 +36,25 @@ namespace RivenStacks {
 BSpit::BSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackBspit, "bSliders.190", "bSliderBG.190") {
 
-	REGISTER_COMMAND(BSpit, xblabopenbook);
-	REGISTER_COMMAND(BSpit, xblabbookprevpage);
-	REGISTER_COMMAND(BSpit, xblabbooknextpage);
-	REGISTER_COMMAND(BSpit, xsoundplug);
-	REGISTER_COMMAND(BSpit, xbchangeboiler);
-	REGISTER_COMMAND(BSpit, xbupdateboiler);
-	REGISTER_COMMAND(BSpit, xbsettrap);
-	REGISTER_COMMAND(BSpit, xbcheckcatch);
-	REGISTER_COMMAND(BSpit, xbait);
-	REGISTER_COMMAND(BSpit, xbfreeytram);
-	REGISTER_COMMAND(BSpit, xbaitplate);
-	REGISTER_COMMAND(BSpit, xbisland190_opencard);
-	REGISTER_COMMAND(BSpit, xbisland190_resetsliders);
-	REGISTER_COMMAND(BSpit, xbisland190_slidermd);
-	REGISTER_COMMAND(BSpit, xbisland190_slidermw);
-	REGISTER_COMMAND(BSpit, xbscpbtn);
-	REGISTER_COMMAND(BSpit, xbisland_domecheck);
-	REGISTER_COMMAND(BSpit, xvalvecontrol);
-	REGISTER_COMMAND(BSpit, xbchipper);
+//	REGISTER_COMMAND(BSpit, xblabopenbook);
+//	REGISTER_COMMAND(BSpit, xblabbookprevpage);
+//	REGISTER_COMMAND(BSpit, xblabbooknextpage);
+//	REGISTER_COMMAND(BSpit, xsoundplug);
+//	REGISTER_COMMAND(BSpit, xbchangeboiler);
+//	REGISTER_COMMAND(BSpit, xbupdateboiler);
+//	REGISTER_COMMAND(BSpit, xbsettrap);
+//	REGISTER_COMMAND(BSpit, xbcheckcatch);
+//	REGISTER_COMMAND(BSpit, xbait);
+//	REGISTER_COMMAND(BSpit, xbfreeytram);
+//	REGISTER_COMMAND(BSpit, xbaitplate);
+//	REGISTER_COMMAND(BSpit, xbisland190_opencard);
+//	REGISTER_COMMAND(BSpit, xbisland190_resetsliders);
+//	REGISTER_COMMAND(BSpit, xbisland190_slidermd);
+//	REGISTER_COMMAND(BSpit, xbisland190_slidermw);
+//	REGISTER_COMMAND(BSpit, xbscpbtn);
+//	REGISTER_COMMAND(BSpit, xbisland_domecheck);
+//	REGISTER_COMMAND(BSpit, xvalvecontrol);
+//	REGISTER_COMMAND(BSpit, xbchipper);
 }
 
 void BSpit::xblabopenbook(uint16 argc, uint16 *argv) {
@@ -137,59 +138,59 @@ void BSpit::xbchangeboiler(uint16 argc, uint16 *argv) {
 	uint32 platform = _vm->_vars["bblrgrt"];
 
 	// Stop any background videos
-	_vm->_video->stopVideos();
+	_vm->_video->closeVideos();
 
 	if (argv[0] == 1) {
 		// Water is filling/draining from the boiler
 		if (water == 0) {
 			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(12));
+				_vm->getCard()->playMovie(12);
 			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
+				_vm->getCard()->playMovie(10);
 		} else if (heat == 1) {
 			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(22));
+				_vm->getCard()->playMovie(22);
 			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(19));
+			_vm->getCard()->playMovie(19);
 		} else {
 			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(16));
+				_vm->getCard()->playMovie(16);
 			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(13));
+			_vm->getCard()->playMovie(13);
 		}
 	} else if (argv[0] == 2 && water != 0) {
 		if (heat == 1) {
 			// Turning on the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(23));
+				_vm->getCard()->playMovie(23);
 			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(20));
+			_vm->getCard()->playMovie(20);
 		} else {
 			// Turning off the heat
 			if (platform == 1)
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(18));
+				_vm->getCard()->playMovie(18);
 			else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(15));
+			_vm->getCard()->playMovie(15);
 		}
 	} else if (argv[0] == 3) {
 		if (platform == 1) {
 			// Lowering the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(24));
+					_vm->getCard()->playMovie(24);
 				else
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(17));
+				_vm->getCard()->playMovie(17);
 			} else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
+				_vm->getCard()->playMovie(11);
 		} else {
 			// Raising the platform
 			if (water == 1) {
 				if (heat == 1)
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(21));
+					_vm->getCard()->playMovie(21);
 				else
-					_vm->_video->activateMLST(_vm->getCard()->getMovie(14));
+				_vm->getCard()->playMovie(14);
 			} else
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
+				_vm->getCard()->playMovie(9);
 		}
 	}
 
@@ -198,26 +199,28 @@ void BSpit::xbchangeboiler(uint16 argc, uint16 *argv) {
 	else if (argv[0] == 2)
 		_vm->getCard()->playSound(1);
 
-	_vm->_cursor->setCursor(kRivenHideCursor);
-	_vm->_video->playMovieBlockingRiven(11);
+	RivenVideo *video = _vm->_video->openSlot(11);
+	video->playBlocking();
 }
 
 void BSpit::xbupdateboiler(uint16 argc, uint16 *argv) {
 	if (_vm->_vars["bheat"] != 0) {
 		if (_vm->_vars["bblrgrt"] == 0) {
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
-			_vm->_video->playMovieRiven(8);
+			_vm->getCard()->playMovie(8);
 		} else {
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(7));
-			_vm->_video->playMovieRiven(7);
+			_vm->getCard()->playMovie(7);
 		}
 	} else {
-		VideoEntryPtr video = _vm->_video->findVideoRiven(7);
-		if (video)
-			video->setEnabled(false);
-		video = _vm->_video->findVideoRiven(8);
-		if (video)
-			video->setEnabled(false);
+		RivenVideo *video = _vm->_video->getSlot(7);
+		if (video) {
+			video->disable();
+			video->stop();
+		}
+		video = _vm->_video->getSlot(8);
+		if (video) {
+			video->disable();
+			video->stop();
+		}
 	}
 }
 
@@ -324,17 +327,22 @@ void BSpit::xbfreeytram(uint16 argc, uint16 *argv) {
 			mlstId = 12;
 			break;
 		default:
+			// The original did rand(13, 14)
 			mlstId = _vm->_rnd->getRandomNumberRng(13, 15);
 			break;
 	}
 
-	// Activate the MLST and play the video
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId));
-	_vm->_video->playMovieBlockingRiven(11);
+	// Play the video
+	_vm->getCard()->playMovie(mlstId);
+	RivenVideo *first = _vm->_video->openSlot(11);
+	first->playBlocking();
 
 	// Now play the second movie
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstId + 5));
-	_vm->_video->playMovieBlockingRiven(12);
+	_vm->getCard()->playMovie(mlstId + 5);
+	RivenVideo *second = _vm->_video->openSlot(12);
+	second->playBlocking();
+
+	_vm->getCard()->drawPicture(4);
 }
 
 void BSpit::xbaitplate(uint16 argc, uint16 *argv) {
@@ -432,27 +440,31 @@ void BSpit::xvalvecontrol(uint16 argc, uint16 *argv) {
 						valve = 1;
 						_vm->_cursor->setCursor(kRivenHideCursor);
 						_vm->_system->updateScreen();
-						_vm->_video->playMovieBlockingRiven(2);
+						RivenVideo *video = _vm->_video->openSlot(2);
+						video->playBlocking();
 						_vm->refreshCard();
 					} else if (valve == 1) {
 						if (changeX >= 0 && changeY >= 10) {
 							valve = 0;
 							_vm->_cursor->setCursor(kRivenHideCursor);
 							_vm->_system->updateScreen();
-							_vm->_video->playMovieBlockingRiven(3);
+							RivenVideo *video = _vm->_video->openSlot(3);
+							video->playBlocking();
 							_vm->refreshCard();
 						} else if (changeX <= -10 && changeY <= 10) {
 							valve = 2;
 							_vm->_cursor->setCursor(kRivenHideCursor);
 							_vm->_system->updateScreen();
-							_vm->_video->playMovieBlockingRiven(1);
+							RivenVideo *video = _vm->_video->openSlot(1);
+							video->playBlocking();
 							_vm->refreshCard();
 						}
 					} else if (valve == 2 && changeX >= 10) {
 						valve = 1;
 						_vm->_cursor->setCursor(kRivenHideCursor);
 						_vm->_system->updateScreen();
-						_vm->_video->playMovieBlockingRiven(4);
+						RivenVideo *video = _vm->_video->openSlot(4);
+						video->playBlocking();
 						_vm->refreshCard();
 					}
 					done = true;
@@ -485,8 +497,10 @@ void BSpit::xvalvecontrol(uint16 argc, uint16 *argv) {
 
 void BSpit::xbchipper(uint16 argc, uint16 *argv) {
 	// Why is this an external command....?
-	if (_vm->_vars["bvalve"] == 2)
-		_vm->_video->playMovieBlockingRiven(2);
+	if (_vm->_vars["bvalve"] == 2) {
+		RivenVideo *video = _vm->_video->openSlot(2);
+		video->playBlocking();
+	}
 }
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/domespit.cpp b/engines/mohawk/riven_stacks/domespit.cpp
index 6a1d26b..7aaa7d6 100644
--- a/engines/mohawk/riven_stacks/domespit.cpp
+++ b/engines/mohawk/riven_stacks/domespit.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_video.h"
 
 #include "common/events.h"
 
@@ -44,13 +45,15 @@ DomeSpit::DomeSpit(MohawkEngine_Riven *vm, uint16 id, const char *sliderBmpName,
 
 void DomeSpit::runDomeButtonMovie() {
 	// This command just plays the video of the button moving down and up.
-	_vm->_video->playMovieBlockingRiven(2);
+	// The original displayed images of the button going down
+	RivenVideo *video = _vm->_video->openSlot(2);
+	video->playBlocking();
 }
 
 void DomeSpit::runDomeCheck() {
 	// Check if we clicked while the golden frame was showing
 
-	VideoEntryPtr video = _vm->_video->findVideoRiven(1);
+	const RivenVideo *video = _vm->_video->getSlot(1);
 	assert(video);
 
 	int32 curFrame = video->getCurFrame();
diff --git a/engines/mohawk/riven_stacks/gspit.cpp b/engines/mohawk/riven_stacks/gspit.cpp
index 836dd27..dd41e62 100644
--- a/engines/mohawk/riven_stacks/gspit.cpp
+++ b/engines/mohawk/riven_stacks/gspit.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_video.h"
 
 #include "common/events.h"
 
@@ -35,26 +36,26 @@ namespace RivenStacks {
 GSpit::GSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackGspit, "gsliders.190", "gsliderbg.190") {
 
-	REGISTER_COMMAND(GSpit, xgresetpins);
-	REGISTER_COMMAND(GSpit, xgrotatepins);
-	REGISTER_COMMAND(GSpit, xgpincontrols);
-	REGISTER_COMMAND(GSpit, xgisland25_opencard);
-	REGISTER_COMMAND(GSpit, xgisland25_resetsliders);
-	REGISTER_COMMAND(GSpit, xgisland25_slidermd);
-	REGISTER_COMMAND(GSpit, xgisland25_slidermw);
-	REGISTER_COMMAND(GSpit, xgscpbtn);
-	REGISTER_COMMAND(GSpit, xgisland1490_domecheck);
-	REGISTER_COMMAND(GSpit, xgplateau3160_dopools);
-	REGISTER_COMMAND(GSpit, xgwt200_scribetime);
-	REGISTER_COMMAND(GSpit, xgwt900_scribe);
-	REGISTER_COMMAND(GSpit, xgplaywhark);
-	REGISTER_COMMAND(GSpit, xgrviewer);
-	REGISTER_COMMAND(GSpit, xgwharksnd);
-	REGISTER_COMMAND(GSpit, xglview_prisonoff);
-	REGISTER_COMMAND(GSpit, xglview_villageoff);
-	REGISTER_COMMAND(GSpit, xglviewer);
-	REGISTER_COMMAND(GSpit, xglview_prisonon);
-	REGISTER_COMMAND(GSpit, xglview_villageon);
+//	REGISTER_COMMAND(GSpit, xgresetpins);
+//	REGISTER_COMMAND(GSpit, xgrotatepins);
+//	REGISTER_COMMAND(GSpit, xgpincontrols);
+//	REGISTER_COMMAND(GSpit, xgisland25_opencard);
+//	REGISTER_COMMAND(GSpit, xgisland25_resetsliders);
+//	REGISTER_COMMAND(GSpit, xgisland25_slidermd);
+//	REGISTER_COMMAND(GSpit, xgisland25_slidermw);
+//	REGISTER_COMMAND(GSpit, xgscpbtn);
+//	REGISTER_COMMAND(GSpit, xgisland1490_domecheck);
+//	REGISTER_COMMAND(GSpit, xgplateau3160_dopools);
+//	REGISTER_COMMAND(GSpit, xgwt200_scribetime);
+//	REGISTER_COMMAND(GSpit, xgwt900_scribe);
+//	REGISTER_COMMAND(GSpit, xgplaywhark);
+//	REGISTER_COMMAND(GSpit, xgrviewer);
+//	REGISTER_COMMAND(GSpit, xgwharksnd);
+//	REGISTER_COMMAND(GSpit, xglview_prisonoff);
+//	REGISTER_COMMAND(GSpit, xglview_villageoff);
+//	REGISTER_COMMAND(GSpit, xglviewer);
+//	REGISTER_COMMAND(GSpit, xglview_prisonon);
+//	REGISTER_COMMAND(GSpit, xglview_villageon);
 }
 
 void GSpit::lowerPins() {
@@ -75,10 +76,10 @@ void GSpit::lowerPins() {
 	uint32 &upMovie = _vm->_vars["gupmoov"];
 
 	// Play the video of the pins going down
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(upMovie);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
-	_vm->_video->waitUntilMovieEnds(handle);
+	RivenVideo *video = _vm->_video->openSlot(upMovie);
+	assert(video);
+	video->setBounds(startTime, startTime + 550);
+	video->playBlocking();
 
 	upMovie = 0;
 }
@@ -107,10 +108,10 @@ void GSpit::xgrotatepins(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(12);
 
 	// Play the video of the pins rotating
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(_vm->_vars["gupmoov"]);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 1215, 600));
-	_vm->_video->waitUntilMovieEnds(handle);
+	RivenVideo *video = _vm->_video->openSlot(_vm->_vars["gupmoov"]);
+	assert(video);
+	video->setBounds(startTime, startTime + 1215);
+	video->playBlocking();
 }
 
 void GSpit::xgpincontrols(uint16 argc, uint16 *argv) {
@@ -193,11 +194,11 @@ void GSpit::xgpincontrols(uint16 argc, uint16 *argv) {
 	_vm->_sound->playSound(14);
 
 	// Actually play the movie
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(pinMovieCodes[imagePos - 1]);
+	RivenVideo *handle = _vm->_video->openSlot(pinMovieCodes[imagePos - 1]);
 	assert(handle);
 	uint32 startTime = 9630 - pinPos * 600;
-	handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600));
-	_vm->_video->waitUntilMovieEnds(handle);
+	handle->setBounds(startTime, startTime + 550);
+	handle->playBlocking();
 
 	// Update the relevant variables
 	_vm->_vars["gupmoov"] = pinMovieCodes[imagePos - 1];
@@ -233,7 +234,8 @@ void GSpit::xgplateau3160_dopools(uint16 argc, uint16 *argv) {
 	// Play the deactivation of a pool if one is active and a different one is activated
 	_vm->_cursor->setCursor(kRivenHideCursor);
 	_vm->_system->updateScreen();
-	_vm->_video->playMovieBlockingRiven(_vm->_vars["glkbtns"] * 2);
+	RivenVideo *video = _vm->_video->openSlot(_vm->_vars["glkbtns"] * 2);
+	video->playBlocking();
 }
 
 void GSpit::xgwt200_scribetime(uint16 argc, uint16 *argv) {
@@ -273,10 +275,10 @@ void GSpit::xgrviewer(uint16 argc, uint16 *argv) {
 	uint32 newPos = curPos + buttonPos;
 
 	// Now play the movie
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
-	_vm->_video->waitUntilMovieEnds(handle);
+	RivenVideo *video = _vm->_video->openSlot(1);
+	assert(video);
+	video->setBounds(s_viewerTimeIntervals[curPos], s_viewerTimeIntervals[newPos]);
+	video->playBlocking();
 
 	// Set the new position and let the card's scripts take over again
 	curPos = newPos % 6; // Clip it to 0-5
@@ -307,24 +309,25 @@ void GSpit::xgplaywhark(uint16 argc, uint16 *argv) {
 	// Activate the correct video based on the amount of times we've been visited
 	switch (wharkVisits) {
 		case 1:
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(3));
+			_vm->getCard()->playMovie(3);
 			break;
 		case 2:
 			// One of two random videos
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(4 + _vm->_rnd->getRandomBit()));
+			_vm->getCard()->playMovie(4 + _vm->_rnd->getRandomBit());
 			break;
 		case 3:
 			// One of two random videos
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(6 + _vm->_rnd->getRandomBit()));
+			_vm->getCard()->playMovie(6 + _vm->_rnd->getRandomBit());
 			break;
 		case 4:
 			// Red alert! Shields online! Brace yourself for impact!
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
+			_vm->getCard()->playMovie(8);
 			break;
 	}
 
 	// For whatever reason the devs felt fit, code 31 is used for all of the videos
-	_vm->_video->playMovieBlockingRiven(31);
+	RivenVideo *video = _vm->_video->openSlot(31);
+	video->playBlocking();
 	_vm->refreshCard();
 }
 
@@ -344,10 +347,10 @@ void GSpit::xglviewer(uint16 argc, uint16 *argv) {
 	uint32 newPos = curPos + buttonPos;
 
 	// Now play the movie
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(1);
-	assert(handle);
-	handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600));
-	_vm->_video->waitUntilMovieEnds(handle);
+	RivenVideo *video = _vm->_video->openSlot(1);
+	assert(video);
+	video->setBounds(s_viewerTimeIntervals[curPos], s_viewerTimeIntervals[newPos]);
+	video->playBlocking();
 
 	// Set the new position to the variable
 	curPos = newPos % 6; // Clip it to 0-5
@@ -393,11 +396,12 @@ void GSpit::catherineViewerIdleTimer() {
 		cathState = 3;
 
 	// Begin playing the new movie
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(movie));
-	VideoEntryPtr video = _vm->_video->playMovieRiven(30);
+	_vm->getCard()->playMovie(movie);
+	RivenVideo *video = _vm->_video->openSlot(30);
+	video->play();
 
 	// Reset the timer
-	_vm->installTimer(TIMER(GSpit, catherineViewerIdleTimer), video->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000);
+	_vm->installTimer(TIMER(GSpit, catherineViewerIdleTimer), video->getDuration() + _vm->_rnd->getRandomNumber(60) * 1000);
 }
 
 void GSpit::xglview_prisonon(uint16 argc, uint16 *argv) {
@@ -427,17 +431,19 @@ void GSpit::xglview_prisonon(uint16 argc, uint16 *argv) {
 
 	// Turn on the viewer
 	_vm->_cursor->hideCursor();
-	_vm->_video->playMovieBlockingRiven(turnOnMovie);
+	RivenVideo *turnOn = _vm->_video->openSlot(turnOnMovie);
+	turnOn->playBlocking();
 	_vm->_cursor->showCursor();
 
 	uint32 timeUntilNextMovie;
 
 	// Begin playing a movie immediately if Catherine is already in the viewer
 	if (cathMovie == 8 || (cathMovie >= 13 && cathMovie <= 16)) {
-		_vm->_video->activateMLST(_vm->getCard()->getMovie(cathMovie));
-		VideoEntryPtr video = _vm->_video->playMovieRiven(30);
+		_vm->getCard()->playMovie(cathMovie);
+		RivenVideo *video = _vm->_video->openSlot(30);
+		video->play();
 
-		timeUntilNextMovie = video->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000;
+		timeUntilNextMovie = video->getDuration() + _vm->_rnd->getRandomNumber(60) * 1000;
 	} else {
 		// Otherwise, just redraw the imager
 		timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000;
@@ -458,9 +464,10 @@ void GSpit::xglview_prisonoff(uint16 argc, uint16 *argv) {
 	_vm->removeTimer();
 
 	// Play the 'turn off' movie after stopping any videos still playing
-	_vm->_video->stopVideos();
+	_vm->_video->closeVideos();
 	_vm->_cursor->hideCursor();
-	_vm->_video->playMovieBlockingRiven(5);
+	RivenVideo *video = _vm->_video->openSlot(5);
+	video->playBlocking();
 	_vm->_cursor->showCursor();
 
 	// Redraw the viewer
diff --git a/engines/mohawk/riven_stacks/jspit.cpp b/engines/mohawk/riven_stacks/jspit.cpp
index 1df5501..81994e3 100644
--- a/engines/mohawk/riven_stacks/jspit.cpp
+++ b/engines/mohawk/riven_stacks/jspit.cpp
@@ -35,35 +35,35 @@ namespace RivenStacks {
 JSpit::JSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackJspit, "jsliders.190", "jsliderbg.190") {
 
-	REGISTER_COMMAND(JSpit, xreseticons);
-	REGISTER_COMMAND(JSpit, xicon);
-	REGISTER_COMMAND(JSpit, xcheckicons);
-	REGISTER_COMMAND(JSpit, xtoggleicon);
-	REGISTER_COMMAND(JSpit, xjtunnel103_pictfix);
-	REGISTER_COMMAND(JSpit, xjtunnel104_pictfix);
-	REGISTER_COMMAND(JSpit, xjtunnel105_pictfix);
-	REGISTER_COMMAND(JSpit, xjtunnel106_pictfix);
-	REGISTER_COMMAND(JSpit, xvga1300_carriage);
-	REGISTER_COMMAND(JSpit, xjdome25_resetsliders);
-	REGISTER_COMMAND(JSpit, xjdome25_slidermd);
-	REGISTER_COMMAND(JSpit, xjdome25_slidermw);
-	REGISTER_COMMAND(JSpit, xjscpbtn);
-	REGISTER_COMMAND(JSpit, xjisland3500_domecheck);
-	REGISTER_COMMAND(JSpit, xhandlecontroldown);
-	REGISTER_COMMAND(JSpit, xhandlecontrolmid);
-	REGISTER_COMMAND(JSpit, xhandlecontrolup);
-	REGISTER_COMMAND(JSpit, xjplaybeetle_550);
-	REGISTER_COMMAND(JSpit, xjplaybeetle_600);
-	REGISTER_COMMAND(JSpit, xjplaybeetle_950);
-	REGISTER_COMMAND(JSpit, xjplaybeetle_1050);
-	REGISTER_COMMAND(JSpit, xjplaybeetle_1450);
-	REGISTER_COMMAND(JSpit, xjlagoon700_alert);
-	REGISTER_COMMAND(JSpit, xjlagoon800_alert);
-	REGISTER_COMMAND(JSpit, xjlagoon1500_alert);
-	REGISTER_COMMAND(JSpit, xschool280_playwhark);
-	REGISTER_COMMAND(JSpit, xjschool280_resetleft);
-	REGISTER_COMMAND(JSpit, xjschool280_resetright);
-	REGISTER_COMMAND(JSpit, xjatboundary);
+//	REGISTER_COMMAND(JSpit, xreseticons);
+//	REGISTER_COMMAND(JSpit, xicon);
+//	REGISTER_COMMAND(JSpit, xcheckicons);
+//	REGISTER_COMMAND(JSpit, xtoggleicon);
+//	REGISTER_COMMAND(JSpit, xjtunnel103_pictfix);
+//	REGISTER_COMMAND(JSpit, xjtunnel104_pictfix);
+//	REGISTER_COMMAND(JSpit, xjtunnel105_pictfix);
+//	REGISTER_COMMAND(JSpit, xjtunnel106_pictfix);
+//	REGISTER_COMMAND(JSpit, xvga1300_carriage);
+//	REGISTER_COMMAND(JSpit, xjdome25_resetsliders);
+//	REGISTER_COMMAND(JSpit, xjdome25_slidermd);
+//	REGISTER_COMMAND(JSpit, xjdome25_slidermw);
+//	REGISTER_COMMAND(JSpit, xjscpbtn);
+//	REGISTER_COMMAND(JSpit, xjisland3500_domecheck);
+//	REGISTER_COMMAND(JSpit, xhandlecontroldown);
+//	REGISTER_COMMAND(JSpit, xhandlecontrolmid);
+//	REGISTER_COMMAND(JSpit, xhandlecontrolup);
+//	REGISTER_COMMAND(JSpit, xjplaybeetle_550);
+//	REGISTER_COMMAND(JSpit, xjplaybeetle_600);
+//	REGISTER_COMMAND(JSpit, xjplaybeetle_950);
+//	REGISTER_COMMAND(JSpit, xjplaybeetle_1050);
+//	REGISTER_COMMAND(JSpit, xjplaybeetle_1450);
+//	REGISTER_COMMAND(JSpit, xjlagoon700_alert);
+//	REGISTER_COMMAND(JSpit, xjlagoon800_alert);
+//	REGISTER_COMMAND(JSpit, xjlagoon1500_alert);
+//	REGISTER_COMMAND(JSpit, xschool280_playwhark);
+//	REGISTER_COMMAND(JSpit, xjschool280_resetleft);
+//	REGISTER_COMMAND(JSpit, xjschool280_resetright);
+//	REGISTER_COMMAND(JSpit, xjatboundary);
 }
 
 void JSpit::xreseticons(uint16 argc, uint16 *argv) {
@@ -224,19 +224,23 @@ void JSpit::xvga1300_carriage(uint16 argc, uint16 *argv) {
 
 	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 	_vm->_system->updateScreen();                      // Update
-	_vm->_video->playMovieBlockingRiven(1);            // Play handle movie
+	RivenVideo *handleVideo = _vm->_video->openSlot(1);            // Play handle movie
+	handleVideo->playBlocking();
 	_vm->_gfx->scheduleTransition(kRivenTransitionPanDown);
 	_vm->changeToCard(_vm->getStack()->getCardStackId(0x18e77));  // Change to card facing up
 	_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor (again)
 	_vm->_system->updateScreen();                      // Update
-	_vm->_video->playMovieBlockingRiven(4);            // Play carriage beginning to drop
+	RivenVideo *beginDropVideo = _vm->_video->openSlot(4);            // Play carriage beginning to drop
+	beginDropVideo->playBlocking();
 	_vm->_gfx->scheduleTransition(kRivenTransitionPanUp);
 	_vm->changeToCard(_vm->getStack()->getCardStackId(0x183a9));  // Change to card looking straight again
-	_vm->_video->playMovieBlockingRiven(2);
+	RivenVideo *video = _vm->_video->openSlot(2);
+	video->playBlocking();
 
 	if (_vm->_vars["jgallows"] == 1) {
 		// If the gallows is open, play the up movie and return
-		_vm->_video->playMovieBlockingRiven(3);
+		RivenVideo *upVideo = _vm->_video->openSlot(3);
+		upVideo->playBlocking();
 		return;
 	}
 
@@ -274,10 +278,14 @@ void JSpit::xvga1300_carriage(uint16 argc, uint16 *argv) {
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x18ab5));  // Turn right
 		_vm->_cursor->setCursor(kRivenHideCursor);         // Hide the cursor
 		_vm->_system->updateScreen();                      // Update
-		_vm->_video->playMovieBlockingRiven(1);            // Play carriage ride movie
+		RivenVideo *rideVideo = _vm->_video->openSlot(1);            // Play carriage ride movie
+		rideVideo->playBlocking();
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x17167));  // We have arrived at the top
-	} else
-		_vm->_video->playMovieBlockingRiven(3);            // Too slow!
+	} else {
+
+		RivenVideo *tooSlowVideo = _vm->_video->openSlot(3);            // Too slow!
+		tooSlowVideo->playBlocking();
+	}
 }
 
 void JSpit::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
@@ -339,8 +347,10 @@ void JSpit::xhandlecontrolup(uint16 argc, uint16 *argv) {
 
 	// If we've moved the handle down, go down a floor
 	if (changeLevel == -1) {
-		_vm->_video->playMovieBlockingRiven(1);
-		_vm->_video->playMovieBlockingRiven(2);
+		RivenVideo *firstVideo = _vm->_video->openSlot(1);
+		firstVideo->playBlocking();
+		RivenVideo *secondVideo = _vm->_video->openSlot(2);
+		secondVideo->playBlocking();
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
 	}
 }
@@ -350,8 +360,10 @@ void JSpit::xhandlecontroldown(uint16 argc, uint16 *argv) {
 
 	// If we've moved the handle up, go up a floor
 	if (changeLevel == 1) {
-		_vm->_video->playMovieBlockingRiven(1);
-		_vm->_video->playMovieBlockingRiven(2);
+		RivenVideo *firstVideo = _vm->_video->openSlot(1);
+		firstVideo->playBlocking();
+		RivenVideo *secondVideo = _vm->_video->openSlot(2);
+		secondVideo->playBlocking();
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e374));
 	}
 }
@@ -363,25 +375,31 @@ void JSpit::xhandlecontrolmid(uint16 argc, uint16 *argv) {
 		return;
 
 	// Play the handle moving video
+	RivenVideo *handleVideo;
 	if (changeLevel == 1)
-		_vm->_video->playMovieBlockingRiven(7);
+		handleVideo = _vm->_video->openSlot(7);
 	else
-		_vm->_video->playMovieBlockingRiven(6);
+		handleVideo = _vm->_video->openSlot(6);
+	handleVideo->playBlocking();
 
 	// If the whark's mouth is open, close it
 	uint32 &mouthVar = _vm->_vars["jwmouth"];
 	if (mouthVar == 1) {
-		_vm->_video->playMovieBlockingRiven(3);
-		_vm->_video->playMovieBlockingRiven(8);
+		RivenVideo *closeVideo1 = _vm->_video->openSlot(3);
+		closeVideo1->playBlocking();
+		RivenVideo *closeVideo2 = _vm->_video->openSlot(8);
+		closeVideo2->playBlocking();
 		mouthVar = 0;
 	}
 
 	// Play the elevator video and then change the card
 	if (changeLevel == 1) {
-		_vm->_video->playMovieBlockingRiven(5);
+		RivenVideo *elevatorVideo = _vm->_video->openSlot(5);
+		elevatorVideo->playBlocking();
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e597));
 	} else {
-		_vm->_video->playMovieBlockingRiven(4);
+		RivenVideo *elevatorVideo = _vm->_video->openSlot(4);
+		elevatorVideo->playBlocking();
 		_vm->changeToCard(_vm->getStack()->getCardStackId(0x1e29c));
 	}
 }
@@ -420,7 +438,7 @@ void JSpit::xjlagoon700_alert(uint16 argc, uint16 *argv) {
 		return;
 	}
 
-	VideoEntryPtr sunnerAlertVideo = _vm->_video->playMovieRiven(1);
+	RivenVideo *sunnerAlertVideo = _vm->_video->openSlot(1);
 
 	// Wait for a click while the alert video is playing
 	sunnersPlayVideo(sunnerAlertVideo, 0x7BEB);
@@ -433,14 +451,16 @@ void JSpit::xjlagoon800_alert(uint16 argc, uint16 *argv) {
 
 	if (sunners == 0) {
 		// Show the sunners alert video
-		VideoEntryPtr sunnerAlertVideo = _vm->_video->playMovieRiven(1);
+		RivenVideo *sunnerAlertVideo = _vm->_video->openSlot(1);
 
 		// Wait for a click while the alert video is playing
 		sunnersPlayVideo(sunnerAlertVideo, 0xB6CA);
 	} else if (sunners == 1) {
 		// Show the sunners leaving if you moved forward in their "alert" status
-		_vm->_video->playMovieBlockingRiven(2);
-		_vm->_video->playMovieBlockingRiven(6);
+		RivenVideo *leaving1 = _vm->_video->openSlot(2);
+		leaving1->playBlocking();
+		RivenVideo *leaving2 = _vm->_video->openSlot(6);
+		leaving2->playBlocking();
 		sunners = 2;
 		_vm->refreshCard();
 	}
@@ -453,19 +473,22 @@ void JSpit::xjlagoon1500_alert(uint16 argc, uint16 *argv) {
 
 	if (sunners == 0) {
 		// Show the sunners alert video
-		_vm->_video->playMovieBlockingRiven(3);
+		RivenVideo *alertVideo = _vm->_video->openSlot(3);
+		alertVideo->playBlocking();
 	} else if (sunners == 1) {
 		// Show the sunners leaving if you moved forward in their "alert" status
-		_vm->_video->playMovieBlockingRiven(2);
+		RivenVideo *leavingVideo = _vm->_video->openSlot(2);
+		leavingVideo->playBlocking();
 		sunners = 2;
 		_vm->refreshCard();
 	}
 }
 
-void JSpit::sunnersPlayVideo(VideoEntryPtr &video, uint32 destCardGlobalId) {
+void JSpit::sunnersPlayVideo(RivenVideo *video, uint32 destCardGlobalId) {
 	uint32 &sunners = _vm->_vars["jsunners"];
 
 	mouseForceUp();
+	video->play();
 	while (!video->endOfVideo() && !_vm->shouldQuit()) {
 		_vm->doFrame();
 
@@ -491,7 +514,7 @@ void JSpit::sunnersTopStairsTimer() {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoEntryPtr oldVideo = _vm->_video->findVideoRiven(1);
+	RivenVideo *oldVideo = _vm->_video->getSlot(1);
 	uint32 timerTime = 500;
 
 	if (!oldVideo || oldVideo->endOfVideo()) {
@@ -500,9 +523,10 @@ void JSpit::sunnersTopStairsTimer() {
 		if (sunnerTime == 0) {
 			timerTime = _vm->_rnd->getRandomNumberRng(2, 15) * 1000;
 		} else if (sunnerTime < _vm->getTotalPlayTime()) {
-			VideoEntryPtr video = _vm->_video->playMovieRiven(_vm->_rnd->getRandomNumberRng(1, 3));
+			RivenVideo *video = _vm->_video->openSlot(_vm->_rnd->getRandomNumberRng(1, 3));
+			video->play();
 
-			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(2, 15) * 1000;
+			timerTime = video->getDuration() + _vm->_rnd->getRandomNumberRng(2, 15) * 1000;
 		}
 
 		sunnerTime = timerTime + _vm->getTotalPlayTime();
@@ -521,7 +545,7 @@ void JSpit::sunnersMidStairsTimer() {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoEntryPtr oldVideo = _vm->_video->findVideoRiven(1);
+	RivenVideo *oldVideo = _vm->_video->getSlot(1);
 	uint32 timerTime = 500;
 
 	if (!oldVideo || oldVideo->endOfVideo()) {
@@ -538,9 +562,10 @@ void JSpit::sunnersMidStairsTimer() {
 			else if (randValue == 5)
 				movie = 3;
 
-			VideoEntryPtr video = _vm->_video->playMovieRiven(movie);
+			RivenVideo *video = _vm->_video->openSlot(movie);
+			video->play();
 
-			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(1, 10) * 1000;
+			timerTime = video->getDuration() + _vm->_rnd->getRandomNumberRng(1, 10) * 1000;
 		}
 
 		sunnerTime = timerTime + _vm->getTotalPlayTime();
@@ -559,7 +584,7 @@ void JSpit::sunnersLowerStairsTimer() {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoEntryPtr oldVideo = _vm->_video->findVideoRiven(1);
+	RivenVideo *oldVideo = _vm->_video->getSlot(1);
 	uint32 timerTime = 500;
 
 	if (!oldVideo || oldVideo->endOfVideo()) {
@@ -568,9 +593,10 @@ void JSpit::sunnersLowerStairsTimer() {
 		if (sunnerTime == 0) {
 			timerTime = _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
 		} else if (sunnerTime < _vm->getTotalPlayTime()) {
-			VideoEntryPtr video = _vm->_video->playMovieRiven(_vm->_rnd->getRandomNumberRng(3, 5));
+			RivenVideo *video = _vm->_video->openSlot(_vm->_rnd->getRandomNumberRng(3, 5));
+			video->play();
 
-			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+			timerTime = video->getDuration() + _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
 		}
 
 		sunnerTime = timerTime + _vm->getTotalPlayTime();
@@ -589,7 +615,7 @@ void JSpit::sunnersBeachTimer() {
 	// Play a random sunners video if the script one is not playing already
 	// and then set a new timer for when the new video should be played
 
-	VideoEntryPtr oldvideo = _vm->_video->findVideoRiven(3);
+	RivenVideo *oldvideo = _vm->_video->getSlot(3);
 	uint32 timerTime = 500;
 
 	if (!oldvideo || oldvideo->endOfVideo()) {
@@ -601,10 +627,11 @@ void JSpit::sunnersBeachTimer() {
 			// Unlike the other cards' scripts which automatically
 			// activate the MLST, we have to set it manually here.
 			uint16 mlstID = _vm->_rnd->getRandomNumberRng(3, 8);
-			_vm->_video->activateMLST(_vm->getCard()->getMovie(mlstID));
-			VideoEntryPtr video = _vm->_video->playMovieRiven(mlstID);
+			_vm->getCard()->playMovie(mlstID);
+			RivenVideo *video = _vm->_video->openSlot(mlstID);
+			video->play();
 
-			timerTime = video->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
+			timerTime = video->getDuration() + _vm->_rnd->getRandomNumberRng(1, 30) * 1000;
 		}
 
 		sunnerTime = timerTime + _vm->getTotalPlayTime();
@@ -658,7 +685,8 @@ void JSpit::xschool280_playwhark(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();
 
 	// Play the spin movie
-	_vm->_video->playMovieBlockingRiven(spinMLST);
+	RivenVideo *spinVideo = _vm->_video->openSlot(spinMLST);
+	spinVideo->playBlocking();
 
 	// Get our random number and redraw the area
 	uint16 number = _vm->_rnd->getRandomNumberRng(1, 10);
@@ -667,16 +695,17 @@ void JSpit::xschool280_playwhark(uint16 argc, uint16 *argv) {
 	// Handle movement
 	// (11560/600)s is the length of each of the two movies. We divide it into 19 parts
 	// (one for each of the possible positions the villager can have).
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(doomMLST);
-	Audio::Timestamp startTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
+	RivenVideo *video = _vm->_video->openSlot(doomMLST);
+	uint32 startTime = (11560 / 19) * (*posVar);
 	*posVar += number; // Adjust to the end
-	Audio::Timestamp endTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600);
-	handle->setBounds(startTime, endTime);
-	_vm->_video->waitUntilMovieEnds(handle);
+	uint32 endTime = (11560 / 19) * (*posVar);
+	video->setBounds(startTime, endTime);
+	video->playBlocking();
 
 	if (*posVar > 19) {
 		// The villager has died :(
-		_vm->_video->playMovieBlockingRiven(snackMLST);
+		RivenVideo *snackVideo = _vm->_video->openSlot(snackMLST);
+		snackVideo->playBlocking();
 		redrawWharkNumberPuzzle(overlayPLST, number);
 		*posVar = 0;
 	}
diff --git a/engines/mohawk/riven_stacks/jspit.h b/engines/mohawk/riven_stacks/jspit.h
index 9ce32bc..9bd35a5 100644
--- a/engines/mohawk/riven_stacks/jspit.h
+++ b/engines/mohawk/riven_stacks/jspit.h
@@ -24,7 +24,7 @@
 #define RIVEN_STACKS_JSPIT_H
 
 #include "mohawk/riven_stacks/domespit.h"
-#include "mohawk/video.h"
+#include "mohawk/riven_video.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -94,7 +94,7 @@ private:
 	int jspitElevatorLoop();
 	void redrawWharkNumberPuzzle(uint16 overlay, uint16 number);
 
-	void sunnersPlayVideo(VideoEntryPtr &video, uint32 destCardGlobalId);
+	void sunnersPlayVideo(RivenVideo *video, uint32 destCardGlobalId);
 };
 
 } // End of namespace RivenStacks
diff --git a/engines/mohawk/riven_stacks/ospit.cpp b/engines/mohawk/riven_stacks/ospit.cpp
index 12328de..82e2d34 100644
--- a/engines/mohawk/riven_stacks/ospit.cpp
+++ b/engines/mohawk/riven_stacks/ospit.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
+#include "mohawk/riven_video.h"
 
 #include "common/events.h"
 
@@ -35,14 +36,14 @@ namespace RivenStacks {
 OSpit::OSpit(MohawkEngine_Riven *vm) :
 		RivenStack(vm, kStackOspit) {
 
-	REGISTER_COMMAND(OSpit, xorollcredittime);
-	REGISTER_COMMAND(OSpit, xbookclick);
-	REGISTER_COMMAND(OSpit, xooffice30_closebook);
-	REGISTER_COMMAND(OSpit, xobedroom5_closedrawer);
-	REGISTER_COMMAND(OSpit, xogehnopenbook);
-	REGISTER_COMMAND(OSpit, xogehnbookprevpage);
-	REGISTER_COMMAND(OSpit, xogehnbooknextpage);
-	REGISTER_COMMAND(OSpit, xgwatch);
+//	REGISTER_COMMAND(OSpit, xorollcredittime);
+//	REGISTER_COMMAND(OSpit, xbookclick);
+//	REGISTER_COMMAND(OSpit, xooffice30_closebook);
+//	REGISTER_COMMAND(OSpit, xobedroom5_closedrawer);
+//	REGISTER_COMMAND(OSpit, xogehnopenbook);
+//	REGISTER_COMMAND(OSpit, xogehnbookprevpage);
+//	REGISTER_COMMAND(OSpit, xogehnbooknextpage);
+//	REGISTER_COMMAND(OSpit, xgwatch);
 }
 
 void OSpit::xorollcredittime(uint16 argc, uint16 *argv) {
@@ -72,7 +73,7 @@ void OSpit::xbookclick(uint16 argc, uint16 *argv) {
 	_vm->_system->updateScreen();
 
 	// Let's hook onto our video
-	VideoEntryPtr video = _vm->_video->findVideoRiven(argv[0]);
+	RivenVideo *video = _vm->_video->getSlot(argv[0]);
 
 	// Convert from the standard QuickTime base time to milliseconds
 	// The values are in terms of 1/600 of a second.
@@ -93,8 +94,8 @@ void OSpit::xbookclick(uint16 argc, uint16 *argv) {
 
 	// Just let the video play while we wait until Gehn opens the trap book for us
 	while (video->getTime() < startTime && !_vm->shouldQuit()) {
-		if (_vm->_video->updateMovies())
-			_vm->_system->updateScreen();
+		_vm->_video->updateMovies();
+		_vm->_system->updateScreen();
 
 		Common::Event event;
 		while (_vm->_system->getEventManager()->pollEvent(event))
@@ -118,7 +119,7 @@ void OSpit::xbookclick(uint16 argc, uint16 *argv) {
 	// OK, Gehn has opened the trap book and has asked us to go in. Let's watch
 	// and see what the player will do...
 	while (video->getTime() < endTime && !_vm->shouldQuit()) {
-		bool updateScreen = _vm->_video->updateMovies();
+		_vm->_video->updateMovies();
 
 		Common::Event event;
 		while (_vm->_system->getEventManager()->pollEvent(event)) {
@@ -128,18 +129,18 @@ void OSpit::xbookclick(uint16 argc, uint16 *argv) {
 						_vm->_cursor->setCursor(kRivenOpenHandCursor);
 					else
 						_vm->_cursor->setCursor(kRivenMainCursor);
-					updateScreen = true;
 					break;
 				case Common::EVENT_LBUTTONUP:
 					if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos())) {
 						// OK, we've used the trap book! We go for ride lady!
 						_vm->_scriptMan->stopAllScripts();                  // Stop all running scripts (so we don't remain in the cage)
-						_vm->_video->stopVideos();                          // Stop all videos
+						_vm->_video->closeVideos();                          // Stop all videos
 						_vm->_cursor->setCursor(kRivenHideCursor);          // Hide the cursor
 						_vm->getCard()->drawPicture(3);                  // Black out the screen
 						_vm->_sound->playSound(0);                          // Play the link sound
-						_vm->_video->activateMLST(_vm->getCard()->getMovie(7));    // Activate Gehn Link Video
-						_vm->_video->playMovieBlockingRiven(1);             // Play Gehn Link Video
+						_vm->getCard()->playMovie(7);    // Activate Gehn Link Video
+						RivenVideo *linkVideo = _vm->_video->openSlot(1);             // Play Gehn Link Video
+						linkVideo->playBlocking();
 						_vm->_vars["agehn"] = 4;                            // Set Gehn to the trapped state
 						_vm->_vars["atrapbook"] = 1;                        // We've got the trap book again
 						_vm->_sound->playSound(0);                          // Play the link sound again
@@ -152,8 +153,7 @@ void OSpit::xbookclick(uint16 argc, uint16 *argv) {
 			}
 		}
 
-		if (updateScreen && !_vm->shouldQuit())
-			_vm->_system->updateScreen();
+		_vm->_system->updateScreen();
 
 		_vm->_system->delayMillis(10);
 	}
@@ -176,7 +176,7 @@ void OSpit::xbookclick(uint16 argc, uint16 *argv) {
 	}
 
 	// There was no click, so just play the rest of the video.
-	_vm->_video->waitUntilMovieEnds(video);
+	video->playBlocking();
 }
 
 void OSpit::xooffice30_closebook(uint16 argc, uint16 *argv) {
@@ -189,7 +189,8 @@ void OSpit::xooffice30_closebook(uint16 argc, uint16 *argv) {
 	book = 0;
 
 	// Play the movie
-	_vm->_video->playMovieBlockingRiven(1);
+	RivenVideo *video = _vm->_video->openSlot(1);
+	video->playBlocking();
 
 	// Set the hotspots into their correct states
 	RivenHotspot *closeBook = _vm->getCard()->getHotspotByName("closeBook");
@@ -207,7 +208,8 @@ void OSpit::xooffice30_closebook(uint16 argc, uint16 *argv) {
 
 void OSpit::xobedroom5_closedrawer(uint16 argc, uint16 *argv) {
 	// Close the drawer if open when clicking on the journal.
-	_vm->_video->playMovieBlockingRiven(2);
+	RivenVideo *video = _vm->_video->openSlot(2);
+	video->playBlocking();
 	_vm->_vars["ostanddrawer"] = 0;
 }
 
@@ -278,8 +280,9 @@ void OSpit::xgwatch(uint16 argc, uint16 *argv) {
 	}
 
 	// Now play the video for the watch
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(1));
-	_vm->_video->playMovieBlockingRiven(1);
+	_vm->getCard()->playMovie(1);
+	RivenVideo *watchVideo = _vm->_video->openSlot(1);
+	watchVideo->playBlocking();
 
 	// And, finally, refresh
 	_vm->refreshCard();
diff --git a/engines/mohawk/riven_stacks/pspit.cpp b/engines/mohawk/riven_stacks/pspit.cpp
index e2bfec0..63f921d 100644
--- a/engines/mohawk/riven_stacks/pspit.cpp
+++ b/engines/mohawk/riven_stacks/pspit.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_sound.h"
+#include "mohawk/riven_video.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -33,13 +34,13 @@ namespace RivenStacks {
 PSpit::PSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackPspit, "psliders.25", "psliderbg.25") {
 
-	REGISTER_COMMAND(PSpit, xpisland990_elevcombo);
-	REGISTER_COMMAND(PSpit, xpscpbtn);
-	REGISTER_COMMAND(PSpit, xpisland290_domecheck);
-	REGISTER_COMMAND(PSpit, xpisland25_opencard);
-	REGISTER_COMMAND(PSpit, xpisland25_resetsliders);
-	REGISTER_COMMAND(PSpit, xpisland25_slidermd);
-	REGISTER_COMMAND(PSpit, xpisland25_slidermw);
+//	REGISTER_COMMAND(PSpit, xpisland990_elevcombo);
+//	REGISTER_COMMAND(PSpit, xpscpbtn);
+//	REGISTER_COMMAND(PSpit, xpisland290_domecheck);
+//	REGISTER_COMMAND(PSpit, xpisland25_opencard);
+//	REGISTER_COMMAND(PSpit, xpisland25_resetsliders);
+//	REGISTER_COMMAND(PSpit, xpisland25_slidermd);
+//	REGISTER_COMMAND(PSpit, xpisland25_slidermw);
 }
 
 void PSpit::catherineIdleTimer() {
@@ -67,9 +68,10 @@ void PSpit::catherineIdleTimer() {
 		cathState = 1;
 
 	// Play the movie, blocking
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(movie));
+	_vm->getCard()->playMovie(movie);
 	_vm->_cursor->hideCursor();
-	_vm->_video->playMovieBlockingRiven(movie);
+	RivenVideo *video = _vm->_video->openSlot(movie);
+	video->playBlocking();
 	_vm->_cursor->showCursor();
 	_vm->_system->updateScreen();
 
diff --git a/engines/mohawk/riven_stacks/rspit.cpp b/engines/mohawk/riven_stacks/rspit.cpp
index 49fac73..bfb305e 100644
--- a/engines/mohawk/riven_stacks/rspit.cpp
+++ b/engines/mohawk/riven_stacks/rspit.cpp
@@ -26,6 +26,7 @@
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_inventory.h"
+#include "mohawk/riven_video.h"
 
 namespace Mohawk {
 namespace RivenStacks {
@@ -33,10 +34,10 @@ namespace RivenStacks {
 RSpit::RSpit(MohawkEngine_Riven *vm) :
 		RivenStack(vm, kStackRspit) {
 
-	REGISTER_COMMAND(RSpit, xrshowinventory);
-	REGISTER_COMMAND(RSpit, xrhideinventory);
-	REGISTER_COMMAND(RSpit, xrcredittime);
-	REGISTER_COMMAND(RSpit, xrwindowsetup);
+//	REGISTER_COMMAND(RSpit, xrshowinventory);
+//	REGISTER_COMMAND(RSpit, xrhideinventory);
+//	REGISTER_COMMAND(RSpit, xrcredittime);
+//	REGISTER_COMMAND(RSpit, xrwindowsetup);
 }
 
 void RSpit::xrcredittime(uint16 argc, uint16 *argv) {
@@ -64,11 +65,12 @@ void RSpit::xrhideinventory(uint16 argc, uint16 *argv) {
 void RSpit::rebelPrisonWindowTimer() {
 	// Randomize a video out in the middle of Tay
 	uint16 movie = _vm->_rnd->getRandomNumberRng(2, 13);
-	_vm->_video->activateMLST(_vm->getCard()->getMovie(movie));
-	VideoEntryPtr handle = _vm->_video->playMovieRiven(movie);
+	_vm->getCard()->playMovie(movie);
+	RivenVideo *video = _vm->_video->openSlot(movie);
+	video->play();
 
 	// Ensure the next video starts after this one ends
-	uint32 timeUntilNextVideo = handle->getDuration().msecs() + _vm->_rnd->getRandomNumberRng(38, 58) * 1000;
+	uint32 timeUntilNextVideo = video->getDuration() + _vm->_rnd->getRandomNumberRng(38, 58) * 1000;
 
 	// Save the time in case we leave the card and return
 	_vm->_vars["rvillagetime"] = timeUntilNextVideo + _vm->getTotalPlayTime();
diff --git a/engines/mohawk/riven_stacks/tspit.cpp b/engines/mohawk/riven_stacks/tspit.cpp
index 1903a89..89eb165 100644
--- a/engines/mohawk/riven_stacks/tspit.cpp
+++ b/engines/mohawk/riven_stacks/tspit.cpp
@@ -27,6 +27,7 @@
 #include "mohawk/riven_card.h"
 #include "mohawk/riven_graphics.h"
 #include "mohawk/riven_inventory.h"
+#include "mohawk/riven_video.h"
 
 #include "common/events.h"
 
@@ -36,29 +37,32 @@ namespace RivenStacks {
 TSpit::TSpit(MohawkEngine_Riven *vm) :
 		DomeSpit(vm, kStackTspit, "tsliders.190", "tsliderbg.190") {
 
-	REGISTER_COMMAND(TSpit, xtexterior300_telescopedown);
-	REGISTER_COMMAND(TSpit, xtexterior300_telescopeup);
-	REGISTER_COMMAND(TSpit, xtisland390_covercombo);
-	REGISTER_COMMAND(TSpit, xtatrusgivesbooks);
-	REGISTER_COMMAND(TSpit, xtchotakesbook);
-	REGISTER_COMMAND(TSpit, xthideinventory);
-	REGISTER_COMMAND(TSpit, xt7500_checkmarbles);
-	REGISTER_COMMAND(TSpit, xt7600_setupmarbles);
-	REGISTER_COMMAND(TSpit, xt7800_setup);
-	REGISTER_COMMAND(TSpit, xdrawmarbles);
-	REGISTER_COMMAND(TSpit, xtakeit);
-	REGISTER_COMMAND(TSpit, xtscpbtn);
-	REGISTER_COMMAND(TSpit, xtisland4990_domecheck);
-	REGISTER_COMMAND(TSpit, xtisland5056_opencard);
-	REGISTER_COMMAND(TSpit, xtisland5056_resetsliders);
-	REGISTER_COMMAND(TSpit, xtisland5056_slidermd);
-	REGISTER_COMMAND(TSpit, xtisland5056_slidermw);
-	REGISTER_COMMAND(TSpit, xtatboundary);
+	REGISTER_COMMAND(TSpit, xtexterior300_telescopedown); // TODO: Check endgame
+	REGISTER_COMMAND(TSpit, xtexterior300_telescopeup);   // TODO: Check endgame
+	REGISTER_COMMAND(TSpit, xtisland390_covercombo); // Done
+	REGISTER_COMMAND(TSpit, xtatrusgivesbooks);      // Done
+	REGISTER_COMMAND(TSpit, xtchotakesbook);         // Done
+	REGISTER_COMMAND(TSpit, xthideinventory);        // Done
+//	REGISTER_COMMAND(TSpit, xt7500_checkmarbles);
+//	REGISTER_COMMAND(TSpit, xt7600_setupmarbles);
+//	REGISTER_COMMAND(TSpit, xt7800_setup);
+//	REGISTER_COMMAND(TSpit, xdrawmarbles);
+//	REGISTER_COMMAND(TSpit, xtakeit);
+//	REGISTER_COMMAND(TSpit, xtscpbtn);
+//	REGISTER_COMMAND(TSpit, xtisland4990_domecheck);
+//	REGISTER_COMMAND(TSpit, xtisland5056_opencard);
+//	REGISTER_COMMAND(TSpit, xtisland5056_resetsliders);
+//	REGISTER_COMMAND(TSpit, xtisland5056_slidermd);
+//	REGISTER_COMMAND(TSpit, xtisland5056_slidermw);
+//	REGISTER_COMMAND(TSpit, xtatboundary);
 }
 
 void TSpit::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
 	// First, show the button movie
-	_vm->_video->playMovieBlockingRiven(3);
+	RivenVideo *buttonVideo = _vm->_video->openSlot(3);
+	buttonVideo->seek(0);
+	buttonVideo->enable();
+	buttonVideo->playBlocking();
 
 	// Don't do anything else if the telescope power is off
 	if (_vm->_vars["ttelevalve"] == 0)
@@ -67,61 +71,41 @@ void TSpit::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
 	uint32 &telescopePos = _vm->_vars["ttelescope"];
 	uint32 &telescopeCover = _vm->_vars["ttelecover"];
 
-	if (telescopePos == 1) {
-		// We're at the bottom, which means one of two things can happen...
-		if (telescopeCover == 1 && _vm->_vars["ttelepin"] == 1) {
-			// ...if the cover is open and the pin is up, the game is now over.
-			if (_vm->_vars["pcage"] == 2) {
-				// The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
-				// And now we fall back to Earth... all the way...
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(8));
-				runEndGame(8, 5000);
-			} else if (_vm->_vars["agehn"] == 4) {
-				// The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
-				// Nice going! Catherine and the islanders are all dead now! Just go back to your home...
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(9));
-				runEndGame(9, 5000);
-			} else if (_vm->_vars["atrapbook"] == 1) {
-				// The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
-				// And then you get shot by Cho. Nice going! Catherine and the islanders are dead
-				// and you have just set Gehn free from Riven, not to mention you're dead.
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(10));
-				runEndGame(10, 5000);
-			} else {
-				// The impossible ending: You don't have Catherine's journal and yet you were somehow
-				// able to open the hatch on the telescope. The game provides an ending for those who
-				// cheat, load a saved game with the combo, or just guess the telescope combo. Atrus
-				// doesn't come and you just fall into the fissure.
-				_vm->_video->activateMLST(_vm->getCard()->getMovie(11));
-				runEndGame(11, 5000);
-			}
-		} else {
-			// ...the telescope can't move down anymore.
-			// Play the sound of not being able to move
-			_vm->_cursor->setCursor(kRivenHideCursor);
-			_vm->_system->updateScreen();
-			_vm->_sound->playSound(13);
-		}
-	} else {
+	if (telescopePos != 1) {
 		// We're not at the bottom, and we can move down again
 
 		// Play a piece of the moving down movie
-		static const uint32 timeIntervals[] = { 4320, 3440, 2560, 1760, 880, 0 };
+		static const uint32 timeIntervals[] = { 4320, 3440, 2660, 1760, 880, 0 };
 		uint16 movieCode = telescopeCover ? 1 : 2;
-		VideoEntryPtr handle = _vm->_video->playMovieRiven(movieCode);
-		handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos], 600), Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600));
-		_vm->_sound->playSound(14); // Play the moving sound
-		_vm->_video->waitUntilMovieEnds(handle);
+		RivenVideo *video = _vm->_video->openSlot(movieCode);
+		video->enable();
+		video->setBounds(timeIntervals[telescopePos], timeIntervals[telescopePos - 1] + 7);
+		_vm->_sound->playCardSound("tTeleMove"); // Play the moving sound
+		video->playBlocking();
 
 		// Now move the telescope down a position and refresh
 		telescopePos--;
-		_vm->refreshCard();
+		_vm->getCard()->enter(false);
+		return;
+	}
+
+	// We're at the bottom, which means one of two things can happen...
+	if (telescopeCover == 1 && _vm->_vars["ttelepin"] == 1) {
+		// ...if the cover is open and the pin is up, the game is now over.
+		xtopenfissure();
+	} else {
+		// ...the telescope can't move down anymore.
+		// Play the sound of not being able to move
+		_vm->_sound->playCardSound("tTelDnMore");
 	}
 }
 
 void TSpit::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
 	// First, show the button movie
-	_vm->_video->playMovieBlockingRiven(3);
+	RivenVideo *buttonVideo = _vm->_video->openSlot(3);
+	buttonVideo->seek(0);
+	buttonVideo->enable();
+	buttonVideo->playBlocking();
 
 	// Don't do anything else if the telescope power is off
 	if (_vm->_vars["ttelevalve"] == 0)
@@ -132,23 +116,49 @@ void TSpit::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
 	// Check if we can't move up anymore
 	if (telescopePos == 5) {
 		// Play the sound of not being able to move
-		_vm->_cursor->setCursor(kRivenHideCursor);
-		_vm->_system->updateScreen();
-		_vm->_sound->playSound(13);
+		_vm->_sound->playCardSound("tTelDnMore");
 		return;
 	}
 
 	// Play a piece of the moving up movie
 	static const