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/mechanical.h"
32
33 #include "common/events.h"
34 #include "common/system.h"
35
36 namespace Mohawk {
37 namespace MystStacks {
38
Mechanical(MohawkEngine_Myst * vm)39 Mechanical::Mechanical(MohawkEngine_Myst *vm) :
40 MystScriptParser(vm, kMechanicalStack),
41 _state(vm->_gameState->_mechanical) {
42 setupOpcodes();
43
44 _elevatorGoingMiddle = false;
45 _elevatorPosition = 0;
46 _elevatorGoingDown = 0;
47 _elevatorRotationSpeed = 0;
48 _elevatorRotationGearPosition = 0;
49 _elevatorRotationSoundId = 0;
50 _elevatorRotationLeverMoving = false;
51 _elevatorTooLate = false;
52 _elevatorInCabin = false;
53 _elevatorTopCounter = 0;
54 _elevatorNextTime = 0;
55
56 _crystalLit = 0;
57
58 _mystStaircaseState = false;
59 _fortressDirection = kSouth;
60 _gearsWereRunning = false;
61
62 _fortressRotationShortMovieWorkaround = false;
63 _fortressRotationShortMovieCount = 0;
64 _fortressRotationShortMovieLast = 0;
65
66 _fortressRotationRunning = false;
67 _fortressRotationSpeed = 0;
68 _fortressRotationBrake = 0;
69 _fortressRotationGears = nullptr;
70
71 _fortressSimulationRunning = false;
72 _fortressSimulationInit = false;
73 _fortressSimulationSpeed = 0;
74 _fortressSimulationBrake = 0;
75 _fortressSimulationStartSound1 = 0;
76 _fortressSimulationStartSound2 = 0;
77 _fortressSimulationHolo = nullptr;
78 _fortressSimulationStartup = nullptr;
79 _fortressSimulationHoloRate = 0;
80
81 _birdSinging = false;
82 _birdCrankStartTime = 0;
83 _birdSingEndTime = 0;
84 _bird = nullptr;
85
86 _snakeBox = nullptr;
87 }
88
~Mechanical()89 Mechanical::~Mechanical() {
90 }
91
setupOpcodes()92 void Mechanical::setupOpcodes() {
93 // "Stack-Specific" Opcodes
94 REGISTER_OPCODE(100, Mechanical, o_throneEnablePassage);
95 REGISTER_OPCODE(101, Mechanical, o_birdCrankStart);
96 REGISTER_OPCODE(102, Mechanical, NOP);
97 REGISTER_OPCODE(103, Mechanical, o_birdCrankStop);
98 REGISTER_OPCODE(104, Mechanical, o_snakeBoxTrigger);
99 REGISTER_OPCODE(105, Mechanical, o_fortressStaircaseMovie);
100 REGISTER_OPCODE(106, Mechanical, o_elevatorRotationStart);
101 REGISTER_OPCODE(107, Mechanical, o_elevatorRotationMove);
102 REGISTER_OPCODE(108, Mechanical, o_elevatorRotationStop);
103 REGISTER_OPCODE(109, Mechanical, o_fortressRotationSpeedStart);
104 REGISTER_OPCODE(110, Mechanical, o_fortressRotationSpeedMove);
105 REGISTER_OPCODE(111, Mechanical, o_fortressRotationSpeedStop);
106 REGISTER_OPCODE(112, Mechanical, o_fortressRotationBrakeStart);
107 REGISTER_OPCODE(113, Mechanical, o_fortressRotationBrakeMove);
108 REGISTER_OPCODE(114, Mechanical, o_fortressRotationBrakeStop);
109 REGISTER_OPCODE(115, Mechanical, o_fortressSimulationSpeedStart);
110 REGISTER_OPCODE(116, Mechanical, o_fortressSimulationSpeedMove);
111 REGISTER_OPCODE(117, Mechanical, o_fortressSimulationSpeedStop);
112 REGISTER_OPCODE(118, Mechanical, o_fortressSimulationBrakeStart);
113 REGISTER_OPCODE(119, Mechanical, o_fortressSimulationBrakeMove);
114 REGISTER_OPCODE(120, Mechanical, o_fortressSimulationBrakeStop);
115 REGISTER_OPCODE(121, Mechanical, o_elevatorWindowMovie);
116 REGISTER_OPCODE(122, Mechanical, o_elevatorGoMiddle);
117 REGISTER_OPCODE(123, Mechanical, o_elevatorTopMovie);
118 REGISTER_OPCODE(124, Mechanical, o_fortressRotationSetPosition);
119 REGISTER_OPCODE(125, Mechanical, o_mystStaircaseMovie);
120 REGISTER_OPCODE(126, Mechanical, o_elevatorWaitTimeout);
121 REGISTER_OPCODE(127, Mechanical, o_crystalEnterYellow);
122 REGISTER_OPCODE(128, Mechanical, o_crystalLeaveYellow);
123 REGISTER_OPCODE(129, Mechanical, o_crystalEnterGreen);
124 REGISTER_OPCODE(130, Mechanical, o_crystalLeaveGreen);
125 REGISTER_OPCODE(131, Mechanical, o_crystalEnterRed);
126 REGISTER_OPCODE(132, Mechanical, o_crystalLeaveRed);
127
128 // "Init" Opcodes
129 REGISTER_OPCODE(200, Mechanical, o_throne_init);
130 REGISTER_OPCODE(201, Mechanical, o_fortressStaircase_init);
131 REGISTER_OPCODE(202, Mechanical, o_bird_init);
132 REGISTER_OPCODE(203, Mechanical, o_snakeBox_init);
133 REGISTER_OPCODE(204, Mechanical, o_elevatorRotation_init);
134 REGISTER_OPCODE(205, Mechanical, o_fortressRotation_init);
135 REGISTER_OPCODE(206, Mechanical, o_fortressSimulation_init);
136 REGISTER_OPCODE(209, Mechanical, o_fortressSimulationStartup_init);
137
138 // "Exit" Opcodes
139 REGISTER_OPCODE(300, Mechanical, NOP);
140 }
141
disablePersistentScripts()142 void Mechanical::disablePersistentScripts() {
143 _fortressSimulationRunning = false;
144 _elevatorRotationLeverMoving = false;
145 _birdSinging = false;
146 _fortressRotationRunning = false;
147 }
148
runPersistentScripts()149 void Mechanical::runPersistentScripts() {
150 if (_birdSinging)
151 birdSing_run();
152
153 if (_elevatorRotationLeverMoving)
154 elevatorRotation_run();
155
156 if (_elevatorGoingMiddle)
157 elevatorGoMiddle_run();
158
159 if (_fortressRotationRunning)
160 fortressRotation_run();
161
162 if (_fortressSimulationRunning)
163 fortressSimulation_run();
164 }
165
getVar(uint16 var)166 uint16 Mechanical::getVar(uint16 var) {
167 switch(var) {
168 case 0: // Achenar's Secret Panel State
169 return _state.achenarPanelState;
170 case 1: // Sirrus's Secret Panel State
171 return _state.sirrusPanelState;
172 case 2: // Achenar's Secret Room Crate Lid Open and Blue Page Present
173 if (_state.achenarCrateOpened) {
174 if (_globals.bluePagesInBook & 4 || _globals.heldPage == kBlueMechanicalPage)
175 return 2;
176 else
177 return 3;
178 } else {
179 return _globals.bluePagesInBook & 4 || _globals.heldPage == kBlueMechanicalPage;
180 }
181 case 3: // Achenar's Secret Room Crate State
182 return _state.achenarCrateOpened;
183 case 4: // Myst Book Room Staircase State
184 return _mystStaircaseState;
185 case 5: // Fortress Position
186 return _fortressDirection;
187 case 6: // Fortress Position - Big Cog Visible Through Doorway
188 return _fortressDirection == kSouth;
189 case 7: // Fortress Elevator Open
190 if (_state.elevatorRotation == 4)
191 return 1; // Open
192 else
193 return 0; // Closed
194 case 10: // Fortress Staircase State
195 return _state.staircaseState;
196 case 11: // Fortress Elevator Rotation Position
197 return _state.elevatorRotation;
198 case 12: // Fortress Elevator Rotation Cog Position
199 return 5 - (uint16)(_elevatorRotationGearPosition + 0.5) % 6;
200 case 13: // Elevator position
201 return _elevatorPosition;
202 case 14: // Elevator going down when at top
203 if (_elevatorGoingDown && _elevatorTooLate)
204 return 2;
205 else
206 return _elevatorGoingDown;
207 case 15: // Code Lock Execute Button Script
208 if (_mystStaircaseState)
209 return 0;
210 else if (_state.codeShape[0] == 2 && _state.codeShape[1] == 8
211 && _state.codeShape[2] == 5 && _state.codeShape[3] == 1)
212 return 1;
213 else
214 return 2;
215 case 16: // Code Lock Shape #1 - Left
216 case 17: // Code Lock Shape #2
217 case 18: // Code Lock Shape #3
218 case 19: // Code Lock Shape #4 - Right
219 return _state.codeShape[var - 16];
220 case 20: // Crystal Lit Flag - Yellow
221 return _crystalLit == 3;
222 case 21: // Crystal Lit Flag - Green
223 return _crystalLit == 1;
224 case 22: // Crystal Lit Flag - Red
225 return _crystalLit == 2;
226 case 102: // Red page
227 return !(_globals.redPagesInBook & 4) && (_globals.heldPage != kRedMechanicalPage);
228 case 103: // Blue page
229 return !(_globals.bluePagesInBook & 4) && (_globals.heldPage != kBlueMechanicalPage);
230 default:
231 return MystScriptParser::getVar(var);
232 }
233 }
234
toggleVar(uint16 var)235 void Mechanical::toggleVar(uint16 var) {
236 switch(var) {
237 case 0: // Achenar's Secret Panel State
238 _state.achenarPanelState ^= 1;
239 break;
240 case 1: // Sirrus's Secret Panel State
241 _state.sirrusPanelState ^= 1;
242 break;
243 case 3: // Achenar's Secret Room Crate State
244 _state.achenarCrateOpened ^= 1;
245 break;
246 case 4: // Myst Book Room Staircase State
247 _mystStaircaseState ^= 1;
248 break;
249 case 10: // Fortress Staircase State
250 _state.staircaseState ^= 1;
251 break;
252 case 16: // Code Lock Shape #1 - Left
253 case 17: // Code Lock Shape #2
254 case 18: // Code Lock Shape #3
255 case 19: // Code Lock Shape #4 - Right
256 _state.codeShape[var - 16] = (_state.codeShape[var - 16] + 1) % 10;
257 break;
258 case 23: // Elevator player is in cabin
259 _elevatorInCabin = false;
260 break;
261 case 102: // Red page
262 if (!(_globals.redPagesInBook & 4)) {
263 if (_globals.heldPage == kRedMechanicalPage)
264 _globals.heldPage = kNoPage;
265 else
266 _globals.heldPage = kRedMechanicalPage;
267 }
268 break;
269 case 103: // Blue page
270 if (!(_globals.bluePagesInBook & 4)) {
271 if (_globals.heldPage == kBlueMechanicalPage)
272 _globals.heldPage = kNoPage;
273 else
274 _globals.heldPage = kBlueMechanicalPage;
275 }
276 break;
277 default:
278 MystScriptParser::toggleVar(var);
279 break;
280 }
281 }
282
setVarValue(uint16 var,uint16 value)283 bool Mechanical::setVarValue(uint16 var, uint16 value) {
284 bool refresh = false;
285
286 switch (var) {
287 case 13:
288 _elevatorPosition = value;
289 break;
290 case 14: // Elevator going down when at top
291 _elevatorGoingDown = value;
292 break;
293 default:
294 refresh = MystScriptParser::setVarValue(var, value);
295 break;
296 }
297
298 return refresh;
299 }
300
o_throneEnablePassage(uint16 var,const ArgumentsArray & args)301 void Mechanical::o_throneEnablePassage(uint16 var, const ArgumentsArray &args) {
302 _vm->getCard()->getResource<MystArea>(args[0])->setEnabled(getVar(var));
303 }
304
o_birdCrankStart(uint16 var,const ArgumentsArray & args)305 void Mechanical::o_birdCrankStart(uint16 var, const ArgumentsArray &args) {
306 MystAreaDrag *crank = getInvokingResource<MystAreaDrag>();
307
308 uint16 crankSoundId = crank->getList2(0);
309 _vm->_sound->playEffect(crankSoundId, true);
310
311 _birdSingEndTime = 0;
312 _birdCrankStartTime = _vm->getTotalPlayTime();
313
314 MystAreaVideo *crankMovie = static_cast<MystAreaVideo *>(crank->getSubResource(0));
315 crankMovie->playMovie();
316 }
317
o_birdCrankStop(uint16 var,const ArgumentsArray & args)318 void Mechanical::o_birdCrankStop(uint16 var, const ArgumentsArray &args) {
319 MystAreaDrag *crank = getInvokingResource<MystAreaDrag>();
320
321 MystAreaVideo *crankMovie = static_cast<MystAreaVideo *>(crank->getSubResource(0));
322 crankMovie->pauseMovie(true);
323
324 uint16 crankSoundId = crank->getList2(1);
325 _vm->_sound->playEffect(crankSoundId);
326
327 _birdSingEndTime = 2 * _vm->getTotalPlayTime() - _birdCrankStartTime;
328 _birdSinging = true;
329
330 _bird->playMovie();
331 }
332
o_snakeBoxTrigger(uint16 var,const ArgumentsArray & args)333 void Mechanical::o_snakeBoxTrigger(uint16 var, const ArgumentsArray &args) {
334 // Used on Mechanical Card 6043 (Weapons Rack with Snake Box)
335 _snakeBox->playMovie();
336 }
337
o_fortressStaircaseMovie(uint16 var,const ArgumentsArray & args)338 void Mechanical::o_fortressStaircaseMovie(uint16 var, const ArgumentsArray &args) {
339 VideoEntryPtr staircase = _vm->playMovie("hhstairs", kMechanicalStack);
340 staircase->moveTo(174, 222);
341
342 if (_state.staircaseState) {
343 staircase->setBounds(Audio::Timestamp(0, 840, 600), Audio::Timestamp(0, 1680, 600));
344 } else {
345 staircase->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 840, 600));
346 }
347
348 _vm->waitUntilMovieEnds(staircase);
349 }
350
o_elevatorRotationStart(uint16 var,const ArgumentsArray & args)351 void Mechanical::o_elevatorRotationStart(uint16 var, const ArgumentsArray &args) {
352 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
353 lever->drawFrame(0);
354
355 _elevatorRotationLeverMoving = true;
356 _elevatorRotationSpeed = 0;
357
358 _vm->_sound->stopBackground();
359
360 _vm->_cursor->setCursor(700);
361 }
362
o_elevatorRotationMove(uint16 var,const ArgumentsArray & args)363 void Mechanical::o_elevatorRotationMove(uint16 var, const ArgumentsArray &args) {
364 const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
365 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
366
367 // Make the handle follow the mouse
368 int16 maxStep = lever->getNumFrames() - 1;
369 Common::Rect rect = lever->getRect();
370 int16 step = ((rect.bottom - mouse.y) * lever->getNumFrames()) / rect.height();
371 step = CLIP<int16>(step, 0, maxStep);
372
373 _elevatorRotationSpeed = step * 0.1f;
374
375 // Draw current frame
376 lever->drawFrame(step);
377 }
378
o_elevatorRotationStop(uint16 var,const ArgumentsArray & args)379 void Mechanical::o_elevatorRotationStop(uint16 var, const ArgumentsArray &args) {
380 const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
381 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
382
383 // Get current lever frame
384 int16 maxStep = lever->getNumFrames() - 1;
385 Common::Rect rect = lever->getRect();
386 int16 step = ((rect.bottom - mouse.y) * lever->getNumFrames()) / rect.height();
387 step = CLIP<int16>(step, 0, maxStep);
388
389 // Release lever
390 for (int i = step; i >= 0; i--) {
391 lever->drawFrame(i);
392 _vm->doFrame();
393 }
394
395 // Stop persistent script
396 _elevatorRotationLeverMoving = false;
397
398 float speed = _elevatorRotationSpeed * 10;
399
400 if (speed > 0) {
401
402 // Decrease speed
403 while (speed > 2) {
404 speed -= 0.5f;
405
406 _elevatorRotationGearPosition += speed * 0.1f;
407
408 if (_elevatorRotationGearPosition > 12)
409 break;
410
411 _vm->getCard()->redrawArea(12);
412 _vm->wait(100);
413 }
414
415 // Increment position
416 _state.elevatorRotation = (_state.elevatorRotation + 1) % 10;
417
418 _vm->_sound->playEffect(_elevatorRotationSoundId);
419 _vm->getCard()->redrawArea(11);
420 }
421
422 _vm->refreshCursor();
423 }
424
o_fortressRotationSpeedStart(uint16 var,const ArgumentsArray & args)425 void Mechanical::o_fortressRotationSpeedStart(uint16 var, const ArgumentsArray &args) {
426 _vm->_cursor->setCursor(700);
427
428 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
429 lever->drawFrame(0);
430 }
431
o_fortressRotationSpeedMove(uint16 var,const ArgumentsArray & args)432 void Mechanical::o_fortressRotationSpeedMove(uint16 var, const ArgumentsArray &args) {
433 const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
434 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
435
436 // Make the handle follow the mouse
437 int16 maxStep = lever->getNumFrames() - 1;
438 Common::Rect rect = lever->getRect();
439 int16 step = ((rect.top + 65 - mouse.y) * lever->getNumFrames()) / 65;
440 step = CLIP<int16>(step, 0, maxStep);
441
442 _fortressRotationSpeed = step;
443
444 // Draw current frame
445 lever->drawFrame(step);
446 }
447
o_fortressRotationSpeedStop(uint16 var,const ArgumentsArray & args)448 void Mechanical::o_fortressRotationSpeedStop(uint16 var, const ArgumentsArray &args) {
449 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
450
451 // Release lever
452 for (int i = _fortressRotationSpeed; i >= 0; i--) {
453 lever->drawFrame(i);
454 _vm->doFrame();
455 }
456
457 _fortressRotationSpeed = 0;
458
459 _vm->refreshCursor();
460 }
461
o_fortressRotationBrakeStart(uint16 var,const ArgumentsArray & args)462 void Mechanical::o_fortressRotationBrakeStart(uint16 var, const ArgumentsArray &args) {
463 _vm->_cursor->setCursor(700);
464
465 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
466 lever->drawFrame(_fortressRotationBrake);
467 }
468
o_fortressRotationBrakeMove(uint16 var,const ArgumentsArray & args)469 void Mechanical::o_fortressRotationBrakeMove(uint16 var, const ArgumentsArray &args) {
470 const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
471 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
472
473 // Make the handle follow the mouse
474 int16 maxStep = lever->getNumFrames() - 1;
475 Common::Rect rect = lever->getRect();
476 int16 step = ((rect.top + 65 - mouse.y) * lever->getNumFrames()) / 65;
477 step = CLIP<int16>(step, 0, maxStep);
478
479 _fortressRotationBrake = step;
480
481 // Draw current frame
482 lever->drawFrame(step);
483 }
484
o_fortressRotationBrakeStop(uint16 var,const ArgumentsArray & args)485 void Mechanical::o_fortressRotationBrakeStop(uint16 var, const ArgumentsArray &args) {
486 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
487 lever->drawFrame(_fortressRotationBrake);
488
489 _vm->refreshCursor();
490 }
491
o_fortressSimulationSpeedStart(uint16 var,const ArgumentsArray & args)492 void Mechanical::o_fortressSimulationSpeedStart(uint16 var, const ArgumentsArray &args) {
493 _vm->_cursor->setCursor(700);
494
495 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
496 lever->drawFrame(0);
497 }
498
o_fortressSimulationSpeedMove(uint16 var,const ArgumentsArray & args)499 void Mechanical::o_fortressSimulationSpeedMove(uint16 var, const ArgumentsArray &args) {
500 const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
501 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
502
503 // Make the handle follow the mouse
504 int16 maxStep = lever->getNumFrames() - 1;
505 Common::Rect rect = lever->getRect();
506 int16 step = ((rect.bottom - mouse.y) * lever->getNumFrames()) / rect.height();
507 step = CLIP<int16>(step, 0, maxStep);
508
509 _fortressSimulationSpeed = step;
510
511 // Draw current frame
512 lever->drawFrame(step);
513 }
514
o_fortressSimulationSpeedStop(uint16 var,const ArgumentsArray & args)515 void Mechanical::o_fortressSimulationSpeedStop(uint16 var, const ArgumentsArray &args) {
516 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
517
518 // Release lever
519 for (int i = _fortressSimulationSpeed; i >= 0; i--) {
520 lever->drawFrame(i);
521 _vm->doFrame();
522 }
523
524 _fortressSimulationSpeed = 0;
525
526 _vm->refreshCursor();
527 }
528
o_fortressSimulationBrakeStart(uint16 var,const ArgumentsArray & args)529 void Mechanical::o_fortressSimulationBrakeStart(uint16 var, const ArgumentsArray &args) {
530 _vm->_cursor->setCursor(700);
531
532 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
533 lever->drawFrame(_fortressSimulationBrake);
534 }
535
o_fortressSimulationBrakeMove(uint16 var,const ArgumentsArray & args)536 void Mechanical::o_fortressSimulationBrakeMove(uint16 var, const ArgumentsArray &args) {
537 const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
538 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
539
540 // Make the handle follow the mouse
541 int16 maxStep = lever->getNumFrames() - 1;
542 Common::Rect rect = lever->getRect();
543 int16 step = ((rect.bottom - mouse.y) * lever->getNumFrames()) / rect.height();
544 step = CLIP<int16>(step, 0, maxStep);
545
546 _fortressSimulationBrake = step;
547
548 // Draw current frame
549 lever->drawFrame(step);
550 }
551
o_fortressSimulationBrakeStop(uint16 var,const ArgumentsArray & args)552 void Mechanical::o_fortressSimulationBrakeStop(uint16 var, const ArgumentsArray &args) {
553 MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
554 lever->drawFrame(_fortressSimulationBrake);
555
556 _vm->refreshCursor();
557 }
558
o_elevatorWindowMovie(uint16 var,const ArgumentsArray & args)559 void Mechanical::o_elevatorWindowMovie(uint16 var, const ArgumentsArray &args) {
560 uint16 startTime = args[0];
561 uint16 endTime = args[1];
562
563 VideoEntryPtr window = _vm->playMovie("ewindow", kMechanicalStack);
564 window->moveTo(253, 0);
565 window->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600));
566
567 _vm->waitUntilMovieEnds(window);
568 }
569
o_elevatorGoMiddle(uint16 var,const ArgumentsArray & args)570 void Mechanical::o_elevatorGoMiddle(uint16 var, const ArgumentsArray &args) {
571 _elevatorTooLate = false;
572 _elevatorTopCounter = 5;
573 _elevatorGoingMiddle = true;
574 _elevatorInCabin = true;
575 _elevatorNextTime = _vm->getTotalPlayTime() + 1000;
576 }
577
elevatorGoMiddle_run()578 void Mechanical::elevatorGoMiddle_run() {
579 uint32 time = _vm->getTotalPlayTime();
580 if (_elevatorNextTime < time) {
581 _elevatorNextTime = time + 1000;
582 _elevatorTopCounter--;
583
584 if (_elevatorTopCounter > 0) {
585 // Draw button pressed
586 if (_elevatorInCabin) {
587 _vm->_gfx->copyImageSectionToScreen(6332, Common::Rect(0, 35, 51, 63), Common::Rect(10, 137, 61, 165));
588 }
589
590 // Blip
591 _vm->playSoundBlocking(14120);
592
593 // Restore button
594 if (_elevatorInCabin) {
595 _vm->_gfx->copyBackBufferToScreen(Common::Rect(10, 137, 61, 165));
596 }
597 } else {
598 _elevatorTooLate = true;
599 _elevatorGoingMiddle = false;
600
601 if (_elevatorInCabin) {
602
603 // Elevator going to middle animation
604 _vm->_cursor->hideCursor();
605 _vm->playSoundBlocking(11120);
606 _vm->_gfx->copyImageToBackBuffer(6118, Common::Rect(544, 333));
607 _vm->_sound->playEffect(12120);
608 _vm->_gfx->runTransition(kTransitionSlideToLeft, Common::Rect(177, 0, 370, 333), 25, 0);
609 _vm->playSoundBlocking(13120);
610 _vm->_sound->playEffect(8120);
611 _vm->_gfx->copyImageToBackBuffer(6327, Common::Rect(544, 333));
612 _vm->wait(500);
613 _vm->_sound->playEffect(9120);
614 static uint16 moviePos[2] = { 3540, 5380 };
615 o_elevatorWindowMovie(0, ArgumentsArray(moviePos, ARRAYSIZE(moviePos)));
616 _vm->_gfx->copyBackBufferToScreen(Common::Rect(544, 333));
617 _vm->_sound->playEffect(10120);
618 _vm->_cursor->showCursor();
619
620 _elevatorPosition = 1;
621
622 _vm->changeToCard(6327, kTransitionRightToLeft);
623 }
624 }
625 }
626 }
627
o_elevatorTopMovie(uint16 var,const ArgumentsArray & args)628 void Mechanical::o_elevatorTopMovie(uint16 var, const ArgumentsArray &args) {
629 uint16 startTime = args[0];
630 uint16 endTime = args[1];
631
632 VideoEntryPtr window = _vm->playMovie("hcelev", kMechanicalStack);
633 window->moveTo(206, 38);
634 window->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600));
635
636 _vm->waitUntilMovieEnds(window);
637 }
638
o_fortressRotationSetPosition(uint16 var,const ArgumentsArray & args)639 void Mechanical::o_fortressRotationSetPosition(uint16 var, const ArgumentsArray &args) {
640 // The fortress direction is already set in fortressRotation_run() so we don't do it here
641 // Stop the gears video so that it does not play while the elevator is going up
642 _fortressRotationGears->getVideo()->stop();
643 }
644
o_mystStaircaseMovie(uint16 var,const ArgumentsArray & args)645 void Mechanical::o_mystStaircaseMovie(uint16 var, const ArgumentsArray &args) {
646 _vm->playMovieBlocking("sstairs", kMechanicalStack, 199, 108);
647 }
648
o_elevatorWaitTimeout(uint16 var,const ArgumentsArray & args)649 void Mechanical::o_elevatorWaitTimeout(uint16 var, const ArgumentsArray &args) {
650 // Wait while the elevator times out
651 while (_elevatorGoingMiddle) {
652 runPersistentScripts();
653 _vm->doFrame();
654 }
655 }
656
o_crystalEnterYellow(uint16 var,const ArgumentsArray & args)657 void Mechanical::o_crystalEnterYellow(uint16 var, const ArgumentsArray &args) {
658 _crystalLit = 3;
659 _vm->getCard()->redrawArea(20);
660 }
661
o_crystalEnterGreen(uint16 var,const ArgumentsArray & args)662 void Mechanical::o_crystalEnterGreen(uint16 var, const ArgumentsArray &args) {
663 _crystalLit = 1;
664 _vm->getCard()->redrawArea(21);
665 }
666
o_crystalEnterRed(uint16 var,const ArgumentsArray & args)667 void Mechanical::o_crystalEnterRed(uint16 var, const ArgumentsArray &args) {
668 _crystalLit = 2;
669 _vm->getCard()->redrawArea(22);
670 }
671
o_crystalLeaveYellow(uint16 var,const ArgumentsArray & args)672 void Mechanical::o_crystalLeaveYellow(uint16 var, const ArgumentsArray &args) {
673 _crystalLit = 0;
674 _vm->getCard()->redrawArea(20);
675 }
676
o_crystalLeaveGreen(uint16 var,const ArgumentsArray & args)677 void Mechanical::o_crystalLeaveGreen(uint16 var, const ArgumentsArray &args) {
678 _crystalLit = 0;
679 _vm->getCard()->redrawArea(21);
680 }
681
o_crystalLeaveRed(uint16 var,const ArgumentsArray & args)682 void Mechanical::o_crystalLeaveRed(uint16 var, const ArgumentsArray &args) {
683 _crystalLit = 0;
684 _vm->getCard()->redrawArea(22);
685 }
686
o_throne_init(uint16 var,const ArgumentsArray & args)687 void Mechanical::o_throne_init(uint16 var, const ArgumentsArray &args) {
688 // Used on Card 6238 (Sirrus' Throne) and Card 6027 (Achenar's Throne)
689 getInvokingResource<MystArea>()->setEnabled(getVar(var));
690 }
691
o_fortressStaircase_init(uint16 var,const ArgumentsArray & args)692 void Mechanical::o_fortressStaircase_init(uint16 var, const ArgumentsArray &args) {
693 _vm->getCard()->getResource<MystArea>(args[0])->setEnabled(!_state.staircaseState);
694 _vm->getCard()->getResource<MystArea>(args[1])->setEnabled(!_state.staircaseState);
695 _vm->getCard()->getResource<MystArea>(args[2])->setEnabled(_state.staircaseState);
696 }
697
birdSing_run()698 void Mechanical::birdSing_run() {
699 // Used for Card 6220 (Sirrus' Mechanical Bird)
700 uint32 time = _vm->getTotalPlayTime();
701 if (_birdSingEndTime < time) {
702 _bird->pauseMovie(true);
703 _vm->_sound->stopEffect();
704 _birdSinging = false;
705 }
706 }
707
o_bird_init(uint16 var,const ArgumentsArray & args)708 void Mechanical::o_bird_init(uint16 var, const ArgumentsArray &args) {
709 _birdSinging = false;
710 _birdSingEndTime = 0;
711 _bird = getInvokingResource<MystAreaVideo>();
712 }
713
o_snakeBox_init(uint16 var,const ArgumentsArray & args)714 void Mechanical::o_snakeBox_init(uint16 var, const ArgumentsArray &args) {
715 _snakeBox = getInvokingResource<MystAreaVideo>();
716 }
717
elevatorRotation_run()718 void Mechanical::elevatorRotation_run() {
719 _vm->getCard()->redrawArea(12);
720
721 _elevatorRotationGearPosition += _elevatorRotationSpeed;
722
723 if (_elevatorRotationGearPosition > 12) {
724 uint16 position = (uint16)_elevatorRotationGearPosition;
725 _elevatorRotationGearPosition = _elevatorRotationGearPosition - position + position % 6;
726
727 _state.elevatorRotation = (_state.elevatorRotation + 1) % 10;
728
729 _vm->_sound->playEffect(_elevatorRotationSoundId);
730 _vm->getCard()->redrawArea(11);
731 _vm->wait(100);
732 }
733 }
734
o_elevatorRotation_init(uint16 var,const ArgumentsArray & args)735 void Mechanical::o_elevatorRotation_init(uint16 var, const ArgumentsArray &args) {
736 _elevatorRotationSoundId = args[0];
737 _elevatorRotationGearPosition = 0;
738 _elevatorRotationLeverMoving = false;
739 }
740
fortressRotation_run()741 void Mechanical::fortressRotation_run() {
742 VideoEntryPtr gears = _fortressRotationGears->getVideo();
743
744 double oldRate = gears->getRate().toDouble();
745 uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames();
746
747 // Myst ME short movie workaround, explained in o_fortressRotation_init
748 if (_fortressRotationShortMovieWorkaround) {
749 // Detect if we just looped
750 if (ABS<int32>(_fortressRotationShortMovieLast - 3680) < 50
751 && ABS<int32>(moviePosition) < 50) {
752 _fortressRotationShortMovieCount++;
753 }
754
755 _fortressRotationShortMovieLast = moviePosition;
756
757 // Simulate longer movie
758 moviePosition += 3600 * _fortressRotationShortMovieCount;
759 }
760
761 int32 positionInQuarter = 900 - (moviePosition + 900) % 1800;
762
763 // Are the gears moving?
764 if (oldRate >= 0.1 || ABS<int32>(positionInQuarter) >= 30 || _fortressRotationBrake) {
765
766 double newRate = oldRate;
767 if (_fortressRotationBrake && (double)_fortressRotationBrake * 0.2 > oldRate) {
768 newRate += 0.1;
769 }
770
771 // Don't let the gears get stuck between two fortress positions
772 if (ABS<double>(oldRate) <= 0.05) {
773 if (oldRate <= 0.0) {
774 newRate += oldRate;
775 } else {
776 newRate -= oldRate;
777 }
778 } else {
779 if (oldRate <= 0.0) {
780 newRate += 0.05;
781 } else {
782 newRate -= 0.05;
783 }
784 }
785
786 // Adjust speed accordingly to acceleration lever
787 newRate += (double) (positionInQuarter / 1500.0)
788 * (double) (9 - _fortressRotationSpeed) / 9.0;
789
790 newRate = CLIP<double>(newRate, -2.5, 2.5);
791
792 gears->setRate(Common::Rational((int)(newRate * 1000.0), 1000));
793
794 _gearsWereRunning = true;
795 } else if (_gearsWereRunning) {
796 // The fortress has stopped. Set its new position
797 _fortressDirection = (moviePosition + 900) / 1800 % 4;
798
799 gears->setRate(0);
800
801 if (!_fortressRotationShortMovieWorkaround) {
802 gears->seek(Audio::Timestamp(0, 1800 * _fortressDirection, 600));
803 } else {
804 gears->seek(Audio::Timestamp(0, 1800 * (_fortressDirection % 2), 600));
805 }
806
807 _vm->playSoundBlocking(_fortressRotationSounds[_fortressDirection]);
808
809 _gearsWereRunning = false;
810 }
811 }
812
o_fortressRotation_init(uint16 var,const ArgumentsArray & args)813 void Mechanical::o_fortressRotation_init(uint16 var, const ArgumentsArray &args) {
814 _fortressRotationGears = getInvokingResource<MystAreaVideo>();
815
816 VideoEntryPtr gears = _fortressRotationGears->playMovie();
817 gears->setLooping(true);
818
819 // WORKAROUND for the tower rotation bug in Myst ME.
820 // The original engine only allowed to visit two out of the three small islands,
821 // preventing the game from being fully completable.
822 // The fortress rotation is computed from the current position in the movie
823 // hcgears.mov. The version of this movie that shipped with the ME edition is
824 // too short to allow to visit all the islands.
825 // ScummVM simulates a longer movie by counting the number of times the movie
826 // looped and adding that time to the current movie position.
827 // Hence allowing the fortress position to be properly computed.
828 uint32 movieDuration = gears->getDuration().convertToFramerate(600).totalNumberOfFrames();
829 _fortressRotationShortMovieWorkaround = movieDuration == 3680;
830
831 if (!_fortressRotationShortMovieWorkaround) {
832 gears->seek(Audio::Timestamp(0, 1800 * _fortressDirection, 600));
833 } else {
834 _fortressRotationShortMovieLast = 1800 * (_fortressDirection % 2);
835 _fortressRotationShortMovieCount = _fortressDirection >= 2 ? 1 : 0;
836 gears->seek(Audio::Timestamp(0, _fortressRotationShortMovieLast, 600));
837 }
838
839 gears->setRate(0);
840
841 _fortressRotationSounds[0] = args[0];
842 _fortressRotationSounds[1] = args[1];
843 _fortressRotationSounds[2] = args[2];
844 _fortressRotationSounds[3] = args[3];
845
846 _fortressRotationBrake = 0;
847
848 _fortressRotationRunning = true;
849 _gearsWereRunning = false;
850 }
851
fortressSimulation_run()852 void Mechanical::fortressSimulation_run() {
853 if (_fortressSimulationInit) {
854 // Init sequence
855 _vm->_sound->playBackground(_fortressSimulationStartSound1, 65535);
856 _vm->wait(5000, true);
857
858 VideoEntryPtr startup = _fortressSimulationStartup->playMovie();
859 _vm->playSoundBlocking(_fortressSimulationStartSound2);
860 _vm->_sound->playBackground(_fortressSimulationStartSound1, 65535);
861 _vm->waitUntilMovieEnds(startup);
862 _vm->_sound->stopBackground();
863 _vm->_sound->playEffect(_fortressSimulationStartSound2);
864
865
866 Common::Rect src = Common::Rect(0, 0, 176, 176);
867 Common::Rect dst = Common::Rect(187, 3, 363, 179);
868 _vm->_gfx->copyImageSectionToBackBuffer(6046, src, dst);
869 _vm->_gfx->copyBackBufferToScreen(dst);
870
871 _fortressSimulationStartup->pauseMovie(true);
872 VideoEntryPtr holo = _fortressSimulationHolo->playMovie();
873 holo->setLooping(true);
874 holo->setRate(0);
875
876 // HACK: Support negative rates with edit lists
877 _fortressSimulationHoloRate = 0;
878 // END HACK
879
880 _vm->_cursor->showCursor();
881
882 _fortressSimulationInit = false;
883 } else {
884 VideoEntryPtr holo = _fortressSimulationHolo->getVideo();
885
886 double oldRate = holo->getRate().toDouble();
887
888 // HACK: Support negative rates with edit lists
889 oldRate = _fortressSimulationHoloRate;
890 // END HACK
891
892 uint32 moviePosition = Audio::Timestamp(holo->getTime(), 600).totalNumberOfFrames();
893
894 int32 positionInQuarter = 900 - (moviePosition + 900) % 1800;
895
896 // Are the gears moving?
897 if (oldRate >= 0.1 || ABS<int32>(positionInQuarter) >= 30 || _fortressSimulationBrake) {
898
899 double newRate = oldRate;
900 if (_fortressSimulationBrake && (double)_fortressSimulationBrake * 0.2 > oldRate) {
901 newRate += 0.1;
902 }
903
904 // Don't let the gears get stuck between two fortress positions
905 if (ABS<double>(oldRate) <= 0.05) {
906 if (oldRate <= 0.0) {
907 newRate += oldRate;
908 } else {
909 newRate -= oldRate;
910 }
911 } else {
912 if (oldRate <= 0.0) {
913 newRate += 0.05;
914 } else {
915 newRate -= 0.05;
916 }
917 }
918
919 // Adjust speed accordingly to acceleration lever
920 newRate += (double) (positionInQuarter / 1500.0)
921 * (double) (9 - _fortressSimulationSpeed) / 9.0;
922
923 newRate = CLIP<double>(newRate, -2.5, 2.5);
924
925 // HACK: Support negative rates with edit lists
926
927 // Our current QuickTime implementation does not support negative
928 // playback rates for movies using edit lists.
929 // The fortress rotation simulator movie this code handles is the
930 // only movie in the game requiring that feature.
931
932 // This hack approximates the next frame to display when the rate
933 // is negative, and seeks to it. It's not intended to be precise.
934
935 _fortressSimulationHoloRate = newRate;
936
937 if (_fortressSimulationHoloRate < 0) {
938 double newMoviePosition = moviePosition + _fortressSimulationHoloRate * 10;
939 holo->setRate(0);
940 holo->seek(Audio::Timestamp(0, (uint)newMoviePosition, 600));
941 } else {
942 holo->setRate(Common::Rational((int)(newRate * 1000.0), 1000));
943 }
944 // END HACK
945
946 _gearsWereRunning = true;
947 } else if (_gearsWereRunning) {
948 // The fortress has stopped. Set its new position
949 uint16 simulationPosition = (moviePosition + 900) / 1800 % 4;
950
951 holo->setRate(0);
952
953 // HACK: Support negative rates with edit lists
954 _fortressSimulationHoloRate = 0;
955 // END HACK
956
957 holo->seek(Audio::Timestamp(0, 1800 * simulationPosition, 600));
958 _vm->playSoundBlocking( _fortressRotationSounds[simulationPosition]);
959
960 _gearsWereRunning = false;
961 }
962 }
963 }
964
o_fortressSimulation_init(uint16 var,const ArgumentsArray & args)965 void Mechanical::o_fortressSimulation_init(uint16 var, const ArgumentsArray &args) {
966 _fortressSimulationHolo = getInvokingResource<MystAreaVideo>();
967
968 _fortressSimulationStartSound1 = args[0];
969 _fortressSimulationStartSound2 = args[1];
970
971 _fortressRotationSounds[0] = args[2];
972 _fortressRotationSounds[1] = args[3];
973 _fortressRotationSounds[2] = args[4];
974 _fortressRotationSounds[3] = args[5];
975
976 _fortressSimulationBrake = 0;
977
978 _fortressSimulationRunning = true;
979 _gearsWereRunning = false;
980 _fortressSimulationInit = true;
981
982 _vm->_cursor->hideCursor();
983 }
984
o_fortressSimulationStartup_init(uint16 var,const ArgumentsArray & args)985 void Mechanical::o_fortressSimulationStartup_init(uint16 var, const ArgumentsArray &args) {
986 _fortressSimulationStartup = getInvokingResource<MystAreaVideo>();
987 }
988
989 } // End of namespace MystStacks
990 } // End of namespace Mohawk
991