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