diff options
Diffstat (limited to 'engines/mohawk/riven_external.cpp')
| -rw-r--r-- | engines/mohawk/riven_external.cpp | 941 | 
1 files changed, 743 insertions, 198 deletions
| diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index 67d621a54c..21464a6a48 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -34,8 +34,12 @@  namespace Mohawk { +static const uint32 kDomeSliderDefaultState = 0x01F00000; +static const uint32 kDomeSliderSlotCount = 25; +  RivenExternal::RivenExternal(MohawkEngine_Riven *vm) : _vm(vm) {  	setupCommands(); +	_sliderState = kDomeSliderDefaultState;  }  RivenExternal::~RivenExternal() { @@ -64,6 +68,8 @@ void RivenExternal::setupCommands() {  	COMMAND(xadisablemenureturn);  	COMMAND(xaenablemenureturn);  	COMMAND(xalaunchbrowser); +	COMMAND(xadisablemenuintro); +	COMMAND(xaenablemenuintro);  	// bspit (Bookmaking Island) external commands  	COMMAND(xblabopenbook); @@ -200,16 +206,33 @@ void RivenExternal::runCommand(uint16 argc, uint16 *argv) {  }  void RivenExternal::runDemoBoundaryDialog() { -	GUI::MessageDialog dialog("This demo does not allow you\n" -							  "to visit that part of Riven."); +	GUI::MessageDialog dialog("Exploration beyond this point available only within the full version of\n" +							  "the game.");  	dialog.runModal();  }  void RivenExternal::runEndGame(uint16 video) {  	_vm->_sound->stopAllSLST(); -	_vm->_video->playMovieBlocking(video); +	_vm->_video->playMovie(video); +	runCredits(video); +} +void RivenExternal::runCredits(uint16 video) {  	// TODO: Play until the last frame and then run the credits + +	VideoHandle videoHandle = _vm->_video->findVideoHandle(video); + +	while (!_vm->_video->endOfVideo(videoHandle) && !_vm->shouldQuit()) { +		if (_vm->_video->updateBackgroundMovies()) +			_vm->_system->updateScreen(); + +		Common::Event event; +		while (_vm->_system->getEventManager()->pollEvent(event)) +			; + +		_vm->_system->delayMillis(10); +	} +  	_vm->setGameOver();  } @@ -231,7 +254,151 @@ void RivenExternal::runDomeCheck() {  	// 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->matchVarToString("domecheck") = 1; +		*_vm->getVar("domecheck") = 1; +} + +void RivenExternal::resetDomeSliders(uint16 bitmapId, 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(bitmapId, startHotspot); +				_vm->_system->delayMillis(10); +			} +		} +	} + +	// 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(uint16 resetSlidersHotspot, uint16 openDomeHotspot) { +	// Let's see if we're all matched up... +	if (*_vm->getVar("adomecombo") == _sliderState) { +		// Set the button hotspot to the open dome hotspot +		_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; +	} +} + +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 (_sliderState & (1 << (24 - i))) +				_vm->_gfx->changeCursor(kRivenOpenHandCursor); +			else +				_vm->_gfx->changeCursor(kRivenMainCursor); +			break; +		} +	} +} + +void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot) { +	int16 foundSlider = -1; + +	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) { +		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; + +			foundSlider = i; +			break; +		} +	} + +	// We're not over any slider +	if (foundSlider < 0) +		return; + +	// We've clicked down, so show the closed hand cursor +	_vm->_gfx->changeCursor(kRivenClosedHandCursor); + +	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))) && _vm->_hotspots[foundSlider + startHotspot + 1].rect.contains(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(bitmapId, startHotspot); +				} 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--; +					_sliderState |= 1 << (24 - foundSlider); + +					// Now play a click sound and redraw +					_vm->_sound->playSound(soundId); +					drawDomeSliders(bitmapId, 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(resetSlidersHotspot, openDomeHotspot); +} + +void RivenExternal::drawDomeSliders(uint16 bitmapId, 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->getCurStack() == pspit) +		dstAreaRect.translate(-2, 0); + +	for (uint16 i = 0; i < kDomeSliderSlotCount; i++) { +		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; + +		if (_sliderState & (1 << (24 - i))) +			_vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect); +		else +			_vm->_gfx->drawImageRect(bitmapId + 1, srcRect, dstRect); +	} + +	_vm->_gfx->updateScreen();  }  // ------------------------------------------------------------------------------------ @@ -252,7 +419,7 @@ void RivenExternal::xasetupcomplete(uint16 argc, uint16 *argv) {  void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {  	// Get the variable -	uint32 page = *_vm->matchVarToString("aatruspage"); +	uint32 page = *_vm->getVar("aatruspage");  	// Set hotspots depending on the page  	if (page == 1) { @@ -271,13 +438,13 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {  void RivenExternal::xaatrusbookback(uint16 argc, uint16 *argv) {  	// Return to where we were before entering the book -	_vm->changeToStack(*_vm->matchVarToString("returnstackid")); -	_vm->changeToCard(*_vm->matchVarToString("returncardid")); +	_vm->changeToStack(*_vm->getVar("returnstackid")); +	_vm->changeToCard(*_vm->getVar("returncardid"));  }  void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {  	// Get the page variable -	uint32 *page = _vm->matchVarToString("aatruspage"); +	uint32 *page = _vm->getVar("aatruspage");  	// Decrement the page if it's not the first page  	if (*page == 1) @@ -286,9 +453,9 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {  	// Play the page turning sound  	if (_vm->getFeatures() & GF_DEMO) -		_vm->_sound->playSound(4, false); +		_vm->_sound->playSound(4);  	else -		_vm->_sound->playSound(3, false); +		_vm->_sound->playSound(3);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(1); @@ -297,7 +464,7 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {  void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {  	// Get the page variable -	uint32 *page = _vm->matchVarToString("aatruspage"); +	uint32 *page = _vm->getVar("aatruspage");  	// Increment the page if it's not the last page  	if (((_vm->getFeatures() & GF_DEMO) && *page == 6) || *page == 10) @@ -306,9 +473,9 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {  	// Play the page turning sound  	if (_vm->getFeatures() & GF_DEMO) -		_vm->_sound->playSound(5, false); +		_vm->_sound->playSound(5);  	else -		_vm->_sound->playSound(4, false); +		_vm->_sound->playSound(4);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(0); @@ -317,7 +484,7 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {  void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {  	// Get the variable -	uint32 page = *_vm->matchVarToString("acathpage"); +	uint32 page = *_vm->getVar("acathpage");  	// Set hotspots depending on the page  	if (page == 1) { @@ -340,19 +507,33 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {  		_vm->_gfx->drawPLST(51);  	if (page == 28) { -		// TODO: Draw telescope combination +		// 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->getVar("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->matchVarToString("returnstackid")); -	_vm->changeToCard(*_vm->matchVarToString("returncardid")); +	_vm->changeToStack(*_vm->getVar("returnstackid")); +	_vm->changeToCard(*_vm->getVar("returncardid"));  }  void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {  	// Get the variable -	uint32 *page = _vm->matchVarToString("acathpage"); +	uint32 *page = _vm->getVar("acathpage");  	// Increment the page if it's not the first page  	if (*page == 1) @@ -360,7 +541,7 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {  	(*page)--;  	// Play the page turning sound -	_vm->_sound->playSound(5, false); +	_vm->_sound->playSound(5);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(3); @@ -369,7 +550,7 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {  void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {  	// Get the variable -	uint32 *page = _vm->matchVarToString("acathpage"); +	uint32 *page = _vm->getVar("acathpage");  	// Increment the page if it's not the last page  	if (*page == 49) @@ -377,7 +558,7 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {  	(*page)++;  	// Play the page turning sound -	_vm->_sound->playSound(6, false); +	_vm->_sound->playSound(6);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(2); @@ -386,27 +567,27 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {  void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {  	// Return to where we were before entering the book -	*_vm->matchVarToString("atrap") = 0; -	_vm->changeToStack(*_vm->matchVarToString("returnstackid")); -	_vm->changeToCard(*_vm->matchVarToString("returncardid")); +	*_vm->getVar("atrap") = 0; +	_vm->changeToStack(*_vm->getVar("returnstackid")); +	_vm->changeToCard(*_vm->getVar("returncardid"));  }  void RivenExternal::xatrapbookclose(uint16 argc, uint16 *argv) {  	// Close the trap book -	*_vm->matchVarToString("atrap") = 0; +	*_vm->getVar("atrap") = 0;  	// Play the page turning sound -	_vm->_sound->playSound(8, false); +	_vm->_sound->playSound(8);  	_vm->refreshCard();  }  void RivenExternal::xatrapbookopen(uint16 argc, uint16 *argv) {  	// Open the trap book -	*_vm->matchVarToString("atrap") = 1; +	*_vm->getVar("atrap") = 1;  	// Play the page turning sound -	_vm->_sound->playSound(9, false); +	_vm->_sound->playSound(9);  	_vm->refreshCard();  } @@ -417,42 +598,99 @@ void RivenExternal::xarestoregame(uint16 argc, uint16 *argv) {  }  void RivenExternal::xadisablemenureturn(uint16 argc, uint16 *argv) { -	// Dummy function -- implemented in Mohawk::go +	// 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) { -	// Dummy function -- implemented in Mohawk::go +	// 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" -							  "open up a web browser to bring you to\n" -							  "the Riven website. ScummVM cannot do\n" -							  "that. Visit the site on your own."); +							  "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(); +} +  // ------------------------------------------------------------------------------------  // bspit (Bookmaking Island) external commands  // ------------------------------------------------------------------------------------  void RivenExternal::xblabopenbook(uint16 argc, uint16 *argv) {  	// Get the variable -	uint32 page = *_vm->matchVarToString("blabbook"); +	uint32 page = *_vm->getVar("blabbook");  	// Draw the image of the page based on the blabbook variable  	_vm->_gfx->drawPLST(page); -	// TODO: Draw the dome combo  	if (page == 14) { -		warning ("Need to draw dome combo"); +		// 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->getVar("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->matchVarToString("blabbook"); +	uint32 *page = _vm->getVar("blabbook");  	// Decrement the page if it's not the first page  	if (*page == 1) @@ -460,7 +698,7 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {  	(*page)--;  	// Play the page turning sound -	_vm->_sound->playSound(22, false); +	_vm->_sound->playSound(22);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(1); @@ -469,7 +707,7 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {  void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {  	// Get the page variable -	uint32 *page = _vm->matchVarToString("blabbook"); +	uint32 *page = _vm->getVar("blabbook");  	// Increment the page if it's not the last page  	if (*page == 22) @@ -477,7 +715,7 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {  	(*page)++;  	// Play the page turning sound -	_vm->_sound->playSound(23, false); +	_vm->_sound->playSound(23);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(0); @@ -485,8 +723,8 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {  }  void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) { -	uint32 heat = *_vm->matchVarToString("bheat"); -	uint32 boilerInactive = *_vm->matchVarToString("bcratergg"); +	uint32 heat = *_vm->getVar("bheat"); +	uint32 boilerInactive = *_vm->getVar("bcratergg");  	if (heat != 0)  		_vm->_sound->playSLST(1, _vm->getCurCard()); @@ -497,58 +735,64 @@ void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {  }  void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) { -	uint32 heat = *_vm->matchVarToString("bheat"); -	uint32 water = *_vm->matchVarToString("bblrwtr"); -	uint32 platform = *_vm->matchVarToString("bblrgrt"); +	uint32 heat = *_vm->getVar("bheat"); +	uint32 water = *_vm->getVar("bblrwtr"); +	uint32 platform = *_vm->getVar("bblrgrt"); + +	// Stop any background videos +	_vm->_video->stopVideos();  	if (argv[0] == 1) { +		// Water is filling/draining from the boiler  		if (water == 0) { -			if (platform == 0) -				_vm->_video->activateMLST(10, _vm->getCurCard()); -			else +			if (platform == 1)  				_vm->_video->activateMLST(12, _vm->getCurCard()); -		} else if (heat == 0) { -			if (platform == 0) -				_vm->_video->activateMLST(19, _vm->getCurCard());  			else +				_vm->_video->activateMLST(10, _vm->getCurCard()); +		} else if (heat == 1) { +			if (platform == 1)  				_vm->_video->activateMLST(22, _vm->getCurCard()); -		} else { -			if (platform == 0) -				_vm->_video->activateMLST(13, _vm->getCurCard());  			else +				_vm->_video->activateMLST(19, _vm->getCurCard()); +		} else { +			if (platform == 1)  				_vm->_video->activateMLST(16, _vm->getCurCard()); +			else +				_vm->_video->activateMLST(13, _vm->getCurCard());  		}  	} else if (argv[0] == 2 && water != 0) { -		if (heat == 0) { -			if (platform == 0) -				_vm->_video->activateMLST(20, _vm->getCurCard()); -			else +		if (heat == 1) { +			// Turning on the heat +			if (platform == 1)  				_vm->_video->activateMLST(23, _vm->getCurCard()); +			else +				_vm->_video->activateMLST(20, _vm->getCurCard());  		} else { -			if (platform == 0) +			// Turning off the heat +			if (platform == 1)  				_vm->_video->activateMLST(18, _vm->getCurCard());  			else  				_vm->_video->activateMLST(15, _vm->getCurCard());  		}  	} else if (argv[0] == 3) { -		if (platform == 0) { -			if (water == 0) { -				_vm->_video->activateMLST(11, _vm->getCurCard()); -			} else { -				if (heat == 0) -					_vm->_video->activateMLST(17, _vm->getCurCard()); -				else +		if (platform == 1) { +			// Lowering the platform +			if (water == 1) { +				if (heat == 1)  					_vm->_video->activateMLST(24, _vm->getCurCard()); -			} -		} else { -			if (water == 0) { -				_vm->_video->activateMLST(9, _vm->getCurCard()); -			} else { -				if (heat == 0) -					_vm->_video->activateMLST(14, _vm->getCurCard());  				else +					_vm->_video->activateMLST(17, _vm->getCurCard()); +			} else +				_vm->_video->activateMLST(11, _vm->getCurCard()); +		} else { +			// Raising the platform +			if (water == 1) { +				if (heat == 1)  					_vm->_video->activateMLST(21, _vm->getCurCard()); -			} +				else +					_vm->_video->activateMLST(14, _vm->getCurCard()); +			} else +				_vm->_video->activateMLST(9, _vm->getCurCard());  		}  	} @@ -557,27 +801,26 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {  	else if (argv[0] == 2)  		_vm->_sound->playSLST(1, _vm->getCurCard()); -	_vm->_video->playMovie(11); +	_vm->_gfx->changeCursor(kRivenHideCursor); +	_vm->_video->playMovieBlocking(11);  }  void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) { -	uint32 heat = *_vm->matchVarToString("bheat"); -	uint32 platform = *_vm->matchVarToString("bblrgrt"); +	uint32 heat = *_vm->getVar("bheat"); +	uint32 platform = *_vm->getVar("bblrgrt");  	if (heat) {  		if (platform == 0) { -			_vm->_video->activateMLST(7, _vm->getCurCard()); -			_vm->_video->playMovie(7); -		} else {  			_vm->_video->activateMLST(8, _vm->getCurCard());  			_vm->_video->playMovie(8); +		} else { +			_vm->_video->activateMLST(7, _vm->getCurCard()); +			_vm->_video->playMovie(7);  		}  	} else { -		_vm->_video->stopMovie(7); -		_vm->_video->stopMovie(8); +		_vm->_video->disableMovie(7); +		_vm->_video->disableMovie(8);  	} - -	_vm->refreshCard();  }  void RivenExternal::xbsettrap(uint16 argc, uint16 *argv) { @@ -613,7 +856,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->matchVarToString("bbait") = 1; +		*_vm->getVar("bbait") = 1;  		_vm->_gfx->drawPLST(4);  		_vm->_gfx->updateScreen();  		_vm->_hotspots[3].enabled = false; // Disable bait hotspot @@ -652,32 +895,32 @@ 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->matchVarToString("bbait") = 1; +		*_vm->getVar("bbait") = 1;  		_vm->_gfx->drawPLST(4);  		_vm->_gfx->updateScreen();  		_vm->_hotspots[3].enabled = false; // Disable bait hotspot  		_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot  	} else { -		*_vm->matchVarToString("bbait") = 0; +		*_vm->getVar("bbait") = 0;  		_vm->_hotspots[3].enabled = true; // Enable bait hotspot  		_vm->_hotspots[9].enabled = false; // Disable baitplate hotspot  	}  }  void RivenExternal::xbisland190_opencard(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkDomeSliders(27, 28);  }  void RivenExternal::xbisland190_resetsliders(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	resetDomeSliders(701, 41, 2);  }  void RivenExternal::xbisland190_slidermd(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	dragDomeSlider(701, 41, 27, 28, 2);  }  void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkSliderCursorChange(2);  }  void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) { @@ -689,15 +932,17 @@ void RivenExternal::xbisland_domecheck(uint16 argc, uint16 *argv) {  }  void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) { +	Common::Point startPos = _vm->_system->getEventManager()->getMousePos(); +  	// Get the variable for the valve -	uint32 *valve = _vm->matchVarToString("bvalve"); +	uint32 *valve = _vm->getVar("bvalve");  	int changeX = 0;  	int changeY = 0;  	bool done = false;  	// Set the cursor to the closed position -	_vm->_gfx->changeCursor(2004); +	_vm->_gfx->changeCursor(kRivenClosedHandCursor);  	_vm->_system->updateScreen();  	while (!done) { @@ -706,8 +951,8 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {  		while (_vm->_system->getEventManager()->pollEvent(event)) {  			switch (event.type) {  			case Common::EVENT_MOUSEMOVE: -				changeX = event.mouse.x - _vm->_mousePos.x; -				changeY = _vm->_mousePos.y - event.mouse.y; +				changeX = event.mouse.x - startPos.x; +				changeY = startPos.y - event.mouse.y;  				_vm->_system->updateScreen();  				break;  			case Common::EVENT_LBUTTONUP: @@ -746,26 +991,26 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {  	// 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->matchVarToString("bidvlv") == 1) { // Check which way the water is going at the boiler -			if (*_vm->matchVarToString("bblrarm") == 1) { +		if (*_vm->getVar("bidvlv") == 1) { // Check which way the water is going at the boiler +			if (*_vm->getVar("bblrarm") == 1) {  				// If the pipe is open, make sure the water is drained out -				*_vm->matchVarToString("bheat") = 0; -				*_vm->matchVarToString("bblrwtr") = 0; +				*_vm->getVar("bheat") = 0; +				*_vm->getVar("bblrwtr") = 0;  			} else {  				// If the pipe is closed, fill the boiler again -				*_vm->matchVarToString("bheat") = *_vm->matchVarToString("bblrvalve"); -				*_vm->matchVarToString("bblrwtr") = 1; +				*_vm->getVar("bheat") = *_vm->getVar("bblrvalve"); +				*_vm->getVar("bblrwtr") = 1;  			}  		} else {  			// Have the grating inside the boiler match the switch outside -			*_vm->matchVarToString("bblrgrt") = (*_vm->matchVarToString("bblrsw") == 1) ? 0 : 1; +			*_vm->getVar("bblrgrt") = (*_vm->getVar("bblrsw") == 1) ? 0 : 1;  		}  	}  }  void RivenExternal::xbchipper(uint16 argc, uint16 *argv) {  	// Why is this an external command....? -	if (*_vm->matchVarToString("bvalve") == 2) +	if (*_vm->getVar("bvalve") == 2)  		_vm->_video->playMovieBlocking(2);  } @@ -786,19 +1031,19 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {  }  void RivenExternal::xgisland25_opencard(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkDomeSliders(29, 30);  }  void RivenExternal::xgisland25_resetsliders(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	resetDomeSliders(161, 16, 2);  }  void RivenExternal::xgisland25_slidermd(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	dragDomeSlider(161, 16, 29, 30, 2);  }  void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkSliderCursorChange(2);  }  void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) { @@ -810,18 +1055,20 @@ void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) {  }  void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) { -	// TODO: "Bubble" map related +	// Play the deactivation of a pool if one is active and a different one is activated +	_vm->_gfx->changeCursor(kRivenHideCursor); +	_vm->_video->playMovieBlocking(*_vm->getVar("glkbtns") * 2);  }  void RivenExternal::xgwt200_scribetime(uint16 argc, uint16 *argv) {  	// Get the current time -	*_vm->matchVarToString("gscribetime") = _vm->_system->getMillis(); +	*_vm->getVar("gscribetime") = _vm->_system->getMillis();  }  void RivenExternal::xgwt900_scribe(uint16 argc, uint16 *argv) { -	uint32 *scribeVar = _vm->matchVarToString("gscribe"); +	uint32 *scribeVar = _vm->getVar("gscribe"); -	if (*scribeVar == 1 && _vm->_system->getMillis() > *_vm->matchVarToString("gscribetime") + 40000) +	if (*scribeVar == 1 && _vm->_system->getMillis() > *_vm->getVar("gscribetime") + 40000)  		*scribeVar = 2;  } @@ -863,9 +1110,9 @@ void RivenExternal::xglview_villageon(uint16 argc, uint16 *argv) {  void RivenExternal::xreseticons(uint16 argc, uint16 *argv) {  	// Reset the icons when going to Tay (rspit) -	*_vm->matchVarToString("jicons") = 0; -	*_vm->matchVarToString("jiconorder") = 0; -	*_vm->matchVarToString("jrbook") = 0; +	*_vm->getVar("jicons") = 0; +	*_vm->getVar("jiconorder") = 0; +	*_vm->getVar("jrbook") = 0;  }  // Count up how many icons are pressed @@ -886,30 +1133,30 @@ static byte countDepressedIcons(uint32 iconOrderVar) {  void RivenExternal::xicon(uint16 argc, uint16 *argv) {  	// Set atemp as the status of whether or not the icon can be depressed. -	if (*_vm->matchVarToString("jicons") & (1 << (argv[0] - 1))) { +	if (*_vm->getVar("jicons") & (1 << (argv[0] - 1))) {  		// This icon is depressed. Allow depression only if the last depressed icon was this one. -		if ((*_vm->matchVarToString("jiconorder") & 0x1f) == argv[0]) -			*_vm->matchVarToString("atemp") = 1; +		if ((*_vm->getVar("jiconorder") & 0x1f) == argv[0]) +			*_vm->getVar("atemp") = 1;  		else -			*_vm->matchVarToString("atemp") = 2; +			*_vm->getVar("atemp") = 2;  	} else -		*_vm->matchVarToString("atemp") = 0; +		*_vm->getVar("atemp") = 0;  }  void RivenExternal::xcheckicons(uint16 argc, uint16 *argv) {  	// Reset the icons if this is the sixth icon -	uint32 *iconOrderVar = _vm->matchVarToString("jiconorder"); +	uint32 *iconOrderVar = _vm->getVar("jiconorder");  	if (countDepressedIcons(*iconOrderVar) == 5) {  		*iconOrderVar = 0; -		*_vm->matchVarToString("jicons") = 0; -		_vm->_sound->playSound(46, false); +		*_vm->getVar("jicons") = 0; +		_vm->_sound->playSound(46);  	}  }  void RivenExternal::xtoggleicon(uint16 argc, uint16 *argv) {  	// Get the variables -	uint32 *iconsDepressed = _vm->matchVarToString("jicons"); -	uint32 *iconOrderVar = _vm->matchVarToString("jiconorder"); +	uint32 *iconsDepressed = _vm->getVar("jicons"); +	uint32 *iconOrderVar = _vm->getVar("jiconorder");  	if (*iconsDepressed & (1 << (argv[0] - 1))) {  		// The icon is depressed, now unpress it @@ -922,13 +1169,13 @@ void RivenExternal::xtoggleicon(uint16 argc, uint16 *argv) {  	}  	// Check if the puzzle is complete now and assign 1 to jrbook if the puzzle is complete. -	if (*iconOrderVar == *_vm->matchVarToString("jiconcorrectorder")) -		*_vm->matchVarToString("jrbook") = 1; +	if (*iconOrderVar == *_vm->getVar("jiconcorrectorder")) +		*_vm->getVar("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->matchVarToString("jicons"); +	uint32 iconsDepressed = *_vm->getVar("jicons");  	// Now, draw which icons are depressed based on the bits of the variable  	if (iconsDepressed & (1 << 0)) @@ -949,7 +1196,7 @@ void RivenExternal::xjtunnel103_pictfix(uint16 argc, uint16 *argv) {  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->matchVarToString("jicons"); +	uint32 iconsDepressed = *_vm->getVar("jicons");  	// Now, draw which icons are depressed based on the bits of the variable  	if (iconsDepressed & (1 << 9)) @@ -972,7 +1219,7 @@ void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {  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->matchVarToString("jicons"); +	uint32 iconsDepressed = *_vm->getVar("jicons");  	// Now, draw which icons are depressed based on the bits of the variable  	if (iconsDepressed & (1 << 3)) @@ -993,7 +1240,7 @@ void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {  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->matchVarToString("jicons"); +	uint32 iconsDepressed = *_vm->getVar("jicons");  	// Now, draw which icons are depressed based on the bits of the variable  	if (iconsDepressed & (1 << 16)) @@ -1027,7 +1274,7 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {  	_vm->changeToCard(_vm->matchRMAPToCard(0x183a9));  // Change to card looking straight again  	_vm->_video->playMovieBlocking(2); -	uint32 *gallows = _vm->matchVarToString("jgallows"); +	uint32 *gallows = _vm->getVar("jgallows");  	if (*gallows == 1) {  		// If the gallows is open, play the up movie and return  		_vm->_video->playMovieBlocking(3); @@ -1072,15 +1319,15 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {  }  void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	resetDomeSliders(_vm->getFeatures() & GF_DVD ? 547 : 548, 81, 2);  }  void RivenExternal::xjdome25_slidermd(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	dragDomeSlider(_vm->getFeatures() & GF_DVD ? 547: 548, 81, 29, 28, 2);  }  void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkSliderCursorChange(2);  }  void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) { @@ -1092,18 +1339,20 @@ void RivenExternal::xjisland3500_domecheck(uint16 argc, uint16 *argv) {  }  int RivenExternal::jspitElevatorLoop() { +	Common::Point startPos = _vm->_system->getEventManager()->getMousePos(); +  	Common::Event event;  	int changeLevel = 0; -	_vm->_gfx->changeCursor(2004); +	_vm->_gfx->changeCursor(kRivenClosedHandCursor);  	_vm->_system->updateScreen();  	for (;;) {  		while (_vm->_system->getEventManager()->pollEvent(event)) {  			switch (event.type) {  			case Common::EVENT_MOUSEMOVE: -				if (event.mouse.y > (_vm->_mousePos.y + 10)) { +				if (event.mouse.y > (startPos.y + 10)) {  					changeLevel = -1; -				} else if (event.mouse.y < (_vm->_mousePos.y - 10)) { +				} else if (event.mouse.y < (startPos.y - 10)) {  					changeLevel = 1;  				} else {  					changeLevel = 0; @@ -1157,7 +1406,7 @@ void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {  		_vm->_video->playMovieBlocking(6);  	// If the whark's mouth is open, close it -	uint32 *mouthVar = _vm->matchVarToString("jwmouth"); +	uint32 *mouthVar = _vm->getVar("jwmouth");  	if (*mouthVar == 1) {  		_vm->_video->playMovieBlocking(3);  		_vm->_video->playMovieBlocking(8); @@ -1176,27 +1425,27 @@ void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {  void RivenExternal::xjplaybeetle_550(uint16 argc, uint16 *argv) {  	// Play a beetle animation 25% of the time -	*_vm->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0; +	*_vm->getVar("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->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0; +	*_vm->getVar("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->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0; +	*_vm->getVar("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->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0; +	*_vm->getVar("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->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0 && *_vm->matchVarToString("jgirl") != 1) ? 1 : 0; +	*_vm->getVar("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0 && *_vm->getVar("jgirl") != 1) ? 1 : 0;  }  void RivenExternal::xjlagoon700_alert(uint16 argc, uint16 *argv) { @@ -1209,7 +1458,7 @@ void RivenExternal::xjlagoon800_alert(uint16 argc, uint16 *argv) {  void RivenExternal::xjlagoon1500_alert(uint16 argc, uint16 *argv) {  	// Have the sunners move a bit as you get closer ;) -	uint32 *sunners = _vm->matchVarToString("jsunners"); +	uint32 *sunners = _vm->getVar("jsunners");  	if (*sunners == 0) {  		_vm->_video->playMovieBlocking(3);  	} else if (*sunners == 1) { @@ -1234,14 +1483,14 @@ 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->matchVarToString("returnstackid") == rspit) { +	if (*_vm->getVar("returnstackid") == rspit) {  		_vm->changeToStack(rspit);  		_vm->changeToCard(2);  		return;  	}  	// You used the trap book... why? What were you thinking? -	uint32 *gehnState = _vm->matchVarToString("agehn"); +	uint32 *gehnState = _vm->getVar("agehn");  	if (*gehnState == 0)		// Gehn who?  		runEndGame(1); @@ -1252,16 +1501,127 @@ void RivenExternal::xorollcredittime(uint16 argc, uint16 *argv) {  }  void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { -	// TODO: This fun external command is probably one of the most complex, -	// up there with the marble puzzle ones. It involves so much... Basically, -	// it's playing when Gehn holds the trap book up to you and you have to -	// click on the book (hence the name of the function). Yeah, not fun. -	// Lots of timing stuff needs to be done for a couple videos. +	// Hide the cursor +	_vm->_gfx->changeCursor(kRivenHideCursor); + +	// Let's hook onto our video +	VideoHandle video = _vm->_video->findVideoHandle(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 +	// 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; + +	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]); + +	// Just let the video play while we wait until Gehn opens the trap book for us +	while (_vm->_video->getElapsedTime(video) < startTime && !_vm->shouldQuit()) { +		if (_vm->_video->updateBackgroundMovies()) +			_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->_gfx->changeCursor(kRivenOpenHandCursor); +	else +		_vm->_gfx->changeCursor(kRivenMainCursor); +	 +	// 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 (_vm->_video->getElapsedTime(video) < endTime && !_vm->shouldQuit()) { +		bool updateScreen = _vm->_video->updateBackgroundMovies(); + +		Common::Event event; +		while (_vm->_system->getEventManager()->pollEvent(event)) { +			switch (event.type) { +			case Common::EVENT_MOUSEMOVE: +				if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos())) +					_vm->_gfx->changeCursor(kRivenOpenHandCursor); +				else +					_vm->_gfx->changeCursor(kRivenMainCursor); +				updateScreen = false; // Don't update twice, changing the cursor already updates the screen +				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->_gfx->changeCursor(kRivenHideCursor);          // Hide the cursor +					_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->playMovieBlocking(1);                  // Play Gehn Link Video +					*_vm->getVar("agehn") = 4;                          // Set Gehn to the trapped state +					*_vm->getVar("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! (TODO: Shouldn't this card change?) +					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->_gfx->changeCursor(kRivenHideCursor); + +	// 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->getVar("agehn") == 3) { +		_vm->_scriptMan->stopAllScripts(); +		runCredits(argv[0]); +		return; +	} + +	// There was no click, so just play the rest of the video. +	while (!_vm->_video->endOfVideo(video) && !_vm->shouldQuit()) { +		if (_vm->_video->updateBackgroundMovies()) +			_vm->_system->updateScreen(); + +		Common::Event event; +		while (_vm->_system->getEventManager()->pollEvent(event)) +			; + +		_vm->_system->delayMillis(10); +	}  }  void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {  	// Close the blank linking book if it's open -	uint32 *book = _vm->matchVarToString("odeskbook"); +	uint32 *book = _vm->getVar("odeskbook");  	if (*book != 1)  		return; @@ -1284,16 +1644,16 @@ void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {  void RivenExternal::xobedroom5_closedrawer(uint16 argc, uint16 *argv) {  	// Close the drawer if open when clicking on the journal.  	_vm->_video->playMovieBlocking(2); -	*_vm->matchVarToString("ostanddrawer") = 0; +	*_vm->getVar("ostanddrawer") = 0;  }  void RivenExternal::xogehnopenbook(uint16 argc, uint16 *argv) { -	_vm->_gfx->drawPLST(*_vm->matchVarToString("ogehnpage")); +	_vm->_gfx->drawPLST(*_vm->getVar("ogehnpage"));  }  void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {  	// Get the page variable -	uint32 *page = _vm->matchVarToString("ogehnpage"); +	uint32 *page = _vm->getVar("ogehnpage");  	// Decrement the page if it's not the first page  	if (*page == 1) @@ -1301,7 +1661,7 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {  	(*page)--;  	// Play the page turning sound -	_vm->_sound->playSound(12, false); +	_vm->_sound->playSound(12);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(1); @@ -1310,7 +1670,7 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {  void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {  	// Get the page variable -	uint32 *page = _vm->matchVarToString("ogehnpage"); +	uint32 *page = _vm->getVar("ogehnpage");  	// Increment the page if it's not the last page  	if (*page == 13) @@ -1318,7 +1678,7 @@ void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {  	(*page)++;  	// Play the page turning sound -	_vm->_sound->playSound(13, false); +	_vm->_sound->playSound(13);  	// Now update the screen :)  	_vm->_gfx->scheduleTransition(0); @@ -1334,7 +1694,7 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {  	// Hide the cursor  	_vm->_gfx->changeCursor(kRivenHideCursor); -	uint32 *prisonCombo = _vm->matchVarToString("pcorrectorder"); +	uint32 *prisonCombo = _vm->getVar("pcorrectorder");  	uint32 soundTime = _vm->_system->getMillis() - 500; // Start the first sound instantly  	byte curSound = 0; @@ -1371,19 +1731,19 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {  void RivenExternal::xpisland990_elevcombo(uint16 argc, uint16 *argv) {  	// Play button sound based on argv[0] -	_vm->_sound->playSound(argv[0] + 5, false); +	_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->matchVarToString("agehn") != 4) +	if (*_vm->getVar("agehn") != 4)  		return; -	uint32 *correctDigits = _vm->matchVarToString("pelevcombo"); +	uint32 *correctDigits = _vm->getVar("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->matchVarToString("pcorrectorder"), *correctDigits)) +	if (*correctDigits < 5 && argv[0] == getComboDigit(*_vm->getVar("pcorrectorder"), *correctDigits))  		*correctDigits += 1;  	else  		*correctDigits = 0; @@ -1398,19 +1758,19 @@ void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) {  }  void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkDomeSliders(31, 5);  }  void RivenExternal::xpisland25_resetsliders(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	resetDomeSliders(58, 10, 6);  }  void RivenExternal::xpisland25_slidermd(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	dragDomeSlider(58, 10, 31, 5, 6);  }  void RivenExternal::xpisland25_slidermw(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkSliderCursorChange(6);  }  // ------------------------------------------------------------------------------------ @@ -1430,8 +1790,8 @@ void RivenExternal::xrcredittime(uint16 argc, uint16 *argv) {  void RivenExternal::xrshowinventory(uint16 argc, uint16 *argv) {  	// Give the trap book and Catherine's journal to the player -	*_vm->matchVarToString("atrapbook") = 1; -	*_vm->matchVarToString("acathbook") = 1; +	*_vm->getVar("atrapbook") = 1; +	*_vm->getVar("acathbook") = 1;  	_vm->_gfx->showInventory();  } @@ -1452,29 +1812,29 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {  	_vm->_video->playMovieBlocking(3);  	// Don't do anything else if the telescope power is off -	if (*_vm->matchVarToString("ttelevalve") == 0) +	if (*_vm->getVar("ttelevalve") == 0)  		return; -	uint32 *telescopePos = _vm->matchVarToString("ttelescope"); -	uint32 *telescopeCover = _vm->matchVarToString("ttelecover"); +	uint32 *telescopePos = _vm->getVar("ttelescope"); +	uint32 *telescopeCover = _vm->getVar("ttelecover");  	if (*telescopePos == 1) {  		// We're at the bottom, which means one of two things can happen... -		if (*telescopeCover == 1 && *_vm->matchVarToString("ttelepin") == 1) { +		if (*telescopeCover == 1 && *_vm->getVar("ttelepin") == 1) {  			// ...if the cover is open and the pin is up, the game is now over. -			if (*_vm->matchVarToString("pcage") == 2) { +			if (*_vm->getVar("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...  				warning("xtexterior300_telescopedown: Good ending");  				_vm->_video->activateMLST(8, _vm->getCurCard());  				runEndGame(8); -			} else if (*_vm->matchVarToString("agehn") == 4) { +			} else if (*_vm->getVar("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...  				warning("xtexterior300_telescopedown: OK ending");  				_vm->_video->activateMLST(9, _vm->getCurCard());  				runEndGame(9); -			} else if (*_vm->matchVarToString("atrapbook") == 1) { +			} else if (*_vm->getVar("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. @@ -1492,7 +1852,9 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {  			}  		} else {  			// ...the telescope can't move down anymore. -			// TODO: Play sound +			// Play the sound of not being able to move +			_vm->_gfx->changeCursor(kRivenHideCursor); +			_vm->_sound->playSoundBlocking(13);  		}  	} else {  		// We're not at the bottom, and we can move down again @@ -1510,14 +1872,16 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {  	_vm->_video->playMovieBlocking(3);  	// Don't do anything else if the telescope power is off -	if (*_vm->matchVarToString("ttelevalve") == 0) +	if (*_vm->getVar("ttelevalve") == 0)  		return; -	uint32 *telescopePos = _vm->matchVarToString("ttelescope"); +	uint32 *telescopePos = _vm->getVar("ttelescope");  	// Check if we can't move up anymore  	if (*telescopePos == 5) { -		// TODO: Play sound +		// Play the sound of not being able to move +		_vm->_gfx->changeCursor(kRivenHideCursor); +		_vm->_sound->playSoundBlocking(13);  		return;  	} @@ -1530,9 +1894,9 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {  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->matchVarToString("tcovercombo"); +	uint32 *correctDigits = _vm->getVar("tcovercombo"); -	if (*correctDigits < 5 && argv[0] == getComboDigit(*_vm->matchVarToString("tcorrectorder"), *correctDigits)) +	if (*correctDigits < 5 && argv[0] == getComboDigit(*_vm->getVar("tcorrectorder"), *correctDigits))  		*correctDigits += 1;  	else  		*correctDigits = 0; @@ -1548,8 +1912,8 @@ void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {  // 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->matchVarToString("aatrusbook") = 1; -	*_vm->matchVarToString("atrapbook") = 1; +	*_vm->getVar("aatrusbook") = 1; +	*_vm->getVar("atrapbook") = 1;  }  // Trap Book is removed from inventory @@ -1557,35 +1921,216 @@ 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->matchVarToString("atrapbook") = 0; +	*_vm->getVar("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) { -	// TODO: Lots of stuff to do here, eventually we have to check each individual -	// marble position and set apower based on that. The game handles the video playing -	// so we don't have to. For the purposes of making the game progress further, we'll -	// just turn the power on for now. -	*_vm->matchVarToString("apower") = 1; +	// 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->getVar(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->getVar("apower") = 1; +		for (uint16 i = 0; i < kMarbleCount; i++) +			*_vm->getVar(s_marbleNames[i]) = 0; +	} else +		*_vm->getVar("apower") = 0;  }  void RivenExternal::xt7600_setupmarbles(uint16 argc, uint16 *argv) { -	// TODO: Marble puzzle related +	// Draw the small marbles when we're a step away from the waffle +	uint16 baseBitmapId = (_vm->getFeatures() & GF_DVD) ? 539 : 526; +	bool waffleDown = *_vm->getVar("twaffle") != 0; + +	// Note that each of the small marble images is exactly 4x2 + +	for (uint16 i = 0; i < kMarbleCount; i++) { +		uint32 *var = _vm->getVar(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) +			int marbleX = 376 + i * 2; +			int marbleY = 253 + i * 4; +			_vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + 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 +			// TODO: Draw them onto the grid +		} +	} +} + +void RivenExternal::setMarbleHotspots() { +	// Set the hotspots +	for (uint16 i = 0; i < kMarbleCount; i++) { +		uint32 *marblePos = _vm->getVar(s_marbleNames[i]); + +		if (*marblePos == 0) // In the receptacle +			_vm->_hotspots[i + 3].rect = _marbleBaseHotspots[i]; +		else                 // On the grid +			_vm->_hotspots[i + 3].rect = generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)); +	}  }  void RivenExternal::xt7800_setup(uint16 argc, uint16 *argv) { -	// TODO: Marble puzzle related +	// 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); + +	// Move the marble hotspots based on their position variables +	setMarbleHotspots(); +	*_vm->getVar("themarble") = 0; +} + +void RivenExternal::drawMarbles() { +	for (uint32 i = 0; i < kMarbleCount; i++) { +		// Don't draw the marble if we're holding it +		if (*_vm->getVar("themarble") - 1 == i) +			continue; + +		Common::Rect rect = _vm->_hotspots[i + 3].rect; +		// 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) { -	// TODO: Marble puzzle related +	// 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) { -	// TODO: Marble puzzle related +	// Pick up and move a marble + +	// First, let's figure out what marble we're now holding +	uint32 *marble = _vm->getVar("themarble"); +	*marble = 0; + +	for (uint32 i = 0; i < kMarbleCount; i++) +		if (_vm->_hotspots[i + 3].rect.contains(_vm->_system->getEventManager()->getMousePos())) { +			*marble = i + 1; +			break; +		} + +	// xtakeit() shouldn't be called if we're not on a marble hotspot +	assert(*marble); + +	// Redraw the background +	_vm->_gfx->drawPLST(1); +	_vm->_gfx->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 +	} + +	// Check if we landed in a valid location and no other marble has that location +	uint32 *marblePos = _vm->getVar(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->getVar(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->_curHotspot = -1; +	_vm->checkHotspotChange(); +	_vm->_gfx->updateScreen();  }  void RivenExternal::xtscpbtn(uint16 argc, uint16 *argv) { @@ -1597,19 +2142,19 @@ void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) {  }  void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkDomeSliders(29, 30);  }  void RivenExternal::xtisland5056_resetsliders(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	resetDomeSliders(_vm->getFeatures() & GF_DVD ? 813 : 798, 37, 3);  }  void RivenExternal::xtisland5056_slidermd(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	dragDomeSlider(_vm->getFeatures() & GF_DVD ? 813 : 798, 37, 29, 30, 3);  }  void RivenExternal::xtisland5056_slidermw(uint16 argc, uint16 *argv) { -	// TODO: Dome related +	checkSliderCursorChange(3);  }  void RivenExternal::xtatboundary(uint16 argc, uint16 *argv) { | 
