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 &param, 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 &param, 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 &param, 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