aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk/riven_stacks/tspit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mohawk/riven_stacks/tspit.cpp')
-rw-r--r--engines/mohawk/riven_stacks/tspit.cpp409
1 files changed, 408 insertions, 1 deletions
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! </bad Seinfeld reference>
+ return ((var >> 16) & 0xff) - 1;
+}
+
+static Common::Rect generateMarbleGridRect(uint16 x, uint16 y) {
+ // x/y in terms of 0!
+ static const int marbleGridOffsetX[] = { 134, 202, 270, 338, 406 };
+ static const int marbleGridOffsetY[] = { 24, 92, 159, 227, 295 };
+
+ uint16 offsetX = marbleGridOffsetX[x / 5] + (x % 5) * kMarbleHotspotSize;
+ uint16 offsetY = marbleGridOffsetY[y / 5] + (y % 5) * kMarbleHotspotSize;
+ return Common::Rect(offsetX, offsetY, offsetX + kMarbleHotspotSize, offsetY + kMarbleHotspotSize);
+}
+
+void TSpit::xt7500_checkmarbles(uint16 argc, uint16 *argv) {
+ // Set apower if the marbles are in their correct spot.
+
+ bool valid = true;
+ static const uint32 marbleFinalValues[] = { 1114121, 1441798, 0, 65552, 65558, 262146 };
+
+ for (uint16 i = 0; i < kMarbleCount; i++)
+ if (_vm->_vars[s_marbleNames[i]] != marbleFinalValues[i]) {
+ valid = false;
+ break;
+ }
+
+ // If we have the correct combo, activate the power and reset the marble positions
+ // Otherwise, make sure the power is off
+ if (valid) {
+ _vm->_vars["apower"] = 1;
+ for (uint16 i = 0; i < kMarbleCount; i++)
+ _vm->_vars[s_marbleNames[i]] = 0;
+ } else
+ _vm->_vars["apower"] = 0;
+}
+
+void TSpit::xt7600_setupmarbles(uint16 argc, uint16 *argv) {
+ // Draw the small marbles when we're a step away from the waffle
+
+ // Convert from marble X coordinate to screen X coordinate
+ static const uint16 xPosOffsets[] = {
+ 246, 245, 244, 243, 243, 241, 240, 240, 239, 238, 237, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 226, 225
+ };
+
+ // Convert from marble Y coordinate to screen Y coordinate
+ static const uint16 yPosOffsets[] = {
+ 261, 263, 265, 267, 268, 270, 272, 274, 276, 278, 281, 284, 285, 288, 290, 293, 295, 298, 300, 303, 306, 309, 311, 314, 316
+ };
+
+ // Handle spacing for y coordinates due to the angle
+ static const double yAdjusts[] = {
+ 4.56, 4.68, 4.76, 4.84, 4.84, 4.96, 5.04, 5.04, 5.12, 5.2, 5.28, 5.28, 5.36, 5.44, 5.4, 5.6, 5.72, 5.8, 5.88, 5.96, 6.04, 6.12, 6.2, 6.2, 6.28
+ };
+
+ // Waffle state of 0 is up, 1 down
+ bool waffleDown = _vm->_vars["twaffle"] != 0;
+
+ // Note that each of the small marble images is exactly 4x2
+ // The original seems to scale the marble images from extras.mhk, but
+ // we're using the pre-scaled images in the stack.
+ uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred");
+
+ for (uint16 i = 0; i < kMarbleCount; i++) {
+ uint32 var = _vm->_vars[s_marbleNames[i]];
+
+ if (var == 0) {
+ // The marble is still in its initial place
+ // (Note that this is still drawn even if the waffle is down)
+ static const uint16 defaultX[] = { 375, 377, 379, 381, 383, 385 };
+ static const uint16 defaultY[] = { 253, 257, 261, 265, 268, 273 };
+ _vm->_gfx->copyImageToScreen(baseBitmapId + i, defaultX[i], defaultY[i], defaultX[i] + kSmallMarbleWidth, defaultY[i] + kSmallMarbleHeight);
+ } else if (waffleDown) {
+ // The marble is on the grid and the waffle is down
+ // (Nothing to draw here)
+ } else {
+ // The marble is on the grid and the waffle is up
+ int marbleX = (int)floor(getMarbleX(var) * yAdjusts[getMarbleY(var)] + xPosOffsets[getMarbleY(var)] + 0.5);
+ int marbleY = yPosOffsets[getMarbleY(var)];
+ _vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight);
+ }
+ }
+}
+
+void TSpit::setMarbleHotspots() {
+ // Set the hotspots
+ for (uint16 i = 0; i < kMarbleCount; i++) {
+ uint32 marblePos = _vm->_vars[s_marbleNames[i]];
+ RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+
+ if (marblePos == 0) // In the receptacle
+ marbleHotspot->setRect(_marbleBaseHotspots[i]);
+ else // On the grid
+ marbleHotspot->setRect(generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)));
+ }
+}
+
+void TSpit::xt7800_setup(uint16 argc, uint16 *argv) {
+ // First, let's store the base receptacle hotspots for the marbles
+ if (_marbleBaseHotspots.empty())
+ for (uint16 i = 0; i < kMarbleCount; i++) {
+ RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+ _marbleBaseHotspots.push_back(marbleHotspot->getRect());
+ }
+
+ // Move the marble hotspots based on their position variables
+ setMarbleHotspots();
+ _vm->_vars["themarble"] = 0;
+}
+
+void TSpit::drawMarbles() {
+ for (uint32 i = 0; i < kMarbleCount; i++) {
+ // Don't draw the marble if we're holding it
+ if (_vm->_vars["themarble"] - 1 == i)
+ continue;
+
+ RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+
+ Common::Rect rect = marbleHotspot->getRect();
+ // Trim the rect down a bit
+ rect.left += 3;
+ rect.top += 3;
+ rect.right -= 2;
+ rect.bottom -= 2;
+ _vm->_gfx->drawExtrasImage(i + 200, rect);
+ }
+}
+
+void TSpit::xdrawmarbles(uint16 argc, uint16 *argv) {
+ // Draw marbles in the closeup
+ drawMarbles();
+}
+
+void TSpit::xtakeit(uint16 argc, uint16 *argv) {
+ // Pick up and move a marble
+
+ // First, let's figure out what marble we're now holding
+ uint32 &marble = _vm->_vars["themarble"];
+ marble = 0;
+
+ for (uint32 i = 0; i < kMarbleCount; i++) {
+ RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
+ if (marbleHotspot->containsPoint(_vm->_system->getEventManager()->getMousePos())) {
+ marble = i + 1;
+ break;
+ }
+ }
+
+ // xtakeit() shouldn't be called if we're not on a marble hotspot
+ assert(marble != 0);
+
+ // Redraw the background
+ _vm->getCard()->drawPicture(1);
+
+ // Loop until the player lets go (or quits)
+ Common::Event event;
+ bool mouseDown = true;
+ while (mouseDown) {
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ if (event.type == Common::EVENT_LBUTTONUP)
+ mouseDown = false;
+ else if (event.type == Common::EVENT_MOUSEMOVE)
+ _vm->_system->updateScreen();
+ else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
+ return;
+ }
+
+ _vm->_system->delayMillis(10); // Take it easy on the CPU
+ }
+
+ // Check if we landed in a valid location and no other marble has that location
+ uint32 &marblePos = _vm->_vars[s_marbleNames[marble - 1]];
+
+ bool foundMatch = false;
+ for (int y = 0; y < 25 && !foundMatch; y++) {
+ for (int x = 0; x < 25 && !foundMatch; x++) {
+ Common::Rect testHotspot = generateMarbleGridRect(x, y);
+
+ // Let's try to place the marble!
+ if (testHotspot.contains(_vm->_system->getEventManager()->getMousePos())) {
+ // Set this as the position
+ setMarbleX(marblePos, x);
+ setMarbleY(marblePos, y);
+
+ // Let's make sure no other marble is in this spot...
+ for (uint16 i = 0; i < kMarbleCount; i++)
+ if (i != marble - 1 && _vm->_vars[s_marbleNames[i]] == marblePos)
+ marblePos = 0;
+
+ // We have a match
+ foundMatch = true;
+ }
+ }
+ }
+
+ // If we still don't have a match, reset it to the original location
+ if (!foundMatch)
+ marblePos = 0;
+
+ // Check the new hotspots and refresh everything
+ marble = 0;
+ setMarbleHotspots();
+ _vm->updateCurrentHotspot();
+ _vm->_gfx->updateScreen();
+}
+
+void TSpit::xtscpbtn(uint16 argc, uint16 *argv) {
+ runDomeButtonMovie();
+}
+
+void TSpit::xtisland4990_domecheck(uint16 argc, uint16 *argv) {
+ runDomeCheck();
+}
+
+void TSpit::xtisland5056_opencard(uint16 argc, uint16 *argv) {
+ checkDomeSliders();
+}
+
+void TSpit::xtisland5056_resetsliders(uint16 argc, uint16 *argv) {
+ resetDomeSliders(37, 24);
+}
+
+void TSpit::xtisland5056_slidermd(uint16 argc, uint16 *argv) {
+ dragDomeSlider(37, 24);
+}
+
+void TSpit::xtisland5056_slidermw(uint16 argc, uint16 *argv) {
+ checkSliderCursorChange(24);
+}
+
+void TSpit::xtatboundary(uint16 argc, uint16 *argv) {
+ runDemoBoundaryDialog();
}
} // End of namespace RivenStacks