/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "neverhood/gamemodule.h" #include "neverhood/navigationscene.h" #include "neverhood/modules/module3000.h" #include "neverhood/modules/module3000_sprites.h" namespace Neverhood { static const uint32 kModule3000SoundList[] = { 0x92025040, 0x90035066, 0x90815450, 0x99801500, 0x90E14440, 0x16805048, 0x90F0D1C3, 0 }; Module3000::Module3000(NeverhoodEngine *vm, Module *parentModule, int which) : Module(vm, parentModule), _waterfallSoundVolume(0) { _vm->_soundMan->addSoundList(0x81293110, kModule3000SoundList); _vm->_soundMan->setSoundListParams(kModule3000SoundList, true, 50, 600, 5, 150); _vm->_soundMan->setSoundParams(0x90F0D1C3, false, 20000, 30000, 20000, 30000); _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0); _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0); _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0); _isWaterfallRunning = getGlobalVar(V_WALL_BROKEN) != 1; if (_isWaterfallRunning) { _vm->_soundMan->setSoundVolume(0x90F0D1C3, 0); _vm->_soundMan->playSoundLooping(0x90F0D1C3); } if (which < 0) { createScene(_vm->gameState().sceneNum, -1); } else if (which == 0) { createScene(1, 0); } else if (which == 1) { createScene(4, 2); } else if (which == 2) { createScene(4, 1); } else if (which == 3) { createScene(5, 1); } } Module3000::~Module3000() { _vm->_soundMan->deleteGroup(0x81293110); } void Module3000::createScene(int sceneNum, int which) { static const byte kNavigationTypes05[] = {2, 0}; static const byte kNavigationTypes06[] = {5}; debug(1, "Module3000::createScene(%d, %d)", sceneNum, which); _vm->gameState().sceneNum = sceneNum; switch (_vm->gameState().sceneNum) { case 1: if (!getGlobalVar(V_BOLT_DOOR_OPEN)) { createNavigationScene(0x004B7C80, which); } else if (getGlobalVar(V_WALL_BROKEN)) { createNavigationScene(0x004B7CE0, which); } else { createNavigationScene(0x004B7CB0, which); } break; case 2: _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0); if (_isWaterfallRunning) { _waterfallSoundVolume = 90; _vm->_soundMan->setSoundVolume(0x90F0D1C3, 90); } if (getGlobalVar(V_WALL_BROKEN)) { createNavigationScene(0x004B7D58, which); } else { createNavigationScene(0x004B7D10, which); } break; case 3: if (getGlobalVar(V_STAIRS_DOWN)) createNavigationScene(0x004B7E60, which); else if (getGlobalVar(V_WALL_BROKEN)) createNavigationScene(0x004B7DA0, which); else createNavigationScene(0x004B7E00, which); break; case 4: if (getGlobalVar(V_STAIRS_DOWN)) createNavigationScene(0x004B7F20, which); else createNavigationScene(0x004B7EC0, which); break; case 5: createNavigationScene(0x004B7F80, which, kNavigationTypes05); break; case 6: createNavigationScene(0x004B7FB0, which, kNavigationTypes06); break; case 7: _vm->_soundMan->setSoundListParams(kModule3000SoundList, false, 0, 0, 0, 0); if (!getSubVar(VA_IS_PUZZLE_INIT, 0x089809C2)) { setSubVar(VA_IS_PUZZLE_INIT, 0x089809C2, 1); createSmackerScene(0x90022001, true, true, false); } else createSmackerScene(0x98022001, true, true, false); break; case 8: _childObject = new Scene3009(_vm, this, which); break; case 9: _childObject = new Scene3010(_vm, this, 0); break; case 10: _childObject = new Scene3011(_vm, this, 0); break; case 11: _vm->_soundMan->setSoundListParams(kModule3000SoundList, false, 0, 0, 0, 0); if (!getSubVar(VA_IS_PUZZLE_INIT, 0x10130993)) { setSubVar(VA_IS_PUZZLE_INIT, 0x10130993, 1); createSmackerScene(0x31093019, true, true, false); } else createSmackerScene(0x20093019, true, true, false); break; case 12: _childObject = new Scene3010(_vm, this, 1); break; // NOTE: Newly introduced sceneNums case 1001: if (!getGlobalVar(V_BOLT_DOOR_OPEN)) if (getGlobalVar(V_WALL_BROKEN)) createSmackerScene(0x00940021, true, true, false); else createSmackerScene(0x01140021, true, true, false); else if (getGlobalVar(V_WALL_BROKEN)) createSmackerScene(0x001011B1, true, true, false); else createSmackerScene(0x001021B1, true, true, false); setGlobalVar(V_BOLT_DOOR_OPEN, getGlobalVar(V_BOLT_DOOR_OPEN) ? 0 : 1); break; case 1006: createSmackerScene(0x080810C5, true, true, false); break; case 1008: createSmackerScene(getGlobalVar(V_CANNON_SMACKER_NAME), true, true, false); break; } SetUpdateHandler(&Module3000::updateScene); _childObject->handleUpdate(); } void Module3000::updateScene() { if (!updateChild()) { switch (_vm->gameState().sceneNum) { case 1: if (!getGlobalVar(V_BOLT_DOOR_OPEN)) { if (_moduleResult == 0) createScene(9, -1); else if (_moduleResult == 1) leaveModule(0); } else { if (_moduleResult == 0) if (_navigationAreaType == 2) createScene(2, 0); else createScene(1001, -1); else if (_moduleResult == 1) leaveModule(0); } break; case 2: _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0); if (_isWaterfallRunning) { _waterfallSoundVolume = 0; _vm->_soundMan->setSoundVolume(0x90F0D1C3, 0); } if (_moduleResult == 0) { createScene(3, 0); } else if (_moduleResult == 1) { setGlobalVar(V_BOLT_DOOR_OPEN, 0); createScene(1, 1); } break; case 3: if (_moduleResult == 1) createScene(4, 0); else if (_moduleResult == 3) createScene(10, -1); else if (getGlobalVar(V_STAIRS_DOWN)) createScene(5, 0); else createScene(2, 1); break; case 4: if (_moduleResult == 0) leaveModule(1); else if (_moduleResult == 1) createScene(7, -1); else if (_moduleResult == 2) createScene(3, 3); break; case 5: if (_moduleResult == 0) createScene(6, 0); else if (_moduleResult == 1) createScene(3, 0); break; case 6: if (_navigationAreaType == 4) createScene(11, -1); else createScene(1006, -1); break; case 7: createScene(8, -1); break; case 8: _isWaterfallRunning = getGlobalVar(V_WALL_BROKEN) != 1; if (_moduleResult != 1) { _vm->_soundMan->setSoundListParams(kModule3000SoundList, true, 0, 0, 0, 0); createScene(4, 1); } else if (getGlobalVar(V_CANNON_SMACKER_NAME)) { createScene(1008, -1); } else { _vm->_soundMan->setSoundListParams(kModule3000SoundList, true, 0, 0, 0, 0); createScene(4, 1); } break; case 9: if (_moduleResult == 0 || _moduleResult == 2) createScene(1, 0); else if (_moduleResult == 1) createScene(1001, -1); break; case 10: createScene(3, 3); break; case 11: leaveModule(3); break; case 12: createScene(1, 0); break; case 1001: if (getGlobalVar(V_BOLT_DOOR_OPEN)) createScene(1, 0); else createScene(12, -1); break; case 1006: createScene(5, 0); break; case 1008: createScene(8, -1); break; } } else { switch (_vm->gameState().sceneNum) { case 1: if (navigationScene()->isWalkingForward()) { uint32 frameNumber = navigationScene()->getFrameNumber(); int navigationIndex = navigationScene()->getNavigationIndex(); if (navigationIndex == 1) { if (frameNumber == 0) { _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0); _vm->_soundMan->setSoundVolume(0x48498E46, 70); _vm->_soundMan->setSoundVolume(0x50399F64, 70); } else if (frameNumber == 100) { _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0); } } else if (navigationIndex == 0) { if (frameNumber == 0) { _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0); _vm->_soundMan->setSoundVolume(0x48498E46, 70); _vm->_soundMan->setSoundVolume(0x50399F64, 70); } else if (frameNumber == 10) { _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0); } if (_isWaterfallRunning && _waterfallSoundVolume < 90 && frameNumber % 2) { if (frameNumber == 0) _waterfallSoundVolume = 40; else _waterfallSoundVolume++; _vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume); } } } break; case 2: if (navigationScene()->isWalkingForward()) { uint32 frameNumber = navigationScene()->getFrameNumber(); int navigationIndex = navigationScene()->getNavigationIndex(); if (_isWaterfallRunning && _waterfallSoundVolume > 1 && frameNumber % 2) { _waterfallSoundVolume--; _vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume); } if (navigationIndex == 0) { if (frameNumber == 35) { _vm->_soundMan->playTwoSounds(0x81293110, 0x41861371, 0x43A2507F, 0); } } else if (navigationIndex == 1) { if (frameNumber == 55) { _vm->_soundMan->playTwoSounds(0x81293110, 0x48498E46, 0x50399F64, 0); _vm->_soundMan->setSoundVolume(0x48498E46, 70); _vm->_soundMan->setSoundVolume(0x50399F64, 70); } } } break; case 3: if (navigationScene()->isWalkingForward()) { uint32 frameNumber = navigationScene()->getFrameNumber(); int navigationIndex = navigationScene()->getNavigationIndex(); if (navigationIndex == 2) { if (frameNumber == 40) { _vm->_soundMan->playTwoSounds(0x81293110, 0x40030A51, 0xC862CA15, 0); } if (_isWaterfallRunning && _waterfallSoundVolume < 90 && frameNumber % 2) { if (frameNumber == 0) _waterfallSoundVolume = 40; else _waterfallSoundVolume++; _vm->_soundMan->setSoundVolume(0x90F0D1C3, _waterfallSoundVolume); } } } break; case 5: if (navigationScene()->isWalkingForward() && navigationScene()->getNavigationIndex() == 0) { _vm->_soundMan->setTwoSoundsPlayFlag(false); } break; } } } // Scene3009 enum { kCTSNull = 0, kCTSBreakWall = 1, kCTSWall = 2, kCTSEmptyness = 3, kCTSFireRobotNoTarget = 4, kCTSFireRobotIsTarget = 5, kCTSFireNoRobot = 6, kCTSRaiseCannon = 7, kCTSRightRobotNoTarget = 8, kCTSRightRobotIsTarget = 9, kCTSRightNoRobot = 10, kCTSLeftRobotNoTarget = 11, kCTSLeftRobotIsTarget = 12, kCTSLeftNoRobot = 13, kCTSLowerCannon = 14, kCTSCount = 14 }; static const uint32 kScene3009CannonScopeVideos[] = { 0x1010000D, 0x340A0049, 0x340A0049, 0x0282081D, 0x0082080D, 0x0882080D, 0x0882080D, 0x0282081D, 0x004B000B, 0x014B000B, 0x044B000B, 0x0282081D, 0x0282081D, 0x0282081D, 0x340A0049 }; static const uint32 kScene3009CannonActionVideos[] = { 0x00000000, 0x8004001B, // 1 Fire cannon at wall, it breaks (lowered) 0x0004001A, // 2 Fire cannon at wall, nothing happens (lowered) 0x1048404B, // 3 Fire cannon at emptyness (raised) 0x50200109, // 4 Fire cannon, robot missed (raised) 0x12032109, // 5 Fire cannon, robot hit (raised) 0x10201109, // 6 Fire cannon, no robot (raised) 0x000A2030, // 7 Raise the cannon 0x000A0028, // 8 0x000A0028, // 9 0x000A0028, // 10 0x040A1069, // 11 0x040A1069, // 12 0x040A1069, // 13 0x240A1101 // 14 Lower the cannon }; Scene3009::Scene3009(NeverhoodEngine *vm, Module *parentModule, int which) : Scene(vm, parentModule), _keepVideo(false), _moveCannonLeftFirst(false), _isTurning(false), _lockSymbolsPart1Countdown(1), _lockSymbolsPart2Countdown(1) { _cannonTargetStatus = getGlobalVar(V_CANNON_TARGET_STATUS); _vm->gameModule()->initCannonSymbolsPuzzle(); setGlobalVar(V_CANNON_SMACKER_NAME, 0); _vm->_screen->clear(); setBackground(0xD000420C); setPalette(0xD000420C); insertPuzzleMouse(0x04208D08, 20, 620); _ssFireCannonButton = insertSprite(this); addCollisionSprite(_ssFireCannonButton); _asVerticalIndicator = insertSprite(this, _cannonTargetStatus); addCollisionSprite(_asVerticalIndicator); _asHorizontalIndicator = insertSprite(this, _cannonTargetStatus); addCollisionSprite(_asHorizontalIndicator); if (_cannonTargetStatus != kCTSNull && _cannonTargetStatus != kCTSRightRobotNoTarget && _cannonTargetStatus != kCTSRightRobotIsTarget && _cannonTargetStatus != kCTSRightNoRobot) { _keepVideo = true; } else { _keepVideo = false; if (_cannonTargetStatus != kCTSNull) { _asHorizontalIndicator->stMoveRight(); _isTurning = true; } } _cannonSmackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, kScene3009CannonScopeVideos[_cannonTargetStatus], false, _keepVideo)); _cannonSmackerPlayer->setDrawPos(89, 37); _palette->usePalette(); // Use it again since the SmackerPlayer overrides the usage insertStaticSprite(0x8540252C, 400); for (int i = 0; i < 2; i++) { _ssSymbolEdges[i] = insertSprite(i); _ssTargetLines[i] = insertSprite(i); } for (int symbolPosition = 0; symbolPosition < 6; symbolPosition++) { _asSymbols[symbolPosition] = insertSprite(this, symbolPosition); if (symbolPosition < 3) _correctSymbols[symbolPosition] = getSubVar(VA_GOOD_CANNON_SYMBOLS_1, symbolPosition); else _correctSymbols[symbolPosition] = getSubVar(VA_GOOD_CANNON_SYMBOLS_2, symbolPosition - 3); } SetMessageHandler(&Scene3009::handleMessage); SetUpdateHandler(&Scene3009::update); } Scene3009::~Scene3009() { } void Scene3009::openSmacker(uint32 fileHash, bool keepLastFrame) { _cannonSmackerPlayer->open(fileHash, keepLastFrame); //_vm->_screen->setSmackerDecoder(_cannonSmackerPlayer->getSmackerDecoder()); _palette->usePalette(); } void Scene3009::update() { Scene::update(); if (!_keepVideo && _cannonSmackerPlayer->isDone() && _cannonTargetStatus <= kCTSCount) { switch (_cannonTargetStatus) { case kCTSNull: case kCTSLowerCannon: openSmacker(0x340A0049, true); _keepVideo = true; break; case kCTSRightRobotNoTarget: openSmacker(0x0082080D, true); _keepVideo = true; _isTurning = false; break; case kCTSRightRobotIsTarget: openSmacker(0x0282080D, true); _keepVideo = true; _isTurning = false; break; case kCTSRightNoRobot: openSmacker(0x0882080D, true); _keepVideo = true; _isTurning = false; break; case kCTSLeftRobotNoTarget: case kCTSLeftRobotIsTarget: case kCTSLeftNoRobot: if (_moveCannonLeftFirst) { if (_cannonTargetStatus == kCTSLeftRobotNoTarget) openSmacker(0x110A000F, false); else if (_cannonTargetStatus == kCTSLeftRobotIsTarget) openSmacker(0x500B004F, false); else if (_cannonTargetStatus == kCTSLeftNoRobot) openSmacker(0x100B010E, false); _moveCannonLeftFirst = false; _asHorizontalIndicator->stMoveLeft(); } else { playActionVideo(); } break; } } if (_lockSymbolsPart1Countdown != 0 && (--_lockSymbolsPart1Countdown == 0) && isSymbolsPart1Solved()) { for (int i = 0; i < 3; i++) _asSymbols[i]->hide(); if (!getGlobalVar(V_ROBOT_HIT) || getGlobalVar(V_CANNON_RAISED) || getGlobalVar(V_CANNON_TURNED)) { _ssSymbolEdges[0]->show(); _ssTargetLines[0]->show(); _asVerticalIndicator->show(); } } if (_lockSymbolsPart2Countdown != 0 && (--_lockSymbolsPart2Countdown == 0) && isSymbolsPart2Solved()) { for (int i = 3; i < 6; i++) _asSymbols[i]->hide(); if (!getGlobalVar(V_ROBOT_HIT) || getGlobalVar(V_CANNON_RAISED) || getGlobalVar(V_CANNON_TURNED)) { _ssSymbolEdges[1]->show(); _ssTargetLines[1]->show(); _asHorizontalIndicator->show(); } } } uint32 Scene3009::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { Scene::handleMessage(messageNum, param, sender); switch (messageNum) { case NM_MOUSE_CLICK: if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !getGlobalVar(V_CANNON_RAISED)) { setGlobalVar(V_CANNON_TARGET_STATUS, 0); leaveScene(0); } break; case NM_ANIMATION_UPDATE: if (!getGlobalVar(V_CANNON_RAISED)) { if (!getGlobalVar(V_WALL_BROKEN)) { _cannonTargetStatus = kCTSBreakWall; setGlobalVar(V_WALL_BROKEN, 1); } else { _cannonTargetStatus = kCTSWall; } } else if (!getGlobalVar(V_CANNON_TURNED)) { _cannonTargetStatus = kCTSEmptyness; } else if (!getGlobalVar(V_ROBOT_TARGET)) { _cannonTargetStatus = kCTSFireRobotNoTarget; } else if (!getGlobalVar(V_ROBOT_HIT)) { setGlobalVar(V_ROBOT_HIT, 1); _cannonTargetStatus = kCTSFireRobotIsTarget; } else { _cannonTargetStatus = kCTSFireNoRobot; } playActionVideo(); break; case 0x2001: _lockSymbolsPart1Countdown = 24; break; case NM_POSITION_CHANGE: // Raise/lower the cannon if (!getGlobalVar(V_CANNON_TURNED) && !_isTurning) { if (getGlobalVar(V_CANNON_RAISED)) { _cannonTargetStatus = kCTSLowerCannon; setGlobalVar(V_CANNON_RAISED, 0); } else { _cannonTargetStatus = kCTSRaiseCannon; setGlobalVar(V_CANNON_RAISED, 1); } playActionVideo(); } break; case 0x2003: _lockSymbolsPart2Countdown = 24; break; case 0x2004: // Turn the cannon if it's raised if (getGlobalVar(V_CANNON_RAISED)) { if (!getGlobalVar(V_CANNON_TURNED)) { // Cannon is at the left position if (!getGlobalVar(V_ROBOT_TARGET)) { _cannonTargetStatus = kCTSRightRobotNoTarget; } else if (!getGlobalVar(V_ROBOT_HIT)) { _cannonTargetStatus = kCTSRightRobotIsTarget; } else { _cannonTargetStatus = kCTSRightNoRobot; } setGlobalVar(V_CANNON_TURNED, 1); _isTurning = true; playActionVideo(); } else { // Cannon is at the right position if (!getGlobalVar(V_ROBOT_TARGET)) { _cannonTargetStatus = kCTSLeftRobotNoTarget; openSmacker(0x108A000F, false); } else if (!getGlobalVar(V_ROBOT_HIT)) { _cannonTargetStatus = kCTSLeftRobotIsTarget; openSmacker(0x500B002F, false); } else { _cannonTargetStatus = kCTSLeftNoRobot; openSmacker(0x100B008E, false); } _moveCannonLeftFirst = true; _isTurning = true; _keepVideo = false; setGlobalVar(V_CANNON_TURNED, 0); } } break; } return 0; } void Scene3009::playActionVideo() { setGlobalVar(V_CANNON_TARGET_STATUS, _cannonTargetStatus); setGlobalVar(V_CANNON_SMACKER_NAME, kScene3009CannonActionVideos[_cannonTargetStatus]); leaveScene(1); } bool Scene3009::isSymbolsPart1Solved() { for (int i = 0; i < 3; i++) if (_correctSymbols[i] != getSubVar(VA_CURR_CANNON_SYMBOLS, i)) return false; return true; } bool Scene3009::isSymbolsPart2Solved() { for (int i = 3; i < 6; i++) if (_correctSymbols[i] != getSubVar(VA_CURR_CANNON_SYMBOLS, i)) return false; return true; } bool Scene3009::isTurning() { return _isTurning; } // Scene3010 static const uint32 kScene3010ButtonNameHashes[] = { 0x304008D2, 0x40119852, 0x01180951 }; Scene3010::Scene3010(NeverhoodEngine *vm, Module *parentModule, int which) : Scene(vm, parentModule), _countdown(0), _doorUnlocked(false), _checkUnlocked(false) { int initCountdown = 0; setBackground(0x80802626); setPalette(0x80802626); for (int i = 0; i < 3; i++) { _asDeadBolts[i] = insertSprite(this, i, which == 1);//CHECKME _ssDeadBoltButtons[i] = insertSprite(this, i, initCountdown, which == 1);//CHECKME addCollisionSprite(_ssDeadBoltButtons[i]); if (getSubVar(VA_LOCKS_DISABLED, kScene3010ButtonNameHashes[i])) initCountdown++; _boltUnlocking[i] = false; _boltUnlocked[i] = false; } if (which == 0) { insertPuzzleMouse(0x02622800, 20, 620); } loadSound(0, 0x68E25540); SetMessageHandler(&Scene3010::handleMessage); SetUpdateHandler(&Scene3010::update); if (which == 1) { _checkUnlocked = true; for (int i = 0; i < 3; i++) { _boltUnlocked[i] = true; _ssDeadBoltButtons[i]->setCountdown(i + 1); _asDeadBolts[i]->setCountdown(i + 1); } } } void Scene3010::update() { Scene::update(); if (_checkUnlocked && !_boltUnlocked[0] && !_boltUnlocked[1] && !_boltUnlocked[2]) { _countdown = 24; _checkUnlocked = false; } if (_countdown != 0 && (--_countdown == 0)) { leaveScene(_doorUnlocked ? 1 : 0); } } uint32 Scene3010::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { Scene::handleMessage(messageNum, param, sender); switch (messageNum) { case NM_MOUSE_CLICK: if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && _countdown == 0 && !_checkUnlocked) { if (!_boltUnlocking[0] && !_boltUnlocking[1] && !_boltUnlocking[2]) { showMouse(false); if (!_boltUnlocked[0] && !_boltUnlocked[1] && !_boltUnlocked[2]) { _countdown = 1; } else { _checkUnlocked = true; for (int i = 0; i < 3; i++) { _ssDeadBoltButtons[i]->setCountdown(i); if (_boltUnlocked[i]) { _asDeadBolts[i]->setCountdown(i); } } } } } break; case NM_ANIMATION_UPDATE: if (!_boltUnlocked[param.asInteger()] && !_checkUnlocked && _countdown == 0) { _asDeadBolts[param.asInteger()]->unlock(false); _boltUnlocking[param.asInteger()] = true; } break; case 0x2001: _boltUnlocked[param.asInteger()] = true; _boltUnlocking[param.asInteger()] = false; if (_boltUnlocked[0] && _boltUnlocked[1] && _boltUnlocked[2]) { if (!getGlobalVar(V_BOLT_DOOR_UNLOCKED)) { setGlobalVar(V_BOLT_DOOR_UNLOCKED, 1); playSound(0); _countdown = 60; } else { _countdown = 48; } _doorUnlocked = true; } break; case NM_POSITION_CHANGE: if (!_checkUnlocked && _countdown == 0) { _asDeadBolts[param.asInteger()]->lock(); } break; case 0x2003: _boltUnlocked[param.asInteger()] = false; break; } return 0; } Scene3011::Scene3011(NeverhoodEngine *vm, Module *parentModule, int which) : Scene(vm, parentModule), _updateStatus(0), _buttonClicked(false), _currentSymbolIndex(0), _countdown(0) { _vm->gameModule()->initCodeSymbolsPuzzle(); _noisySymbolIndex = getGlobalVar(V_NOISY_SYMBOL_INDEX); SetMessageHandler(&Scene3011::handleMessage); SetUpdateHandler(&Scene3011::update); setBackground(0x92124A04); setPalette(0xA4070114); addEntity(_palette); insertPuzzleMouse(0x24A00929, 20, 620); for (int symbolIndex = 0; symbolIndex < 12; symbolIndex++) _asSymbols[symbolIndex] = insertSprite(symbolIndex, true); _ssButton = insertSprite(this, true); addCollisionSprite(_ssButton); } void Scene3011::update() { Scene::update(); if (_countdown != 0 && (--_countdown == 0)) { switch (_updateStatus) { case 0: if (_buttonClicked) { if (_noisySymbolIndex == _currentSymbolIndex) { do { _noisyRandomSymbolIndex = _vm->_rnd->getRandomNumber(12 - 1); } while (_noisySymbolIndex == _noisyRandomSymbolIndex); _asSymbols[getSubVar(VA_CODE_SYMBOLS, _noisyRandomSymbolIndex)]->show(true); } else { _asSymbols[getSubVar(VA_CODE_SYMBOLS, _currentSymbolIndex)]->show(false); } _updateStatus = 1; _countdown = 24; fadeIn(); _buttonClicked = false; } break; case 1: _updateStatus = 2; _countdown = 24; break; case 2: fadeOut(); _updateStatus = 3; _countdown = 24; break; case 3: _updateStatus = 0; _countdown = 1; if (_noisySymbolIndex == _currentSymbolIndex) { _asSymbols[getSubVar(VA_CODE_SYMBOLS, _noisyRandomSymbolIndex)]->hide(); } else { _asSymbols[getSubVar(VA_CODE_SYMBOLS, _currentSymbolIndex)]->hide(); } _currentSymbolIndex++; if (_currentSymbolIndex >= 12) _currentSymbolIndex = 0; break; } } } uint32 Scene3011::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { Scene::handleMessage(messageNum, param, sender); switch (messageNum) { case NM_MOUSE_CLICK: if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { leaveScene(0); } break; case NM_ANIMATION_UPDATE: _buttonClicked = true; if (_countdown == 0) _countdown = 1; break; } return 0; } void Scene3011::fadeIn() { _palette->addBasePalette(0x92124A04, 0, 256, 0); _palette->startFadeToPalette(24); } void Scene3011::fadeOut() { _palette->addBasePalette(0xA4070114, 0, 256, 0); _palette->startFadeToPalette(24); } } // End of namespace Neverhood