1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "mohawk/riven_stacks/tspit.h"
24
25 #include "mohawk/cursors.h"
26 #include "mohawk/riven.h"
27 #include "mohawk/riven_card.h"
28 #include "mohawk/riven_graphics.h"
29 #include "mohawk/riven_inventory.h"
30 #include "mohawk/riven_video.h"
31
32 #include "common/events.h"
33
34 namespace Mohawk {
35 namespace RivenStacks {
36
TSpit(MohawkEngine_Riven * vm)37 TSpit::TSpit(MohawkEngine_Riven *vm) :
38 DomeSpit(vm, kStackTspit, "tsliders.190", "tsliderbg.190") {
39
40 REGISTER_COMMAND(TSpit, xtexterior300_telescopedown);
41 REGISTER_COMMAND(TSpit, xtexterior300_telescopeup);
42 REGISTER_COMMAND(TSpit, xtisland390_covercombo);
43 REGISTER_COMMAND(TSpit, xtatrusgivesbooks);
44 REGISTER_COMMAND(TSpit, xtchotakesbook);
45 REGISTER_COMMAND(TSpit, xthideinventory);
46 REGISTER_COMMAND(TSpit, xt7500_checkmarbles);
47 REGISTER_COMMAND(TSpit, xt7600_setupmarbles);
48 REGISTER_COMMAND(TSpit, xt7800_setup);
49 REGISTER_COMMAND(TSpit, xdrawmarbles);
50 REGISTER_COMMAND(TSpit, xtakeit);
51 REGISTER_COMMAND(TSpit, xtscpbtn);
52 REGISTER_COMMAND(TSpit, xtisland4990_domecheck);
53 REGISTER_COMMAND(TSpit, xtisland5056_opencard);
54 REGISTER_COMMAND(TSpit, xtisland5056_resetsliders);
55 REGISTER_COMMAND(TSpit, xtisland5056_slidermd);
56 REGISTER_COMMAND(TSpit, xtisland5056_slidermw);
57 REGISTER_COMMAND(TSpit, xtatboundary);
58 }
59
xtexterior300_telescopedown(const ArgumentArray & args)60 void TSpit::xtexterior300_telescopedown(const ArgumentArray &args) {
61 // First, show the button movie
62 RivenVideo *buttonVideo = _vm->_video->openSlot(3);
63 buttonVideo->seek(0);
64 buttonVideo->enable();
65 buttonVideo->playBlocking();
66
67 // Don't do anything else if the telescope power is off
68 if (_vm->_vars["ttelevalve"] == 0)
69 return;
70
71 uint32 &telescopePos = _vm->_vars["ttelescope"];
72 uint32 &telescopeCover = _vm->_vars["ttelecover"];
73
74 if (telescopePos != 1) {
75 // We're not at the bottom, and we can move down again
76
77 // Play a piece of the moving down movie
78 static const uint32 timeIntervals[] = { 4320, 3440, 2660, 1760, 880, 0 };
79 uint16 movieCode = telescopeCover ? 1 : 2;
80 RivenVideo *video = _vm->_video->openSlot(movieCode);
81 video->enable();
82 video->seek(timeIntervals[telescopePos]);
83 _vm->_sound->playCardSound("tTeleMove"); // Play the moving sound
84 video->playBlocking(timeIntervals[telescopePos - 1]);
85 video->stop();
86
87 // Now move the telescope down a position and refresh
88 telescopePos--;
89 _vm->getCard()->enter(false);
90 return;
91 }
92
93 // We're at the bottom, which means one of two things can happen...
94 if (telescopeCover == 1 && _vm->_vars["ttelepin"] == 1) {
95 // ...if the cover is open and the pin is up, the game is now over.
96 xtopenfissure();
97 } else {
98 // ...the telescope can't move down anymore.
99 // Play the sound of not being able to move
100 _vm->_sound->playCardSound("tTelDnMore");
101 }
102 }
103
xtexterior300_telescopeup(const ArgumentArray & args)104 void TSpit::xtexterior300_telescopeup(const ArgumentArray &args) {
105 // First, show the button movie
106 RivenVideo *buttonVideo = _vm->_video->openSlot(3);
107 buttonVideo->seek(0);
108 buttonVideo->enable();
109 buttonVideo->playBlocking();
110
111 // Don't do anything else if the telescope power is off
112 if (_vm->_vars["ttelevalve"] == 0)
113 return;
114
115 uint32 &telescopePos = _vm->_vars["ttelescope"];
116
117 // Check if we can't move up anymore
118 if (telescopePos == 5) {
119 // Play the sound of not being able to move
120 _vm->_sound->playCardSound("tTelDnMore");
121 return;
122 }
123
124 // Play a piece of the moving up movie
125 static const uint32 timeIntervals[] = { 0, 800, 1680, 2560, 3440, 4320 };
126 uint16 movieCode = _vm->_vars["ttelecover"] ? 4 : 5;
127 RivenVideo *video = _vm->_video->openSlot(movieCode);
128 video->enable();
129 video->seek(timeIntervals[telescopePos - 1]);
130 _vm->_sound->playCardSound("tTeleMove"); // Play the moving sound
131 video->playBlocking(timeIntervals[telescopePos]);
132 video->stop();
133
134 // Now move the telescope up a position and refresh
135 telescopePos++;
136 _vm->getCard()->enter(false);
137 }
138
xtopenfissure()139 void TSpit::xtopenfissure() {
140 if (_vm->_vars["pcage"] == 2) {
141 // The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
142 // And now we fall back to Earth... all the way...
143 _vm->getCard()->playMovie(8);
144 runEndGame(8, 5000, 2640);
145 } else if (_vm->_vars["agehn"] == 4) {
146 // The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
147 // Nice going! Catherine and the islanders are all dead now! Just go back to your home...
148 _vm->getCard()->playMovie(9);
149 runEndGame(9, 5000, 2088);
150 } else if (_vm->_vars["atrapbook"] == 1) {
151 // The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
152 // And then you get shot by Cho. Nice going! Catherine and the islanders are dead
153 // and you have just set Gehn free from Riven, not to mention you're dead.
154 _vm->getCard()->playMovie(10);
155 runEndGame(10, 5000, 1703);
156 } else {
157 // The impossible ending: You don't have Catherine's journal and yet you were somehow
158 // able to open the hatch on the telescope. The game provides an ending for those who
159 // cheat, load a saved game with the combo, or just guess the telescope combo. Atrus
160 // doesn't come and you just fall into the fissure.
161 _vm->getCard()->playMovie(11);
162 runEndGame(11, 5000, 0);
163 }
164 }
165
xtisland390_covercombo(const ArgumentArray & args)166 void TSpit::xtisland390_covercombo(const ArgumentArray &args) {
167 // Called when clicking the telescope cover buttons. args[0] is the button number (1...5).
168 uint32 &correctDigits = _vm->_vars["tcovercombo"];
169
170 if (correctDigits < 5 && args[0] == getComboDigit(_vm->_vars["tcorrectorder"], correctDigits))
171 correctDigits++;
172 else
173 correctDigits = 0;
174
175 // If we have hit the correct 5 buttons in a row, activate the hotspot to open up the
176 // telescope cover.
177 RivenHotspot *openCover = _vm->getCard()->getHotspotByName("openCover");
178 openCover->enable(correctDigits == 5);
179 }
180
181 // Atrus' Journal and Trap Book are added to inventory
xtatrusgivesbooks(const ArgumentArray & args)182 void TSpit::xtatrusgivesbooks(const ArgumentArray &args) {
183 // Give the player Atrus' Journal and the Trap book
184 }
185
186 // Trap Book is removed from inventory
xtchotakesbook(const ArgumentArray & args)187 void TSpit::xtchotakesbook(const ArgumentArray &args) {
188 // And now Cho takes the trap book
189 }
190
xthideinventory(const ArgumentArray & args)191 void TSpit::xthideinventory(const ArgumentArray &args) {
192 }
193
194 // Marble Puzzle related constants
195 static const uint32 kMarbleCount = 6;
196 static const int kSmallMarbleWidth = 4;
197 static const int kSmallMarbleHeight = 2;
198 //static const int kLargeMarbleSize = 8;
199 static const int kMarbleHotspotSize = 13;
200 static const char *s_marbleNames[] = { "tred", "torange", "tyellow", "tgreen", "tblue", "tviolet" };
201
202 // Marble Puzzle helper functions
203 // The y portion takes the upper 16 bits, while the x portion takes the lower 16 bits
setMarbleX(uint32 & var,byte x)204 static void setMarbleX(uint32 &var, byte x) {
205 var = (var & 0xff00) | (x + 1);
206 }
207
setMarbleY(uint32 & var,byte y)208 static void setMarbleY(uint32 &var, byte y) {
209 var = ((y + 1) << 16) | (var & 0xff);
210 }
211
getMarbleX(uint32 var)212 static byte getMarbleX(uint32 var) {
213 return (var & 0xff) - 1;
214 }
215
getMarbleY(uint32 var)216 static byte getMarbleY(uint32 var) { // Give that that Y you old hag! </bad Seinfeld reference>
217 return ((var >> 16) & 0xff) - 1;
218 }
219
generateMarbleGridRect(uint16 x,uint16 y)220 static Common::Rect generateMarbleGridRect(uint16 x, uint16 y) {
221 // x/y in terms of 0!
222 static const int marbleGridOffsetX[] = { 134, 202, 270, 338, 406 };
223 static const int marbleGridOffsetY[] = { 24, 92, 159, 227, 295 };
224
225 uint16 offsetX = marbleGridOffsetX[x / 5] + (x % 5) * kMarbleHotspotSize;
226 uint16 offsetY = marbleGridOffsetY[y / 5] + (y % 5) * kMarbleHotspotSize;
227 return Common::Rect(offsetX, offsetY, offsetX + kMarbleHotspotSize, offsetY + kMarbleHotspotSize);
228 }
229
xt7500_checkmarbles(const ArgumentArray & args)230 void TSpit::xt7500_checkmarbles(const ArgumentArray &args) {
231 // Set apower if the marbles are in their correct spot.
232
233 bool valid = true;
234 static const uint32 marbleFinalValues[] = { 1114121, 1441798, 0, 65552, 65558, 262146 };
235
236 for (uint16 i = 0; i < kMarbleCount; i++)
237 if (_vm->_vars[s_marbleNames[i]] != marbleFinalValues[i]) {
238 valid = false;
239 break;
240 }
241
242 // If we have the correct combo, activate the power and reset the marble positions
243 // Otherwise, make sure the power is off
244 if (valid) {
245 _vm->_vars["apower"] = 1;
246 for (uint16 i = 0; i < kMarbleCount; i++)
247 _vm->_vars[s_marbleNames[i]] = 0;
248 } else
249 _vm->_vars["apower"] = 0;
250 }
251
xt7600_setupmarbles(const ArgumentArray & args)252 void TSpit::xt7600_setupmarbles(const ArgumentArray &args) {
253 // Draw the small marbles when we're a step away from the waffle
254
255 // Convert from marble X coordinate to screen X coordinate
256 static const uint16 xPosOffsets[] = {
257 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
258 };
259
260 // Convert from marble Y coordinate to screen Y coordinate
261 static const uint16 yPosOffsets[] = {
262 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
263 };
264
265 // Handle spacing for y coordinates due to the angle
266 static const double yAdjusts[] = {
267 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
268 };
269
270 // Waffle state of 0 is up, 1 down
271 bool waffleDown = _vm->_vars["twaffle"] != 0;
272
273 // Note that each of the small marble images is exactly 4x2
274 // The original seems to scale the marble images from extras.mhk, but
275 // we're using the pre-scaled images in the stack.
276 uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, buildCardResourceName("tsmallred"));
277
278 for (uint16 i = 0; i < kMarbleCount; i++) {
279 uint32 var = _vm->_vars[s_marbleNames[i]];
280
281 if (var == 0) {
282 // The marble is still in its initial place
283 // (Note that this is still drawn even if the waffle is down)
284 static const uint16 defaultX[] = { 375, 377, 379, 381, 383, 385 };
285 static const uint16 defaultY[] = { 253, 257, 261, 265, 268, 273 };
286 _vm->_gfx->copyImageToScreen(baseBitmapId + i, defaultX[i], defaultY[i], defaultX[i] + kSmallMarbleWidth, defaultY[i] + kSmallMarbleHeight);
287 } else if (waffleDown) {
288 // The marble is on the grid and the waffle is down
289 // (Nothing to draw here)
290 } else {
291 // The marble is on the grid and the waffle is up
292 int marbleX = (int)floor(getMarbleX(var) * yAdjusts[getMarbleY(var)] + xPosOffsets[getMarbleY(var)] + 0.5);
293 int marbleY = yPosOffsets[getMarbleY(var)];
294 _vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight);
295 }
296 }
297 }
298
setMarbleHotspots()299 void TSpit::setMarbleHotspots() {
300 // Set the hotspots
301 for (uint16 i = 0; i < kMarbleCount; i++) {
302 uint32 marblePos = _vm->_vars[s_marbleNames[i]];
303 RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
304
305 if (marblePos == 0) // In the receptacle
306 marbleHotspot->setRect(_marbleBaseHotspots[i]);
307 else // On the grid
308 marbleHotspot->setRect(generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos)));
309 }
310 }
311
xt7800_setup(const ArgumentArray & args)312 void TSpit::xt7800_setup(const ArgumentArray &args) {
313 // First, let's store the base receptacle hotspots for the marbles
314 if (_marbleBaseHotspots.empty())
315 for (uint16 i = 0; i < kMarbleCount; i++) {
316 RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
317 _marbleBaseHotspots.push_back(marbleHotspot->getRect());
318 }
319
320 // Move the marble hotspots based on their position variables
321 setMarbleHotspots();
322 _vm->_vars["themarble"] = 0;
323 }
324
drawMarbles()325 void TSpit::drawMarbles() {
326 _vm->_gfx->beginScreenUpdate();
327 for (uint32 i = 0; i < kMarbleCount; i++) {
328 // Don't draw the marble if we're holding it
329 if (_vm->_vars["themarble"] - 1 == i)
330 continue;
331
332 RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
333
334 Common::Rect rect = marbleHotspot->getRect();
335 // Trim the rect down a bit
336 rect.left += 3;
337 rect.top += 3;
338 rect.right -= 2;
339 rect.bottom -= 2;
340 _vm->_gfx->drawExtrasImage(i + 200, rect);
341 }
342 _vm->_gfx->applyScreenUpdate();
343 }
344
xdrawmarbles(const ArgumentArray & args)345 void TSpit::xdrawmarbles(const ArgumentArray &args) {
346 // Draw marbles in the closeup
347 drawMarbles();
348 }
349
xtakeit(const ArgumentArray & args)350 void TSpit::xtakeit(const ArgumentArray &args) {
351 // Pick up and move a marble
352
353 // First, let's figure out what marble we're now holding
354 uint32 &marble = _vm->_vars["themarble"];
355 marble = 0;
356
357 for (uint32 i = 0; i < kMarbleCount; i++) {
358 RivenHotspot *marbleHotspot = _vm->getCard()->getHotspotByName(s_marbleNames[i]);
359 if (marbleHotspot->containsPoint(getMousePosition())) {
360 marble = i + 1;
361 break;
362 }
363 }
364
365 if (marble == 0) {
366 // xtakeit() shouldn't be called if we're not on a marble hotspot,
367 // but maybe another mouse moved event was received between the moment
368 // this script was queued and the moment it was executed.
369 return;
370 }
371
372 // Redraw the background
373 _vm->getCard()->drawPicture(1);
374
375 // Loop until the player lets go (or quits)
376 while (mouseIsDown() && !_vm->hasGameEnded()) {
377 _vm->doFrame();
378 }
379
380 // Check if we landed in a valid location and no other marble has that location
381 uint32 &marblePos = _vm->_vars[s_marbleNames[marble - 1]];
382
383 bool foundMatch = false;
384 for (int y = 0; y < 25 && !foundMatch; y++) {
385 for (int x = 0; x < 25 && !foundMatch; x++) {
386 Common::Rect testHotspot = generateMarbleGridRect(x, y);
387
388 // Let's try to place the marble!
389 if (testHotspot.contains(getMousePosition())) {
390 // Set this as the position
391 setMarbleX(marblePos, x);
392 setMarbleY(marblePos, y);
393
394 // Let's make sure no other marble is in this spot...
395 for (uint16 i = 0; i < kMarbleCount; i++)
396 if (i != marble - 1 && _vm->_vars[s_marbleNames[i]] == marblePos)
397 marblePos = 0;
398
399 // We have a match
400 foundMatch = true;
401 }
402 }
403 }
404
405 // If we still don't have a match, reset it to the original location
406 if (!foundMatch)
407 marblePos = 0;
408
409 // Check the new hotspots and refresh everything
410 marble = 0;
411 setMarbleHotspots();
412 drawMarbles();
413 }
414
xtscpbtn(const ArgumentArray & args)415 void TSpit::xtscpbtn(const ArgumentArray &args) {
416 runDomeButtonMovie();
417 }
418
xtisland4990_domecheck(const ArgumentArray & args)419 void TSpit::xtisland4990_domecheck(const ArgumentArray &args) {
420 runDomeCheck();
421 }
422
xtisland5056_opencard(const ArgumentArray & args)423 void TSpit::xtisland5056_opencard(const ArgumentArray &args) {
424 checkDomeSliders();
425 }
426
xtisland5056_resetsliders(const ArgumentArray & args)427 void TSpit::xtisland5056_resetsliders(const ArgumentArray &args) {
428 resetDomeSliders(24);
429 }
430
xtisland5056_slidermd(const ArgumentArray & args)431 void TSpit::xtisland5056_slidermd(const ArgumentArray &args) {
432 dragDomeSlider(24);
433 }
434
xtisland5056_slidermw(const ArgumentArray & args)435 void TSpit::xtisland5056_slidermw(const ArgumentArray &args) {
436 checkSliderCursorChange(24);
437 }
438
xtatboundary(const ArgumentArray & args)439 void TSpit::xtatboundary(const ArgumentArray &args) {
440 runDemoBoundaryDialog();
441 }
442
443 } // End of namespace RivenStacks
444 } // End of namespace Mohawk
445