From 14bbf8aab4ed187ec1e6da9e61a1acf6601d0332 Mon Sep 17 00:00:00 2001 From: Bastien Bouclet Date: Tue, 1 Nov 2016 19:19:26 +0100 Subject: MOHAWK: Move the external commands to their respective stacks --- engines/mohawk/riven_stacks/aspit.cpp | 303 ++++++++++++++++++- engines/mohawk/riven_stacks/aspit.h | 35 +++ engines/mohawk/riven_stacks/bspit.cpp | 466 +++++++++++++++++++++++++++- engines/mohawk/riven_stacks/bspit.h | 36 +++ engines/mohawk/riven_stacks/domespit.cpp | 201 ++++++++++++ engines/mohawk/riven_stacks/domespit.h | 13 + engines/mohawk/riven_stacks/gspit.cpp | 437 ++++++++++++++++++++++++++- engines/mohawk/riven_stacks/gspit.h | 35 +++ engines/mohawk/riven_stacks/jspit.cpp | 503 ++++++++++++++++++++++++++++++- engines/mohawk/riven_stacks/jspit.h | 51 ++++ engines/mohawk/riven_stacks/ospit.cpp | 255 +++++++++++++++- engines/mohawk/riven_stacks/ospit.h | 21 ++ engines/mohawk/riven_stacks/pspit.cpp | 54 +++- engines/mohawk/riven_stacks/pspit.h | 14 + engines/mohawk/riven_stacks/rspit.cpp | 80 ++++- engines/mohawk/riven_stacks/rspit.h | 9 + engines/mohawk/riven_stacks/tspit.cpp | 409 ++++++++++++++++++++++++- engines/mohawk/riven_stacks/tspit.h | 42 +++ 18 files changed, 2956 insertions(+), 8 deletions(-) (limited to 'engines/mohawk/riven_stacks') diff --git a/engines/mohawk/riven_stacks/aspit.cpp b/engines/mohawk/riven_stacks/aspit.cpp index f8aa53af8a..f4ab7d1340 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 3a55714e00..60400dd5f6 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 791317c561..f94098a398 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(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 be3c052d00..7e810f26eb 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 4674a24d2f..71a82ab343 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 776736db85..7ca513c743 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 8617b285eb..b70a12c635 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 794a95bde5..045d47a2b4 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 e19d28952c..582c1d6080 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 9e82a137aa..d9f5ead2a1 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 f36be94cb7..7607a93f8d 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? + 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 0792571d5d..4181ce3651 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 c6b1b97ec7..ae10bd0211 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 797eb29000..ca09417713 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 c099c43417..992319d9dd 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 67384cb4c5..c46537c3ff 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 aa4634adf7..763f5351cd 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! + 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 90c62a0272..6b9e7efdf3 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 _marbleBaseHotspots; }; } // End of namespace RivenStacks -- cgit v1.2.3