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 "neverhood/gamemodule.h"
24 #include "neverhood/navigationscene.h"
25 #include "neverhood/modules/module3000.h"
26 #include "neverhood/modules/module3000_sprites.h"
27
28 namespace Neverhood {
29
30 static const uint32 kModule3000SoundList[] = {
31 0x92025040,
32 0x90035066,
33 0x90815450,
34 0x99801500,
35 0x90E14440,
36 0x16805048,
37 0x90F0D1C3,
38 0
39 };
40
Module3000(NeverhoodEngine * vm,Module * parentModule,int which)41 Module3000::Module3000(NeverhoodEngine *vm, Module *parentModule, int which)
42 : Module(vm, parentModule), _waterfallSoundVolume(0) {
43
44 _vm->_soundMan->addSoundList(0x81293110, kModule3000SoundList);
45 _vm->_soundMan->setSoundListParams(kModule3000SoundList, true, 50, 600, 5, 150);
46 _vm->_soundMan->setSoundParams(0x90F0D1C3, false, 20000, 30000, 20000, 30000);
47 _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0);
48 _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0);
49 _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0);
50
51 _isWaterfallRunning = getGlobalVar(V_WALL_BROKEN) != 1;
52
53 if (_isWaterfallRunning) {
54 _vm->_soundMan->setSoundVolume(0x90F0D1C3, 0);
55 _vm->_soundMan->playSoundLooping(0x90F0D1C3);
56 }
57
58 if (which < 0) {
59 createScene(_vm->gameState().sceneNum, -1);
60 } else if (which == 0) {
61 createScene(1, 0);
62 } else if (which == 1) {
63 createScene(4, 2);
64 } else if (which == 2) {
65 createScene(4, 1);
66 } else if (which == 3) {
67 createScene(5, 1);
68 }
69
70 }
71
~Module3000()72 Module3000::~Module3000() {
73 _vm->_soundMan->deleteGroup(0x81293110);
74 }
75
createScene(int sceneNum,int which)76 void Module3000::createScene(int sceneNum, int which) {
77 static const byte kNavigationTypes05[] = {2, 0};
78 static const byte kNavigationTypes06[] = {5};
79 debug(1, "Module3000::createScene(%d, %d)", sceneNum, which);
80 _vm->gameState().sceneNum = sceneNum;
81 switch (_vm->gameState().sceneNum) {
82 case 1:
83 if (!getGlobalVar(V_BOLT_DOOR_OPEN)) {
84 createNavigationScene(0x004B7C80, which);
85 } else if (getGlobalVar(V_WALL_BROKEN)) {
86 createNavigationScene(0x004B7CE0, which);
87 } else {
88 createNavigationScene(0x004B7CB0, which);
89 }
90 break;
91 case 2:
92 _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0);
93 if (_isWaterfallRunning) {
94 _waterfallSoundVolume = 90;
95 _vm->_soundMan->setSoundVolume(0x90F0D1C3, 90);
96 }
97 if (getGlobalVar(V_WALL_BROKEN)) {
98 createNavigationScene(0x004B7D58, which);
99 } else {
100 createNavigationScene(0x004B7D10, which);
101 }
102 break;
103 case 3:
104 if (getGlobalVar(V_STAIRS_DOWN))
105 createNavigationScene(0x004B7E60, which);
106 else if (getGlobalVar(V_WALL_BROKEN))
107 createNavigationScene(0x004B7DA0, which);
108 else
109 createNavigationScene(0x004B7E00, which);
110 break;
111 case 4:
112 if (getGlobalVar(V_STAIRS_DOWN))
113 createNavigationScene(0x004B7F20, which);
114 else
115 createNavigationScene(0x004B7EC0, which);
116 break;
117 case 5:
118 createNavigationScene(0x004B7F80, which, kNavigationTypes05);
119 break;
120 case 6:
121 createNavigationScene(0x004B7FB0, which, kNavigationTypes06);
122 break;
123 case 7:
124 _vm->_soundMan->setSoundListParams(kModule3000SoundList, false, 0, 0, 0, 0);
125 if (!getSubVar(VA_IS_PUZZLE_INIT, 0x089809C2)) {
126 setSubVar(VA_IS_PUZZLE_INIT, 0x089809C2, 1);
127 createSmackerScene(0x90022001, true, true, false);
128 } else
129 createSmackerScene(0x98022001, true, true, false);
130 break;
131 case 8:
132 _childObject = new Scene3009(_vm, this, which);
133 break;
134 case 9:
135 _childObject = new Scene3010(_vm, this, 0);
136 break;
137 case 10:
138 _childObject = new Scene3011(_vm, this, 0);
139 break;
140 case 11:
141 _vm->_soundMan->setSoundListParams(kModule3000SoundList, false, 0, 0, 0, 0);
142 if (!getSubVar(VA_IS_PUZZLE_INIT, 0x10130993)) {
143 setSubVar(VA_IS_PUZZLE_INIT, 0x10130993, 1);
144 createSmackerScene(0x31093019, true, true, false);
145 } else
146 createSmackerScene(0x20093019, true, true, false);
147 break;
148 case 12:
149 _childObject = new Scene3010(_vm, this, 1);
150 break;
151 // NOTE: Newly introduced sceneNums
152 case 1001:
153 if (!getGlobalVar(V_BOLT_DOOR_OPEN))
154 if (getGlobalVar(V_WALL_BROKEN))
155 createSmackerScene(0x00940021, true, true, false);
156 else
157 createSmackerScene(0x01140021, true, true, false);
158 else
159 if (getGlobalVar(V_WALL_BROKEN))
160 createSmackerScene(0x001011B1, true, true, false);
161 else
162 createSmackerScene(0x001021B1, true, true, false);
163 setGlobalVar(V_BOLT_DOOR_OPEN, getGlobalVar(V_BOLT_DOOR_OPEN) ? 0 : 1);
164 break;
165 case 1006:
166 createSmackerScene(0x080810C5, true, true, false);
167 break;
168 case 1008:
169 createSmackerScene(getGlobalVar(V_CANNON_SMACKER_NAME), true, true, false);
170 break;
171 }
172 SetUpdateHandler(&Module3000::updateScene);
173 _childObject->handleUpdate();
174 }
175
updateScene()176 void Module3000::updateScene() {
177 if (!updateChild()) {
178 switch (_vm->gameState().sceneNum) {
179 case 1:
180 if (!getGlobalVar(V_BOLT_DOOR_OPEN)) {
181 if (_moduleResult == 0)
182 createScene(9, -1);
183 else if (_moduleResult == 1)
184 leaveModule(0);
185 } else {
186 if (_moduleResult == 0)
187 if (_navigationAreaType == 2)
188 createScene(2, 0);
189 else
190 createScene(1001, -1);
191 else if (_moduleResult == 1)
192 leaveModule(0);
193 }
194 break;
195 case 2:
196 _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0);
197 if (_isWaterfallRunning) {
198 _waterfallSoundVolume = 0;
199 _vm->_soundMan->setSoundVolume(0x90F0D1C3, 0);
200 }
201 if (_moduleResult == 0) {
202 createScene(3, 0);
203 } else if (_moduleResult == 1) {
204 setGlobalVar(V_BOLT_DOOR_OPEN, 0);
205 createScene(1, 1);
206 }
207 break;
208 case 3:
209 if (_moduleResult == 1)
210 createScene(4, 0);
211 else if (_moduleResult == 3)
212 createScene(10, -1);
213 else if (getGlobalVar(V_STAIRS_DOWN))
214 createScene(5, 0);
215 else
216 createScene(2, 1);
217 break;
218 case 4:
219 if (_moduleResult == 0)
220 leaveModule(1);
221 else if (_moduleResult == 1)
222 createScene(7, -1);
223 else if (_moduleResult == 2)
224 createScene(3, 3);
225 break;
226 case 5:
227 if (_moduleResult == 0)
228 createScene(6, 0);
229 else if (_moduleResult == 1)
230 createScene(3, 0);
231 break;
232 case 6:
233 if (_navigationAreaType == 4)
234 createScene(11, -1);
235 else
236 createScene(1006, -1);
237 break;
238 case 7:
239 createScene(8, -1);
240 break;
241 case 8:
242 _isWaterfallRunning = getGlobalVar(V_WALL_BROKEN) != 1;
243 if (_moduleResult != 1) {
244 _vm->_soundMan->setSoundListParams(kModule3000SoundList, true, 0, 0, 0, 0);
245 createScene(4, 1);
246 } else if (getGlobalVar(V_CANNON_SMACKER_NAME)) {
247 createScene(1008, -1);
248 } else {
249 _vm->_soundMan->setSoundListParams(kModule3000SoundList, true, 0, 0, 0, 0);
250 createScene(4, 1);
251 }
252 break;
253 case 9:
254 if (_moduleResult == 0 || _moduleResult == 2)
255 createScene(1, 0);
256 else if (_moduleResult == 1)
257 createScene(1001, -1);
258 break;
259 case 10:
260 createScene(3, 3);
261 break;
262 case 11:
263 leaveModule(3);
264 break;
265 case 12:
266 createScene(1, 0);
267 break;
268 case 1001:
269 if (getGlobalVar(V_BOLT_DOOR_OPEN))
270 createScene(1, 0);
271 else
272 createScene(12, -1);
273 break;
274 case 1006:
275 createScene(5, 0);
276 break;
277 case 1008:
278 createScene(8, -1);
279 break;
280 }
281 } else {
282 switch (_vm->gameState().sceneNum) {
283 case 1:
284 if (navigationScene()->isWalkingForward()) {
285 uint32 frameNumber = navigationScene()->getFrameNumber();
286 int navigationIndex = navigationScene()->getNavigationIndex();
287 if (navigationIndex == 1) {
288 if (frameNumber == 0) {
289 _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0);
290 _vm->_soundMan->setSoundVolume(0x48498E46, 70);
291 _vm->_soundMan->setSoundVolume(0x50399F64, 70);
292 } else if (frameNumber == 100) {
293 _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0);
294 }
295 } else if (navigationIndex == 0) {
296 if (frameNumber == 0) {
297 _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0);
298 _vm->_soundMan->setSoundVolume(0x48498E46, 70);
299 _vm->_soundMan->setSoundVolume(0x50399F64, 70);
300 } else if (frameNumber == 10) {
301 _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0);
302 }
303 if (_isWaterfallRunning && _waterfallSoundVolume < 90 && frameNumber % 2) {
304 if (frameNumber == 0)
305 _waterfallSoundVolume = 40;
306 else
307 _waterfallSoundVolume++;
308 _vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume);
309 }
310 }
311 }
312 break;
313 case 2:
314 if (navigationScene()->isWalkingForward()) {
315 uint32 frameNumber = navigationScene()->getFrameNumber();
316 int navigationIndex = navigationScene()->getNavigationIndex();
317 if (_isWaterfallRunning && _waterfallSoundVolume > 1 && frameNumber % 2) {
318 _waterfallSoundVolume--;
319 _vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume);
320 }
321 if (navigationIndex == 0) {
322 if (frameNumber == 35) {
323 _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0);
324 }
325 } else if (navigationIndex == 1) {
326 if (frameNumber == 55) {
327 _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0);
328 _vm->_soundMan->setSoundVolume(0x48498E46, 70);
329 _vm->_soundMan->setSoundVolume(0x50399F64, 70);
330 }
331 }
332 }
333 break;
334 case 3:
335 if (navigationScene()->isWalkingForward()) {
336 uint32 frameNumber = navigationScene()->getFrameNumber();
337 int navigationIndex = navigationScene()->getNavigationIndex();
338 if (navigationIndex == 2) {
339 if (frameNumber == 40) {
340 _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0);
341 }
342 if (_isWaterfallRunning && _waterfallSoundVolume < 90 && frameNumber % 2) {
343 if (frameNumber == 0)
344 _waterfallSoundVolume = 40;
345 else
346 _waterfallSoundVolume++;
347 _vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume);
348 }
349 }
350 }
351 break;
352 case 5:
353 if (navigationScene()->isWalkingForward() && navigationScene()->getNavigationIndex() == 0) {
354 _vm->_soundMan->setTwoSoundsPlayFlag(false);
355 }
356 break;
357 }
358 }
359 }
360
361 // Scene3009
362
363 enum {
364 kCTSNull = 0,
365 kCTSBreakWall = 1,
366 kCTSWall = 2,
367 kCTSEmptyness = 3,
368 kCTSFireRobotNoTarget = 4,
369 kCTSFireRobotIsTarget = 5,
370 kCTSFireNoRobot = 6,
371 kCTSRaiseCannon = 7,
372 kCTSRightRobotNoTarget = 8,
373 kCTSRightRobotIsTarget = 9,
374 kCTSRightNoRobot = 10,
375 kCTSLeftRobotNoTarget = 11,
376 kCTSLeftRobotIsTarget = 12,
377 kCTSLeftNoRobot = 13,
378 kCTSLowerCannon = 14,
379 kCTSCount = 14
380 };
381
382 static const uint32 kScene3009CannonScopeVideos[] = {
383 0x1010000D,
384 0x340A0049,
385 0x340A0049,
386 0x0282081D,
387 0x0082080D,
388 0x0882080D,
389 0x0882080D,
390 0x0282081D,
391 0x004B000B,
392 0x014B000B,
393 0x044B000B,
394 0x0282081D,
395 0x0282081D,
396 0x0282081D,
397 0x340A0049
398 };
399
400 static const uint32 kScene3009CannonActionVideos[] = {
401 0x00000000,
402 0x8004001B, // 1 Fire cannon at wall, it breaks (lowered)
403 0x0004001A, // 2 Fire cannon at wall, nothing happens (lowered)
404 0x1048404B, // 3 Fire cannon at emptyness (raised)
405 0x50200109, // 4 Fire cannon, robot missed (raised)
406 0x12032109, // 5 Fire cannon, robot hit (raised)
407 0x10201109, // 6 Fire cannon, no robot (raised)
408 0x000A2030, // 7 Raise the cannon
409 0x000A0028, // 8
410 0x000A0028, // 9
411 0x000A0028, // 10
412 0x040A1069, // 11
413 0x040A1069, // 12
414 0x040A1069, // 13
415 0x240A1101 // 14 Lower the cannon
416 };
417
Scene3009(NeverhoodEngine * vm,Module * parentModule,int which)418 Scene3009::Scene3009(NeverhoodEngine *vm, Module *parentModule, int which)
419 : Scene(vm, parentModule), _keepVideo(false), _moveCannonLeftFirst(false),
420 _isTurning(false), _lockSymbolsPart1Countdown(1), _lockSymbolsPart2Countdown(1) {
421
422 _cannonTargetStatus = getGlobalVar(V_CANNON_TARGET_STATUS);
423
424 _vm->gameModule()->initCannonSymbolsPuzzle();
425
426 setGlobalVar(V_CANNON_SMACKER_NAME, 0);
427
428 _vm->_screen->clear();
429
430 setBackground(0xD000420C);
431 setPalette(0xD000420C);
432 insertPuzzleMouse(0x04208D08, 20, 620);
433
434 _ssFireCannonButton = insertSprite<SsScene3009FireCannonButton>(this);
435 addCollisionSprite(_ssFireCannonButton);
436
437 _asVerticalIndicator = insertSprite<AsScene3009VerticalIndicator>(this, _cannonTargetStatus);
438 addCollisionSprite(_asVerticalIndicator);
439
440 _asHorizontalIndicator = insertSprite<AsScene3009HorizontalIndicator>(this, _cannonTargetStatus);
441 addCollisionSprite(_asHorizontalIndicator);
442
443 if (_cannonTargetStatus != kCTSNull && _cannonTargetStatus != kCTSRightRobotNoTarget && _cannonTargetStatus != kCTSRightRobotIsTarget && _cannonTargetStatus != kCTSRightNoRobot) {
444 _keepVideo = true;
445 } else {
446 _keepVideo = false;
447 if (_cannonTargetStatus != kCTSNull) {
448 _asHorizontalIndicator->stMoveRight();
449 _isTurning = true;
450 }
451 }
452
453 _cannonSmackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, kScene3009CannonScopeVideos[_cannonTargetStatus], false, _keepVideo));
454 _cannonSmackerPlayer->setDrawPos(89, 37);
455 _palette->usePalette(); // Use it again since the SmackerPlayer overrides the usage
456
457 insertStaticSprite(0x8540252C, 400);
458
459 for (int i = 0; i < 2; i++) {
460 _ssSymbolEdges[i] = insertSprite<SsScene3009SymbolEdges>(i);
461 _ssTargetLines[i] = insertSprite<SsScene3009TargetLine>(i);
462 }
463
464 for (int symbolPosition = 0; symbolPosition < 6; symbolPosition++) {
465 _asSymbols[symbolPosition] = insertSprite<AsScene3009Symbol>(this, symbolPosition);
466 if (symbolPosition < 3)
467 _correctSymbols[symbolPosition] = getSubVar(VA_GOOD_CANNON_SYMBOLS_1, symbolPosition);
468 else
469 _correctSymbols[symbolPosition] = getSubVar(VA_GOOD_CANNON_SYMBOLS_2, symbolPosition - 3);
470 }
471
472 SetMessageHandler(&Scene3009::handleMessage);
473 SetUpdateHandler(&Scene3009::update);
474 }
475
~Scene3009()476 Scene3009::~Scene3009() {
477 }
478
openSmacker(uint32 fileHash,bool keepLastFrame)479 void Scene3009::openSmacker(uint32 fileHash, bool keepLastFrame) {
480 _cannonSmackerPlayer->open(fileHash, keepLastFrame);
481 //_vm->_screen->setSmackerDecoder(_cannonSmackerPlayer->getSmackerDecoder());
482 _palette->usePalette();
483 }
484
update()485 void Scene3009::update() {
486 Scene::update();
487
488 if (!_keepVideo && _cannonSmackerPlayer->isDone() && _cannonTargetStatus <= kCTSCount) {
489 switch (_cannonTargetStatus) {
490 case kCTSNull:
491 case kCTSLowerCannon:
492 openSmacker(0x340A0049, true);
493 _keepVideo = true;
494 break;
495 case kCTSRightRobotNoTarget:
496 openSmacker(0x0082080D, true);
497 _keepVideo = true;
498 _isTurning = false;
499 break;
500 case kCTSRightRobotIsTarget:
501 openSmacker(0x0282080D, true);
502 _keepVideo = true;
503 _isTurning = false;
504 break;
505 case kCTSRightNoRobot:
506 openSmacker(0x0882080D, true);
507 _keepVideo = true;
508 _isTurning = false;
509 break;
510 case kCTSLeftRobotNoTarget:
511 case kCTSLeftRobotIsTarget:
512 case kCTSLeftNoRobot:
513 if (_moveCannonLeftFirst) {
514 if (_cannonTargetStatus == kCTSLeftRobotNoTarget)
515 openSmacker(0x110A000F, false);
516 else if (_cannonTargetStatus == kCTSLeftRobotIsTarget)
517 openSmacker(0x500B004F, false);
518 else if (_cannonTargetStatus == kCTSLeftNoRobot)
519 openSmacker(0x100B010E, false);
520 _moveCannonLeftFirst = false;
521 _asHorizontalIndicator->stMoveLeft();
522 } else {
523 playActionVideo();
524 }
525 break;
526 }
527 }
528
529 if (_lockSymbolsPart1Countdown != 0 && (--_lockSymbolsPart1Countdown == 0) && isSymbolsPart1Solved()) {
530 for (int i = 0; i < 3; i++)
531 _asSymbols[i]->hide();
532 if (!getGlobalVar(V_ROBOT_HIT) || getGlobalVar(V_CANNON_RAISED) || getGlobalVar(V_CANNON_TURNED)) {
533 _ssSymbolEdges[0]->show();
534 _ssTargetLines[0]->show();
535 _asVerticalIndicator->show();
536 }
537 }
538
539 if (_lockSymbolsPart2Countdown != 0 && (--_lockSymbolsPart2Countdown == 0) && isSymbolsPart2Solved()) {
540 for (int i = 3; i < 6; i++)
541 _asSymbols[i]->hide();
542 if (!getGlobalVar(V_ROBOT_HIT) || getGlobalVar(V_CANNON_RAISED) || getGlobalVar(V_CANNON_TURNED)) {
543 _ssSymbolEdges[1]->show();
544 _ssTargetLines[1]->show();
545 _asHorizontalIndicator->show();
546 }
547 }
548
549 }
550
handleMessage(int messageNum,const MessageParam & param,Entity * sender)551 uint32 Scene3009::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
552 Scene::handleMessage(messageNum, param, sender);
553 switch (messageNum) {
554 case NM_MOUSE_CLICK:
555 if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !getGlobalVar(V_CANNON_RAISED)) {
556 setGlobalVar(V_CANNON_TARGET_STATUS, 0);
557 leaveScene(0);
558 }
559 break;
560 case NM_ANIMATION_UPDATE:
561 if (!getGlobalVar(V_CANNON_RAISED)) {
562 if (!getGlobalVar(V_WALL_BROKEN)) {
563 _cannonTargetStatus = kCTSBreakWall;
564 setGlobalVar(V_WALL_BROKEN, 1);
565 } else {
566 _cannonTargetStatus = kCTSWall;
567 }
568 } else if (!getGlobalVar(V_CANNON_TURNED)) {
569 _cannonTargetStatus = kCTSEmptyness;
570 } else if (!getGlobalVar(V_ROBOT_TARGET)) {
571 _cannonTargetStatus = kCTSFireRobotNoTarget;
572 } else if (!getGlobalVar(V_ROBOT_HIT)) {
573 setGlobalVar(V_ROBOT_HIT, 1);
574 _cannonTargetStatus = kCTSFireRobotIsTarget;
575 } else {
576 _cannonTargetStatus = kCTSFireNoRobot;
577 }
578 playActionVideo();
579 break;
580 case 0x2001:
581 _lockSymbolsPart1Countdown = 24;
582 break;
583 case NM_POSITION_CHANGE:
584 // Raise/lower the cannon
585 if (!getGlobalVar(V_CANNON_TURNED) && !_isTurning) {
586 if (getGlobalVar(V_CANNON_RAISED)) {
587 _cannonTargetStatus = kCTSLowerCannon;
588 setGlobalVar(V_CANNON_RAISED, 0);
589 } else {
590 _cannonTargetStatus = kCTSRaiseCannon;
591 setGlobalVar(V_CANNON_RAISED, 1);
592 }
593 playActionVideo();
594 }
595 break;
596 case 0x2003:
597 _lockSymbolsPart2Countdown = 24;
598 break;
599 case 0x2004:
600 // Turn the cannon if it's raised
601 if (getGlobalVar(V_CANNON_RAISED)) {
602 if (!getGlobalVar(V_CANNON_TURNED)) {
603 // Cannon is at the left position
604 if (!getGlobalVar(V_ROBOT_TARGET)) {
605 _cannonTargetStatus = kCTSRightRobotNoTarget;
606 } else if (!getGlobalVar(V_ROBOT_HIT)) {
607 _cannonTargetStatus = kCTSRightRobotIsTarget;
608 } else {
609 _cannonTargetStatus = kCTSRightNoRobot;
610 }
611 setGlobalVar(V_CANNON_TURNED, 1);
612 _isTurning = true;
613 playActionVideo();
614 } else {
615 // Cannon is at the right position
616 if (!getGlobalVar(V_ROBOT_TARGET)) {
617 _cannonTargetStatus = kCTSLeftRobotNoTarget;
618 openSmacker(0x108A000F, false);
619 } else if (!getGlobalVar(V_ROBOT_HIT)) {
620 _cannonTargetStatus = kCTSLeftRobotIsTarget;
621 openSmacker(0x500B002F, false);
622 } else {
623 _cannonTargetStatus = kCTSLeftNoRobot;
624 openSmacker(0x100B008E, false);
625 }
626 _moveCannonLeftFirst = true;
627 _isTurning = true;
628 _keepVideo = false;
629 setGlobalVar(V_CANNON_TURNED, 0);
630 }
631 }
632 break;
633 }
634 return 0;
635 }
636
playActionVideo()637 void Scene3009::playActionVideo() {
638 setGlobalVar(V_CANNON_TARGET_STATUS, _cannonTargetStatus);
639 setGlobalVar(V_CANNON_SMACKER_NAME, kScene3009CannonActionVideos[_cannonTargetStatus]);
640 leaveScene(1);
641 }
642
isSymbolsPart1Solved()643 bool Scene3009::isSymbolsPart1Solved() {
644 for (int i = 0; i < 3; i++)
645 if (_correctSymbols[i] != getSubVar(VA_CURR_CANNON_SYMBOLS, i))
646 return false;
647 return true;
648 }
649
isSymbolsPart2Solved()650 bool Scene3009::isSymbolsPart2Solved() {
651 for (int i = 3; i < 6; i++)
652 if (_correctSymbols[i] != getSubVar(VA_CURR_CANNON_SYMBOLS, i))
653 return false;
654 return true;
655 }
656
isTurning()657 bool Scene3009::isTurning() {
658 return _isTurning;
659 }
660
661 // Scene3010
662
663 static const uint32 kScene3010ButtonNameHashes[] = {
664 0x304008D2,
665 0x40119852,
666 0x01180951
667 };
668
Scene3010(NeverhoodEngine * vm,Module * parentModule,int which)669 Scene3010::Scene3010(NeverhoodEngine *vm, Module *parentModule, int which)
670 : Scene(vm, parentModule), _countdown(0), _doorUnlocked(false), _checkUnlocked(false) {
671
672 int initCountdown = 0;
673
674 setBackground(0x80802626);
675 setPalette(0x80802626);
676
677 for (int i = 0; i < 3; i++) {
678 _asDeadBolts[i] = insertSprite<AsScene3010DeadBolt>(this, i, which == 1);//CHECKME
679 _ssDeadBoltButtons[i] = insertSprite<SsScene3010DeadBoltButton>(this, i, initCountdown, which == 1);//CHECKME
680 addCollisionSprite(_ssDeadBoltButtons[i]);
681 if (getSubVar(VA_LOCKS_DISABLED, kScene3010ButtonNameHashes[i]))
682 initCountdown++;
683 _boltUnlocking[i] = false;
684 _boltUnlocked[i] = false;
685 }
686
687 if (which == 0) {
688 insertPuzzleMouse(0x02622800, 20, 620);
689 }
690
691 loadSound(0, 0x68E25540);
692
693 SetMessageHandler(&Scene3010::handleMessage);
694 SetUpdateHandler(&Scene3010::update);
695
696 if (which == 1) {
697 _checkUnlocked = true;
698 for (int i = 0; i < 3; i++) {
699 _boltUnlocked[i] = true;
700 _ssDeadBoltButtons[i]->setCountdown(i + 1);
701 _asDeadBolts[i]->setCountdown(i + 1);
702 }
703 }
704
705 }
706
update()707 void Scene3010::update() {
708 Scene::update();
709 if (_checkUnlocked && !_boltUnlocked[0] && !_boltUnlocked[1] && !_boltUnlocked[2]) {
710 _countdown = 24;
711 _checkUnlocked = false;
712 }
713 if (_countdown != 0 && (--_countdown == 0)) {
714 leaveScene(_doorUnlocked ? 1 : 0);
715 }
716 }
717
handleMessage(int messageNum,const MessageParam & param,Entity * sender)718 uint32 Scene3010::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
719 Scene::handleMessage(messageNum, param, sender);
720 switch (messageNum) {
721 case NM_MOUSE_CLICK:
722 if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && _countdown == 0 && !_checkUnlocked) {
723 if (!_boltUnlocking[0] && !_boltUnlocking[1] && !_boltUnlocking[2]) {
724 showMouse(false);
725 if (!_boltUnlocked[0] && !_boltUnlocked[1] && !_boltUnlocked[2]) {
726 _countdown = 1;
727 } else {
728 _checkUnlocked = true;
729 for (int i = 0; i < 3; i++) {
730 _ssDeadBoltButtons[i]->setCountdown(i);
731 if (_boltUnlocked[i]) {
732 _asDeadBolts[i]->setCountdown(i);
733 }
734 }
735 }
736 }
737 }
738 break;
739 case NM_ANIMATION_UPDATE:
740 if (!_boltUnlocked[param.asInteger()] && !_checkUnlocked && _countdown == 0) {
741 _asDeadBolts[param.asInteger()]->unlock(false);
742 _boltUnlocking[param.asInteger()] = true;
743 }
744 break;
745 case 0x2001:
746 _boltUnlocked[param.asInteger()] = true;
747 _boltUnlocking[param.asInteger()] = false;
748 if (_boltUnlocked[0] && _boltUnlocked[1] && _boltUnlocked[2]) {
749 if (!getGlobalVar(V_BOLT_DOOR_UNLOCKED)) {
750 setGlobalVar(V_BOLT_DOOR_UNLOCKED, 1);
751 playSound(0);
752 _countdown = 60;
753 } else {
754 _countdown = 48;
755 }
756 _doorUnlocked = true;
757 }
758 break;
759 case NM_POSITION_CHANGE:
760 if (!_checkUnlocked && _countdown == 0) {
761 _asDeadBolts[param.asInteger()]->lock();
762 }
763 break;
764 case 0x2003:
765 _boltUnlocked[param.asInteger()] = false;
766 break;
767 }
768 return 0;
769 }
770
Scene3011(NeverhoodEngine * vm,Module * parentModule,int which)771 Scene3011::Scene3011(NeverhoodEngine *vm, Module *parentModule, int which)
772 : Scene(vm, parentModule), _updateStatus(0), _buttonClicked(false), _currentSymbolIndex(0), _countdown(0) {
773
774 _vm->gameModule()->initCodeSymbolsPuzzle();
775 _noisySymbolIndex = getGlobalVar(V_NOISY_SYMBOL_INDEX);
776
777 SetMessageHandler(&Scene3011::handleMessage);
778 SetUpdateHandler(&Scene3011::update);
779
780 setBackground(0x92124A04);
781 setPalette(0xA4070114);
782 addEntity(_palette);
783
784 insertPuzzleMouse(0x24A00929, 20, 620);
785
786 for (int symbolIndex = 0; symbolIndex < 12; symbolIndex++)
787 _asSymbols[symbolIndex] = insertSprite<AsScene3011Symbol>(symbolIndex, true);
788
789 _ssButton = insertSprite<SsScene3011Button>(this, true);
790 addCollisionSprite(_ssButton);
791
792 }
793
update()794 void Scene3011::update() {
795 Scene::update();
796
797 if (_countdown != 0 && (--_countdown == 0)) {
798 switch (_updateStatus) {
799 case 0:
800 if (_buttonClicked) {
801 if (_noisySymbolIndex == _currentSymbolIndex) {
802 do {
803 _noisyRandomSymbolIndex = _vm->_rnd->getRandomNumber(12 - 1);
804 } while (_noisySymbolIndex == _noisyRandomSymbolIndex);
805 _asSymbols[getSubVar(VA_CODE_SYMBOLS, _noisyRandomSymbolIndex)]->show(true);
806 } else {
807 _asSymbols[getSubVar(VA_CODE_SYMBOLS, _currentSymbolIndex)]->show(false);
808 }
809 _updateStatus = 1;
810 _countdown = 24;
811 fadeIn();
812 _buttonClicked = false;
813 }
814 break;
815 case 1:
816 _updateStatus = 2;
817 _countdown = 24;
818 break;
819 case 2:
820 fadeOut();
821 _updateStatus = 3;
822 _countdown = 24;
823 break;
824 case 3:
825 _updateStatus = 0;
826 _countdown = 1;
827 if (_noisySymbolIndex == _currentSymbolIndex) {
828 _asSymbols[getSubVar(VA_CODE_SYMBOLS, _noisyRandomSymbolIndex)]->hide();
829 } else {
830 _asSymbols[getSubVar(VA_CODE_SYMBOLS, _currentSymbolIndex)]->hide();
831 }
832 _currentSymbolIndex++;
833 if (_currentSymbolIndex >= 12)
834 _currentSymbolIndex = 0;
835 break;
836 }
837 }
838 }
839
handleMessage(int messageNum,const MessageParam & param,Entity * sender)840 uint32 Scene3011::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
841 Scene::handleMessage(messageNum, param, sender);
842 switch (messageNum) {
843 case NM_MOUSE_CLICK:
844 if (param.asPoint().x <= 20 || param.asPoint().x >= 620) {
845 leaveScene(0);
846 }
847 break;
848 case NM_ANIMATION_UPDATE:
849 _buttonClicked = true;
850 if (_countdown == 0)
851 _countdown = 1;
852 break;
853 }
854 return 0;
855 }
856
fadeIn()857 void Scene3011::fadeIn() {
858 _palette->addBasePalette(0x92124A04, 0, 256, 0);
859 _palette->startFadeToPalette(24);
860 }
861
fadeOut()862 void Scene3011::fadeOut() {
863 _palette->addBasePalette(0xA4070114, 0, 256, 0);
864 _palette->startFadeToPalette(24);
865 }
866
867 } // End of namespace Neverhood
868