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/cursors.h"
24 #include "mohawk/myst.h"
25 #include "mohawk/myst_areas.h"
26 #include "mohawk/myst_card.h"
27 #include "mohawk/myst_graphics.h"
28 #include "mohawk/myst_state.h"
29 #include "mohawk/myst_sound.h"
30 #include "mohawk/video.h"
31 #include "mohawk/myst_stacks/stoneship.h"
32 
33 #include "common/events.h"
34 #include "common/system.h"
35 #include "common/textconsole.h"
36 
37 namespace Mohawk {
38 namespace MystStacks {
39 
Stoneship(MohawkEngine_Myst * vm)40 Stoneship::Stoneship(MohawkEngine_Myst *vm) :
41 		MystScriptParser(vm, kStoneshipStack),
42 		_state(vm->_gameState->_stoneship) {
43 	setupOpcodes();
44 
45 	_tunnelRunning = false;
46 	_tunnelNextTime = 0;
47 	_tunnelAlarmSound = 0;
48 	_tunnelImagesCount = 0;
49 
50 	_state.lightState = 0;
51 	_state.generatorDepletionTime = 0;
52 	_state.generatorDuration = 0;
53 	_cabinMystBookPresent = 0;
54 	_siriusDrawerDrugsOpen = 0;
55 	_chestDrawersOpen = 0;
56 	_chestAchenarBottomDrawerClosed = 1;
57 
58 	_brotherDoorOpen = 0;
59 
60 	// Drop key
61 	if (_state.trapdoorKeyState == 1)
62 		_state.trapdoorKeyState = 2;
63 
64 	// Power is not available when loading
65 	if (_state.sideDoorOpened)
66 		_state.generatorPowerAvailable = 2;
67 	else
68 		_state.generatorPowerAvailable = 0;
69 
70 	_batteryCharging = false;
71 	_batteryDepleting = false;
72 	_batteryNextTime = 0;
73 	_batteryLastCharge = 0;
74 	_batteryGaugeRunning = false;
75 	_batteryGauge = nullptr;
76 
77 	_hologramTurnedOn = 0;
78 	_hologramDisplay = nullptr;
79 	_hologramSelection = nullptr;
80 	_hologramDisplayPos = 0;
81 
82 	_telescopeRunning = false;
83 	_telescopePosition = 0;
84 	_telescopePanorama = 0;
85 	_telescopeOldMouse = 0;
86 	_telescopeLighthouseOff = 0;
87 	_telescopeLighthouseOn = 0;
88 	_telescopeLighthouseState = false;
89 	_telescopeNexTime = 0;
90 
91 	_cloudOrbMovie = nullptr;
92 	_cloudOrbSound = 0;
93 	_cloudOrbStopSound = 0;
94 }
95 
~Stoneship()96 Stoneship::~Stoneship() {
97 }
98 
setupOpcodes()99 void Stoneship::setupOpcodes() {
100 	// "Stack-Specific" Opcodes
101 	REGISTER_OPCODE(100, Stoneship, o_pumpTurnOff);
102 	REGISTER_OPCODE(101, Stoneship, o_brotherDoorOpen);
103 	REGISTER_OPCODE(102, Stoneship, o_cabinBookMovie);
104 	REGISTER_OPCODE(103, Stoneship, o_drawerOpenSirius);
105 	REGISTER_OPCODE(104, Stoneship, o_drawerClose);
106 	REGISTER_OPCODE(105, Stoneship, o_telescopeStart);
107 	REGISTER_OPCODE(106, Stoneship, o_telescopeMove);
108 	REGISTER_OPCODE(107, Stoneship, o_telescopeStop);
109 	REGISTER_OPCODE(108, Stoneship, o_generatorStart);
110 	REGISTER_OPCODE(109, Stoneship, NOP);
111 	REGISTER_OPCODE(110, Stoneship, o_generatorStop);
112 	REGISTER_OPCODE(111, Stoneship, o_drawerOpenAchenar);
113 	REGISTER_OPCODE(112, Stoneship, o_hologramPlayback);
114 	REGISTER_OPCODE(113, Stoneship, o_hologramSelectionStart);
115 	REGISTER_OPCODE(114, Stoneship, o_hologramSelectionMove);
116 	REGISTER_OPCODE(115, Stoneship, o_hologramSelectionStop);
117 	REGISTER_OPCODE(116, Stoneship, o_compassButton);
118 	REGISTER_OPCODE(117, Stoneship, o_chestValveVideos);
119 	REGISTER_OPCODE(118, Stoneship, o_chestDropKey);
120 	REGISTER_OPCODE(119, Stoneship, o_trapLockOpen);
121 	REGISTER_OPCODE(120, Stoneship, o_sideDoorsMovies);
122 	REGISTER_OPCODE(121, Stoneship, o_cloudOrbEnter);
123 	REGISTER_OPCODE(122, Stoneship, o_cloudOrbLeave);
124 	REGISTER_OPCODE(125, Stoneship, o_drawerCloseOpened);
125 
126 	// "Init" Opcodes
127 	REGISTER_OPCODE(200, Stoneship, o_hologramDisplay_init);
128 	REGISTER_OPCODE(201, Stoneship, o_hologramSelection_init);
129 	REGISTER_OPCODE(202, Stoneship, o_battery_init);
130 	REGISTER_OPCODE(203, Stoneship, o_tunnelEnter_init);
131 	REGISTER_OPCODE(204, Stoneship, o_batteryGauge_init);
132 	REGISTER_OPCODE(205, Stoneship, o_tunnel_init);
133 	REGISTER_OPCODE(206, Stoneship, o_tunnelLeave_init);
134 	REGISTER_OPCODE(207, Stoneship, o_chest_init);
135 	REGISTER_OPCODE(208, Stoneship, o_telescope_init);
136 	REGISTER_OPCODE(209, Stoneship, o_achenarDrawers_init);
137 	REGISTER_OPCODE(210, Stoneship, o_cloudOrb_init);
138 
139 	// "Exit" Opcodes
140 	REGISTER_OPCODE(300, Stoneship, NOP);
141 }
142 
disablePersistentScripts()143 void Stoneship::disablePersistentScripts() {
144 	_batteryCharging = false;
145 	_batteryDepleting = false;
146 	_batteryGaugeRunning = false;
147 	_telescopeRunning = false;
148 }
149 
runPersistentScripts()150 void Stoneship::runPersistentScripts() {
151 	if (_batteryCharging)
152 		chargeBattery_run();
153 
154 	if (_telescopeRunning)
155 		telescope_run();
156 
157 	if (_batteryGaugeRunning)
158 		batteryGauge_run();
159 
160 	if (_batteryDepleting)
161 		batteryDeplete_run();
162 
163 	if (_tunnelRunning)
164 		tunnel_run();
165 }
166 
getVar(uint16 var)167 uint16 Stoneship::getVar(uint16 var) {
168 	switch(var) {
169 	case 0: // Water Drained From Lighthouse / Right Button Of Pump
170 		return _state.pumpState == 4;
171 	case 1: // Water Drained From Tunnels To Brothers' Rooms / Middle Button Of Pump
172 		return _state.pumpState == 2;
173 	case 2: // Water Drained From Ship Cabin Tunnel / Left Button Of Pump
174 		return _state.pumpState == 1;
175 	case 3: // Lighthouse Chest Floating
176 		return _state.pumpState != 4 && !_state.chestValveState && !_state.chestWaterState;
177 	case 4: // Lighthouse State - Close Up
178 		if (_state.pumpState == 4) {
179 			return 1; // Drained
180 		} else {
181 			if (_state.chestValveState || _state.chestWaterState)
182 				return 0; // Flooded
183 			else
184 				return 2; // Flooded, Chest Floating
185 		}
186 	case 5: // Lighthouse Trapdoor State
187 		return _state.trapdoorState;
188 	case 6: // Chest valve state
189 		return _state.chestValveState;
190 	case 7: // Lighthouse Chest Unlocked
191 		return _state.chestOpenState;
192 	case 8: // Lighthouse Chest Key Position
193 		return _state.trapdoorKeyState;
194 	case 11: // Lighthouse Key State
195 		if (_state.chestOpenState) {
196 			if (_state.trapdoorKeyState == 1)
197 				return 1;
198 			else if (_state.trapdoorKeyState == 2)
199 				return 2;
200 			else
201 				return 3;
202 		} else {
203 			return 0;
204 		}
205 	case 12: // Trapdoor can be unlocked
206 		return _state.trapdoorKeyState == 1 && _state.trapdoorState == 2;
207 	case 13: // State Of Tunnels To Brothers' Rooms - Close Up
208 		if (_state.generatorPowerAvailable != 1) {
209 			if (_state.pumpState != 2)
210 				return 0; // Dark, Flooded
211 			else
212 				return 1; // Dark, Drained
213 		} else {
214 			if (_state.pumpState != 2)
215 				return 2; // Lit, Flooded
216 			else
217 				return 3; // Lit, Drained
218 		}
219 	case 14: // State Of Tunnels lights To Brothers' Rooms - Far
220 		return _state.generatorPowerAvailable;
221 	case 15: // Side Door in Tunnels To Brother's Rooms Open
222 		if (_state.generatorPowerAvailable == 1)
223 			return _state.sideDoorOpened;
224 		else
225 			return 0;
226 	case 16: // Ship Chamber Light State
227 		return _state.lightState;
228 	case 17: // Sirrus' Room Drawer with Drugs Open
229 		return _siriusDrawerDrugsOpen;
230 	case 18: // Brother Room Door Open
231 		return _brotherDoorOpen;
232 	case 19: // Brother Room Door State
233 		if (_brotherDoorOpen) {
234 			if (_state.lightState)
235 				return 2; // Open, Light On
236 			else
237 				return 1; // Open, Light Off
238 		} else {
239 			return 0; // Closed
240 		}
241 	case 20: // Ship Chamber Table/Book State
242 		return _cabinMystBookPresent;
243 	case 21: // Brothers Rooms' Chest Of Drawers Drawer State
244 		return _chestDrawersOpen;
245 	case 28: // Telescope Angle Position
246 		return 0;
247 	case 29: // Achenar's Room Rose/Skull Hologram Button Lit
248 		return _hologramTurnedOn;
249 	case 30: // Light State in Tunnel to Compass Rose Room
250 		if (_state.generatorPowerAvailable == 1) {
251 			if (_state.lightState)
252 				return 0;
253 			else
254 				return 1;
255 		} else {
256 			return 2;
257 		}
258 	case 31: // Lighthouse Lamp Room Battery Pack Indicator Light
259 		return batteryRemainingCharge() >= 10;
260 	case 32: // Lighthouse Lamp Room Battery Pack Meter Level
261 		return 0;
262 	case 33: // State of Side Door in Tunnels to Compass Rose Room
263 		if (_state.sideDoorOpened)
264 			return 2;
265 		else
266 			return _state.generatorPowerAvailable == 1;
267 	case 34: // Achenar's Room Drawer with Torn Note Closed
268 		return _chestAchenarBottomDrawerClosed;
269 	case 35: // Sirrus' Room Drawer #4 (Bottom) Open and Red Page State
270 		if (_chestDrawersOpen == 4)
271 			return getVar(102);
272 		else
273 			return 2;
274 	case 36: // Ship Chamber Door State
275 		if (_tempVar) {
276 			if (_state.lightState)
277 				return 2; // Open, Light On
278 			else
279 				return 1; // Open, Light Off
280 		} else {
281 			return 0; // Closed
282 		}
283 	case 102: // Red page
284 		return !(_globals.redPagesInBook & 8) && (_globals.heldPage != kRedStoneshipPage);
285 	case 103: // Blue page
286 		return !(_globals.bluePagesInBook & 8) && (_globals.heldPage != kBlueStoneshipPage);
287 	default:
288 		return MystScriptParser::getVar(var);
289 	}
290 }
291 
toggleVar(uint16 var)292 void Stoneship::toggleVar(uint16 var) {
293 	switch(var) {
294 	case 0: // Water Drained From Lighthouse / Right Button Of Pump
295 		if (_state.pumpState == 4)
296 			_state.pumpState = 0;
297 		else
298 			_state.pumpState = 4;
299 		break;
300 	case 1: // Water Drained From Tunnels To Brothers' Rooms / Middle Button Of Pump
301 		if (_state.pumpState == 2)
302 			_state.pumpState = 0;
303 		else
304 			_state.pumpState = 2;
305 		break;
306 	case 2: // Water Drained From Ship Cabin Tunnel / Left Button Of Pump
307 		if (_state.pumpState == 1)
308 			_state.pumpState = 0;
309 		else
310 			_state.pumpState = 1;
311 		break;
312 	case 6: // Chest valve state
313 		_state.chestValveState = (_state.chestValveState + 1) % 2;
314 		break;
315 	case 8:  // Lighthouse Chest Key Position
316 		if (_state.trapdoorKeyState) {
317 			if (_state.trapdoorKeyState == 1)
318 				_state.trapdoorKeyState = 2;
319 			else
320 				_state.trapdoorKeyState = 1;
321 		}
322 		break;
323 	case 10: // Chest water state
324 		_state.chestWaterState = 0;
325 		break;
326 	case 11:
327 		if (_state.chestOpenState)
328 			_state.trapdoorKeyState = _state.trapdoorKeyState != 1;
329 		break;
330 	case 20: // Ship Chamber Table/Book State
331 		_cabinMystBookPresent = (_cabinMystBookPresent + 1) % 2;
332 		break;
333 	case 29: // Achenar's Room Rose/Skull Hologram Button Lit
334 		_hologramTurnedOn = (_hologramTurnedOn + 1) % 2;
335 		break;
336 	case 102: // Red page
337 		if (!(_globals.redPagesInBook & 8)) {
338 			if (_globals.heldPage == kRedStoneshipPage)
339 				_globals.heldPage = kNoPage;
340 			else
341 				_globals.heldPage = kRedStoneshipPage;
342 		}
343 		break;
344 	case 103: // Blue page
345 		if (!(_globals.bluePagesInBook & 8)) {
346 			if (_globals.heldPage == kBlueStoneshipPage)
347 				_globals.heldPage = kNoPage;
348 			else
349 				_globals.heldPage = kBlueStoneshipPage;
350 		}
351 		break;
352 	default:
353 		MystScriptParser::toggleVar(var);
354 		break;
355 	}
356 }
357 
setVarValue(uint16 var,uint16 value)358 bool Stoneship::setVarValue(uint16 var, uint16 value) {
359 	bool refresh = false;
360 
361 	switch (var) {
362 	case 5: // Lighthouse Trapdoor State
363 		_state.trapdoorState = value;
364 		break;
365 	case 7:
366 		if (_state.chestOpenState != value)
367 			_state.chestOpenState = value;
368 		break;
369 	case 8: // Lighthouse Chest Key Position
370 		_state.trapdoorKeyState = value;
371 		break;
372 	case 15: // Side Door in Tunnels To Brother's Rooms Open
373 		if (_state.sideDoorOpened != value) {
374 			if (!value && _state.generatorPowerAvailable == 2)
375 				_state.generatorPowerAvailable = 0;
376 			_state.sideDoorOpened = value;
377 			refresh = true;
378 		}
379 		break;
380 	case 17: // Sirrus' Room Drawer with Drugs Open
381 		if (_siriusDrawerDrugsOpen != value) {
382 			_siriusDrawerDrugsOpen = value;
383 			refresh = true;
384 		}
385 		break;
386 	case 18: // Brother Room Door Open
387 		if (_brotherDoorOpen != value) {
388 			_brotherDoorOpen = value;
389 			refresh = true;
390 		}
391 		break;
392 	case 21: // Brothers Rooms' Chest Of Drawers Drawer State
393 		if (_chestDrawersOpen != value) {
394 			_chestDrawersOpen = value;
395 			refresh = true;
396 		}
397 		break;
398 	case 29: // Achenar's Room Rose/Skull Hologram Button Lit
399 		_hologramTurnedOn = value;
400 		break;
401 	case 34: // Achenar's Room Drawer with Torn Note Closed
402 		_chestAchenarBottomDrawerClosed = value;
403 		break;
404 	default:
405 		refresh = MystScriptParser::setVarValue(var, value);
406 		break;
407 	}
408 
409 	return refresh;
410 }
411 
o_pumpTurnOff(uint16 var,const ArgumentsArray & args)412 void Stoneship::o_pumpTurnOff(uint16 var, const ArgumentsArray &args) {
413 	if (_state.pumpState) {
414 		uint16 buttonVar = 0;
415 
416 		switch (_state.pumpState) {
417 		case 1:
418 			buttonVar = 2;
419 			break;
420 		case 2:
421 			buttonVar = 1;
422 			break;
423 		case 4:
424 			buttonVar = 0;
425 			break;
426 		default:
427 			warning("Incorrect pump state");
428 		}
429 
430 		for (uint i = 0; i < _vm->getCard()->_resources.size(); i++) {
431 			MystArea *resource = _vm->getCard()->_resources[i];
432 			if (resource->hasType(kMystAreaImageSwitch) && resource->getImageSwitchVar() == buttonVar) {
433 				static_cast<MystAreaImageSwitch *>(resource)->drawConditionalDataToScreen(0, true);
434 				break;
435 			}
436 		}
437 	}
438 }
439 
o_brotherDoorOpen(uint16 var,const ArgumentsArray & args)440 void Stoneship::o_brotherDoorOpen(uint16 var, const ArgumentsArray &args) {
441 	_brotherDoorOpen = 1;
442 	_vm->getCard()->redrawArea(19, 0);
443 	animatedUpdate(args, 5);
444 }
445 
o_cabinBookMovie(uint16 var,const ArgumentsArray & args)446 void Stoneship::o_cabinBookMovie(uint16 var, const ArgumentsArray &args) {
447 	uint16 startTime = args[0];
448 	uint16 endTime = args[1];
449 
450 	VideoEntryPtr book = _vm->playMovie("bkroom", kStoneshipStack);
451 	book->moveTo(159, 99);
452 	book->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600));
453 
454 	_vm->waitUntilMovieEnds(book);
455 }
456 
o_drawerOpenSirius(uint16 var,const ArgumentsArray & args)457 void Stoneship::o_drawerOpenSirius(uint16 var, const ArgumentsArray &args) {
458 	MystAreaImageSwitch *drawer = _vm->getCard()->getResource<MystAreaImageSwitch>(args[0]);
459 
460 	if (drawer->getImageSwitchVar() == 35) {
461 		drawer->drawConditionalDataToScreen(getVar(102), 0);
462 	} else {
463 		drawer->drawConditionalDataToScreen(0, 0);
464 	}
465 
466 	TransitionType transition = kTransitionTopToBottom;
467 	if (args.size() == 2 && args[1])
468 		transition = kTransitionCopy;
469 
470 	_vm->_gfx->runTransition(transition, drawer->getRect(), 25, 5);
471 }
472 
o_drawerClose(uint16 var,const ArgumentsArray & args)473 void Stoneship::o_drawerClose(uint16 var, const ArgumentsArray &args) {
474 	drawerClose(args[0]);
475 }
476 
o_telescopeStart(uint16 var,const ArgumentsArray & args)477 void Stoneship::o_telescopeStart(uint16 var, const ArgumentsArray &args) {
478 	const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
479 	_telescopeOldMouse = mouse.x;
480 	_vm->_cursor->setCursor(700);
481 }
482 
o_telescopeMove(uint16 var,const ArgumentsArray & args)483 void Stoneship::o_telescopeMove(uint16 var, const ArgumentsArray &args) {
484 	MystAreaDrag *display = getInvokingResource<MystAreaDrag>();
485 	const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
486 
487 	// Compute telescope position
488 	_telescopePosition = (_telescopePosition - (mouse.x - _telescopeOldMouse) / 2 + 3240) % 3240;
489 	_telescopeOldMouse = mouse.x;
490 
491 	// Copy image to screen
492     Common::Rect src = Common::Rect(_telescopePosition, 0, _telescopePosition + 112, 112);
493     _vm->_gfx->copyImageSectionToScreen(_telescopePanorama, src, display->getRect());
494 
495     // Draw lighthouse
496     telescopeLighthouseDraw();
497 }
498 
o_telescopeStop(uint16 var,const ArgumentsArray & args)499 void Stoneship::o_telescopeStop(uint16 var, const ArgumentsArray &args) {
500 	_vm->refreshCursor();
501 }
502 
o_generatorStart(uint16 var,const ArgumentsArray & args)503 void Stoneship::o_generatorStart(uint16 var, const ArgumentsArray &args) {
504 	MystAreaDrag *handle = getInvokingResource<MystAreaDrag>();
505 
506 	uint16 soundId = handle->getList1(0);
507 	if (soundId)
508 		_vm->_sound->playEffect(soundId);
509 
510 	if (_state.generatorDuration)
511 		_state.generatorDuration -= _vm->getTotalPlayTime() - _state.generatorDepletionTime;
512 
513 	// Start charging the battery
514 	_batteryDepleting = false;
515 	_batteryCharging = true;
516 	_batteryNextTime = _vm->getTotalPlayTime() + 1000;
517 
518 	// Start handle movie
519 	MystAreaVideo *movie = static_cast<MystAreaVideo *>(handle->getSubResource(0));
520 	movie->playMovie();
521 
522 	soundId = handle->getList2(0);
523 	if (soundId)
524 		_vm->_sound->playEffect(soundId, true);
525 }
526 
o_generatorStop(uint16 var,const ArgumentsArray & args)527 void Stoneship::o_generatorStop(uint16 var, const ArgumentsArray &args) {
528 	_batteryCharging = false;
529 
530 	if (_state.generatorDuration) {
531 		// Clip battery power
532 		if (_state.generatorDuration > 600000)
533 			_state.generatorDuration = 600000;
534 
535 		// Start depleting power
536 		_state.generatorDepletionTime = _vm->getTotalPlayTime() + _state.generatorDuration;
537 		_state.generatorPowerAvailable = 1;
538 		_batteryDepleting = true;
539 		_batteryNextTime = _vm->getTotalPlayTime() + 60000;
540 	}
541 
542 	// Pause handle movie
543 	MystAreaDrag *handle = getInvokingResource<MystAreaDrag>();
544 	MystAreaVideo *movie = static_cast<MystAreaVideo *>(handle->getSubResource(0));
545 	movie->pauseMovie(true);
546 
547 	uint16 soundId = handle->getList3(0);
548 	if (soundId)
549 		_vm->_sound->playEffect(soundId);
550 }
551 
chargeBattery_run()552 void Stoneship::chargeBattery_run() {
553 	uint32 time = _vm->getTotalPlayTime();
554 
555 	if (time > _batteryNextTime) {
556 		_batteryNextTime = time + 1000;
557 		_state.generatorDuration += 30000;
558 	}
559 }
560 
batteryRemainingCharge()561 uint16 Stoneship::batteryRemainingCharge() {
562 	uint32 time = _vm->getTotalPlayTime();
563 
564 	if (_state.generatorDepletionTime > time) {
565 		return (_state.generatorDepletionTime - time) / 7500;
566 	} else {
567 		return 0;
568 	}
569 }
570 
batteryDeplete_run()571 void Stoneship::batteryDeplete_run() {
572 	uint32 time = _vm->getTotalPlayTime();
573 
574 	if (time > _batteryNextTime) {
575 		if (_state.generatorDuration > 60000) {
576 			_state.generatorDuration -= 60000;
577 			_batteryNextTime = time + 60000;
578 		} else { // Battery depleted
579 			_state.generatorDuration = 0;
580 			_state.generatorDepletionTime = 0;
581 
582 			if (_state.sideDoorOpened)
583 				_state.generatorPowerAvailable = 2;
584 			else
585 				_state.generatorPowerAvailable = 0;
586 
587 			_batteryDepleting = false;
588 		}
589 	}
590 }
591 
o_drawerOpenAchenar(uint16 var,const ArgumentsArray & args)592 void Stoneship::o_drawerOpenAchenar(uint16 var, const ArgumentsArray &args) {
593 	MystAreaImageSwitch *drawer = _vm->getCard()->getResource<MystAreaImageSwitch>(args[0]);
594 	drawer->drawConditionalDataToScreen(0, 0);
595 	_vm->_gfx->runTransition(kTransitionTopToBottom, drawer->getRect(), 25, 5);
596 }
597 
o_hologramPlayback(uint16 var,const ArgumentsArray & args)598 void Stoneship::o_hologramPlayback(uint16 var, const ArgumentsArray &args) {
599 	// Used for Card 2013 (Achenar's Rose-Skull Hologram)
600 	uint16 startPoint = args[0];
601 	uint16 endPoint = args[1];
602 	// uint16 direction = args[2];
603 
604 	_hologramDisplay->setBlocking(false);
605 	VideoEntryPtr displayMovie = _hologramDisplay->playMovie();
606 
607 	if (_hologramTurnedOn) {
608 		if (_hologramDisplayPos)
609 			endPoint = _hologramDisplayPos;
610 		displayMovie->setBounds(Audio::Timestamp(0, startPoint, 600), Audio::Timestamp(0, endPoint, 600));
611 	} else {
612 		displayMovie->setBounds(Audio::Timestamp(0, startPoint, 600), Audio::Timestamp(0, endPoint, 600));
613 	}
614 
615 	_vm->waitUntilMovieEnds(displayMovie);
616 }
617 
o_hologramSelectionStart(uint16 var,const ArgumentsArray & args)618 void Stoneship::o_hologramSelectionStart(uint16 var, const ArgumentsArray &args) {
619 	//_vm->_cursor->setCursor(0);
620 }
621 
o_hologramSelectionMove(uint16 var,const ArgumentsArray & args)622 void Stoneship::o_hologramSelectionMove(uint16 var, const ArgumentsArray &args) {
623 	MystAreaDrag *handle = getInvokingResource<MystAreaDrag>();
624 	const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
625 
626 	if (handle->getRect().contains(mouse)) {
627 		int16 position = mouse.x - 143;
628 		position = CLIP<int16>(position, 0, 242);
629 
630 		// Draw handle movie frame
631 		uint16 selectionPos = position * 1500 / 243;
632 
633 		VideoEntryPtr handleMovie = _hologramSelection->playMovie();
634 		_vm->_video->drawVideoFrame(handleMovie, Audio::Timestamp(0, selectionPos, 600));
635 
636 		_hologramDisplayPos = position * 1450 / 243 + 350;
637 
638 		// Draw display movie frame
639 		if (_hologramTurnedOn) {
640 			_hologramDisplay->setBlocking(false);
641 			VideoEntryPtr displayMovie = _hologramDisplay->playMovie();
642 			_vm->_video->drawVideoFrame(displayMovie, Audio::Timestamp(0, _hologramDisplayPos, 600));
643 		}
644 	}
645 }
646 
o_hologramSelectionStop(uint16 var,const ArgumentsArray & args)647 void Stoneship::o_hologramSelectionStop(uint16 var, const ArgumentsArray &args) {
648 	_vm->refreshCursor();
649 }
650 
o_compassButton(uint16 var,const ArgumentsArray & args)651 void Stoneship::o_compassButton(uint16 var, const ArgumentsArray &args) {
652 	// Used on Card 2111 (Compass Rose)
653 	// Called when Button Clicked.
654 	uint16 correctButton = args[0];
655 
656 	if (correctButton) {
657 		// Correct Button -> Light On Logic
658 		_state.lightState = 1;
659 	} else {
660 		// Wrong Button -> Power Failure Logic
661 		_state.generatorPowerAvailable = 2;
662 		_state.lightState = 0;
663 		_state.generatorDepletionTime = 0;
664 		_state.generatorDuration = 0;
665 
666 		_batteryDepleting = false;
667 	}
668 
669 	o_redrawCard(var, args);
670 }
671 
o_chestValveVideos(uint16 var,const ArgumentsArray & args)672 void Stoneship::o_chestValveVideos(uint16 var, const ArgumentsArray &args) {
673 	_vm->_sound->playEffect(2132);
674 
675 	if (_state.chestValveState) {
676 		// Valve closing
677 		VideoEntryPtr valve = _vm->playMovie("ligspig", kStoneshipStack);
678 		valve->moveTo(97, 267);
679 		valve->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 350, 600));
680 
681 		_vm->waitUntilMovieEnds(valve);
682 	} else if (_state.chestWaterState) {
683 		// Valve opening, spilling water
684 		VideoEntryPtr valve = _vm->playMovie("ligspig", kStoneshipStack);
685 		valve->moveTo(97, 267);
686 		valve->setBounds(Audio::Timestamp(0, 350, 600), Audio::Timestamp(0, 650, 600));
687 
688 		_vm->waitUntilMovieEnds(valve);
689 
690 		_vm->_sound->playEffect(3132);
691 
692 		for (uint i = 0; i < 25; i++) {
693 			valve = _vm->playMovie("ligspig", kStoneshipStack);
694 			valve->moveTo(97, 267);
695 			valve->setBounds(Audio::Timestamp(0, 650, 600), Audio::Timestamp(0, 750, 600));
696 			_vm->waitUntilMovieEnds(valve);
697 		}
698 
699 		_vm->_sound->resumeBackground();
700 	} else {
701 		// Valve opening
702 		VideoEntryPtr valve = _vm->playMovie("ligspig", kStoneshipStack);
703 		valve->moveTo(97, 267);
704 		valve->seek(Audio::Timestamp(0, 350, 600));
705 		valve->setRate(-1);
706 
707 		_vm->waitUntilMovieEnds(valve);
708 	}
709 }
710 
o_chestDropKey(uint16 var,const ArgumentsArray & args)711 void Stoneship::o_chestDropKey(uint16 var, const ArgumentsArray &args) {
712 	// If holding Key to Lamp Room Trapdoor, drop to bottom of
713 	// Lighthouse...
714 	if (_state.trapdoorKeyState == 1) {
715 		_vm->setMainCursor(_savedCursorId);
716 		_state.trapdoorKeyState = 2;
717 	}
718 }
719 
o_trapLockOpen(uint16 var,const ArgumentsArray & args)720 void Stoneship::o_trapLockOpen(uint16 var, const ArgumentsArray &args) {
721 	VideoEntryPtr lock = _vm->playMovie("openloc", kStoneshipStack);
722 	lock->moveTo(187, 71);
723 	lock->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 750, 600));
724 	_vm->waitUntilMovieEnds(lock);
725 
726 	_vm->_sound->playEffect(2143);
727 
728 	lock = _vm->playMovie("openloc", kStoneshipStack);
729 	lock->moveTo(187, 71);
730 	lock->setBounds(Audio::Timestamp(0, 750, 600), Audio::Timestamp(0, 10000, 600));
731 	_vm->waitUntilMovieEnds(lock);
732 
733 	if (_state.pumpState != 4)
734 		_vm->_sound->playEffect(4143);
735 }
736 
o_sideDoorsMovies(uint16 var,const ArgumentsArray & args)737 void Stoneship::o_sideDoorsMovies(uint16 var, const ArgumentsArray &args) {
738 	// Used for Cards 2285, 2289, 2247, 2251 (Side Doors in Tunnels Down To Brothers Rooms)
739 	uint16 movieId = args[0];
740 
741 	_vm->_cursor->hideCursor();
742 	_vm->_sound->pauseBackground();
743 
744 	switch (movieId) {
745 	case 0:
746 		// Card 2251
747 		_vm->playMovieBlocking("tunaup", kStoneshipStack, 149, 161);
748 		break;
749 	case 1:
750 		// Card 2247
751 		_vm->playMovieBlocking("tunadown", kStoneshipStack, 218, 150);
752 		break;
753 	case 2:
754 		// Card 2289
755 		_vm->playMovieBlocking("tuncup", kStoneshipStack, 259, 161);
756 		break;
757 	case 3:
758 		// Card 2285
759 		_vm->playMovieBlocking("tuncdown", kStoneshipStack, 166, 150);
760 		break;
761 	default:
762 		warning("Opcode 120 MovieId Out Of Range");
763 		break;
764 	}
765 
766 	_vm->_sound->resumeBackground();
767 	_vm->_cursor->showCursor();
768 }
769 
o_cloudOrbEnter(uint16 var,const ArgumentsArray & args)770 void Stoneship::o_cloudOrbEnter(uint16 var, const ArgumentsArray &args) {
771 	_vm->_sound->playEffect(_cloudOrbSound, true);
772 	_cloudOrbMovie->playMovie();
773 }
774 
o_cloudOrbLeave(uint16 var,const ArgumentsArray & args)775 void Stoneship::o_cloudOrbLeave(uint16 var, const ArgumentsArray &args) {
776 	_cloudOrbMovie->pauseMovie(true);
777 	_vm->_sound->playEffect(_cloudOrbStopSound);
778 	_vm->_gfx->runTransition(kTransitionTopToBottom, getInvokingResource<MystArea>()->getRect(), 4, 0);
779 }
780 
o_drawerCloseOpened(uint16 var,const ArgumentsArray & args)781 void Stoneship::o_drawerCloseOpened(uint16 var, const ArgumentsArray &args) {
782 	uint16 drawerOpen = getVar(var);
783 	if (drawerOpen)
784 		drawerClose(args[0] + drawerOpen - 1);
785 }
786 
drawerClose(uint16 drawer)787 void Stoneship::drawerClose(uint16 drawer) {
788 	_chestDrawersOpen = 0;
789 	_vm->getCard()->drawBackground();
790 	_vm->getCard()->drawResourceImages();
791 
792 	MystArea *res = _vm->getCard()->getResource<MystArea>(drawer);
793 	_vm->_gfx->runTransition(kTransitionBottomToTop, res->getRect(), 25, 5);
794 }
795 
o_hologramDisplay_init(uint16 var,const ArgumentsArray & args)796 void Stoneship::o_hologramDisplay_init(uint16 var, const ArgumentsArray &args) {
797 	_hologramDisplay = getInvokingResource<MystAreaVideo>();
798 
799 	_hologramDisplayPos = 0;
800 }
801 
o_hologramSelection_init(uint16 var,const ArgumentsArray & args)802 void Stoneship::o_hologramSelection_init(uint16 var, const ArgumentsArray &args) {
803 	_hologramSelection = getInvokingResource<MystAreaVideo>();
804 }
805 
batteryGaugeUpdate()806 void Stoneship::batteryGaugeUpdate() {
807 	uint16 charge = 0;
808 
809 	if (_state.generatorDepletionTime) {
810 		charge = batteryRemainingCharge();
811 	}
812 
813 	Common::Rect rect = _batteryGauge->getRect();
814 
815 	rect.top = rect.bottom - charge;
816 
817 	_batteryGauge->setRect(rect);
818 }
819 
o_battery_init(uint16 var,const ArgumentsArray & args)820 void Stoneship::o_battery_init(uint16 var, const ArgumentsArray &args) {
821 	// Used for Card 2160 (Lighthouse Battery Pack Closeup)
822 	_batteryGauge = getInvokingResource<MystAreaImageSwitch>();
823 
824 	batteryGaugeUpdate();
825 }
826 
o_tunnelEnter_init(uint16 var,const ArgumentsArray & args)827 void Stoneship::o_tunnelEnter_init(uint16 var, const ArgumentsArray &args) {
828 	o_tunnel_init(var, args);
829 
830 	_tunnelRunning = true;
831 	_tunnelNextTime = _vm->getTotalPlayTime() + 1500;
832 }
833 
o_batteryGauge_init(uint16 var,const ArgumentsArray & args)834 void Stoneship::o_batteryGauge_init(uint16 var, const ArgumentsArray &args) {
835 	_batteryLastCharge = batteryRemainingCharge();
836 	_batteryGaugeRunning = true;
837 }
838 
batteryGauge_run()839 void Stoneship::batteryGauge_run() {
840 	uint16 batteryCharge = batteryRemainingCharge();
841 
842 	if (batteryCharge != _batteryLastCharge) {
843 		batteryGaugeUpdate();
844 
845 		_batteryLastCharge = batteryCharge;
846 
847 		// Redraw card
848 		_vm->getCard()->drawBackground();
849 		_vm->getCard()->drawResourceImages();
850 		_vm->_gfx->copyBackBufferToScreen(Common::Rect(544, 333));
851 	}
852 }
853 
o_tunnel_init(uint16 var,const ArgumentsArray & args)854 void Stoneship::o_tunnel_init(uint16 var, const ArgumentsArray &args) {
855 		_tunnelImagesCount = args[0];
856 
857 		assert(_tunnelImagesCount <= 2 && "Too many images");
858 
859 		for (uint i = 0; i < _tunnelImagesCount; i++) {
860 			_tunnelImages[i] = args[i + 1];
861 		}
862 
863 		_tunnelAlarmSound = args[args.size() - 1];
864 
865 		debugC(kDebugScript, "\timage count: %d", _tunnelImagesCount);
866 		debugC(kDebugScript, "\tsoundIdAlarm: %d", _tunnelAlarmSound);
867 }
868 
tunnel_run()869 void Stoneship::tunnel_run() {
870 	uint32 time = _vm->getTotalPlayTime();
871 
872 	if (time > _tunnelNextTime) {
873 		_tunnelNextTime = time + 1500;
874 		if (_state.generatorPowerAvailable == 2) {
875 			// Draw tunnel black
876 			if (_tunnelImagesCount) {
877 				_vm->_gfx->copyImageToScreen(_tunnelImages[1], Common::Rect(544, 333));
878 			}
879 
880 			_vm->_sound->playEffect(_tunnelAlarmSound);
881 
882 			// Draw tunnel dark
883 			if (_tunnelImagesCount) {
884 				_vm->_gfx->copyImageToScreen(_tunnelImages[0], Common::Rect(544, 333));
885 			}
886 		}
887 	}
888 }
889 
o_tunnelLeave_init(uint16 var,const ArgumentsArray & args)890 void Stoneship::o_tunnelLeave_init(uint16 var, const ArgumentsArray &args) {
891 	_tunnelRunning = false;
892 }
893 
o_chest_init(uint16 var,const ArgumentsArray & args)894 void Stoneship::o_chest_init(uint16 var, const ArgumentsArray &args) {
895 	_state.chestOpenState = 0;
896 }
897 
o_telescope_init(uint16 var,const ArgumentsArray & args)898 void Stoneship::o_telescope_init(uint16 var, const ArgumentsArray &args) {
899 	// Used in Card 2218 (Telescope view)
900 	_telescopePanorama = args[0];
901 	_telescopeLighthouseOff = args[1];
902 	_telescopeLighthouseOn = args[2];
903 	_telescopePosition = 0;
904 
905 	_telescopeRunning = true;
906 	_telescopeLighthouseState = false;
907 	_telescopeNexTime = _vm->getTotalPlayTime() + 1000;
908 }
909 
telescope_run()910 void Stoneship::telescope_run() {
911 	uint32 time = _vm->getTotalPlayTime();
912 
913 	if (time > _telescopeNexTime) {
914 
915 		_telescopeNexTime = time + 1000;
916 		_telescopeLighthouseState = !_telescopeLighthouseState;
917 
918 		telescopeLighthouseDraw();
919 	}
920 }
921 
telescopeLighthouseDraw()922 void Stoneship::telescopeLighthouseDraw() {
923 	if (_telescopePosition > 1137 && _telescopePosition < 1294) {
924 		uint16 imageId = _telescopeLighthouseOff;
925 
926 		if (_state.generatorPowerAvailable == 1 && _telescopeLighthouseState)
927 			imageId = _telescopeLighthouseOn;
928 
929 		Common::Rect src(1205, 0, 1205 + 131, 112);
930 		src.clip(Common::Rect(_telescopePosition, 0, _telescopePosition + 112, 112));
931 		src.translate(-1205, 0);
932 		src.clip(131, 112);
933 
934 		Common::Rect dest(_telescopePosition, 0, _telescopePosition + 112, 112);
935 		dest.clip(Common::Rect(1205, 0, 1205 + 131, 112));
936 		dest.translate(-_telescopePosition, 0);
937 		dest.clip(112, 112);
938 		dest.translate(222, 112);
939 
940 		_vm->_gfx->copyImageSectionToScreen(imageId, src, dest);
941 	}
942 }
943 
o_achenarDrawers_init(uint16 var,const ArgumentsArray & args)944 void Stoneship::o_achenarDrawers_init(uint16 var, const ArgumentsArray &args) {
945 	// Used for Card 2004 (Achenar's Room Drawers)
946 	if (!_chestAchenarBottomDrawerClosed) {
947 		uint16 count1 = args[0];
948 		for (uint16 i = 0; i < count1; i++) {
949 			debugC(kDebugScript, "Disable hotspot index %d", args[i + 1]);
950 			_vm->getCard()->setResourceEnabled(args[i + 1], false);
951 		}
952 		uint16 count2 = args[count1 + 1];
953 		for (uint16 i = 0; i < count2; i++) {
954 			debugC(kDebugScript, "Enable hotspot index %d", args[i + count1 + 2]);
955 			_vm->getCard()->setResourceEnabled(args[i + count1 + 2], true);
956 		}
957 	}
958 }
959 
o_cloudOrb_init(uint16 var,const ArgumentsArray & args)960 void Stoneship::o_cloudOrb_init(uint16 var, const ArgumentsArray &args) {
961 	_cloudOrbMovie = getInvokingResource<MystAreaVideo>();
962 	_cloudOrbSound = args[0];
963 	_cloudOrbStopSound = args[1];
964 }
965 
966 } // End of namespace MystStacks
967 } // End of namespace Mohawk
968