diff options
Diffstat (limited to 'engines/neverhood/modules/module2200.cpp')
-rw-r--r-- | engines/neverhood/modules/module2200.cpp | 2565 |
1 files changed, 2565 insertions, 0 deletions
diff --git a/engines/neverhood/modules/module2200.cpp b/engines/neverhood/modules/module2200.cpp new file mode 100644 index 0000000000..4f2d9e8fd2 --- /dev/null +++ b/engines/neverhood/modules/module2200.cpp @@ -0,0 +1,2565 @@ +/* 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/modules/module2200.h" +#include "neverhood/modules/module1000.h" +#include "neverhood/modules/module1200.h" +#include "neverhood/gamemodule.h" +#include "neverhood/diskplayerscene.h" + +namespace Neverhood { + +Module2200::Module2200(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + debug("Create Module2200(%d)", which); + + _vm->_soundMan->addMusic(0x11391412, 0x601C908C); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else + createScene(0, 0); + +} + +Module2200::~Module2200() { + _vm->_soundMan->deleteGroup(0x11391412); +} + +void Module2200::createScene(int sceneNum, int which) { + debug("Module2200::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _childObject = new Scene2201(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _vm->_soundMan->startMusic(0x601C908C, 0, 2); + _childObject = new Scene2202(_vm, this, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->_soundMan->startMusic(0x601C908C, 0, 2); + _childObject = new Scene2203(_vm, this, which); + break; + case 3: + _vm->gameState().sceneNum = 3; + _vm->_soundMan->stopMusic(0x601C908C, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 3); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->stopMusic(0x601C908C, 0, 2); + _childObject = new Scene2205(_vm, this, which); + break; + case 5: + _vm->gameState().sceneNum = 5; + _vm->_soundMan->stopMusic(0x601C908C, 0, 2); + _childObject = new Scene2206(_vm, this, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + _childObject = new Scene2207(_vm, this); + break; + case 7: + if (which >= 0) + _vm->gameState().which = _vm->gameState().sceneNum; + _vm->gameState().sceneNum = 7; + _childObject = new Scene2208(_vm, this, which); + break; + case 8: + _vm->gameState().sceneNum = 8; + _childObject = new Scene2208(_vm, this, which); + break; + case 9: + _vm->gameState().sceneNum = 9; + createHallOfRecordsScene(which, 0x004B7180); + break; + case 10: + _vm->gameState().sceneNum = 10; + createHallOfRecordsScene(which, 0x004B7198); + break; + case 11: + _vm->gameState().sceneNum = 11; + createHallOfRecordsScene(which, 0x004B71B0); + break; + case 12: + _vm->gameState().sceneNum = 12; + createHallOfRecordsScene(which, 0x004B71C8); + break; + case 13: + _vm->gameState().sceneNum = 13; + createHallOfRecordsScene(which, 0x004B71E0); + break; + case 14: + _vm->gameState().sceneNum = 14; + createHallOfRecordsScene(which, 0x004B71F8); + break; + case 15: + _vm->gameState().sceneNum = 15; + createHallOfRecordsScene(which, 0x004B7210); + break; + case 16: + _vm->gameState().sceneNum = 16; + createHallOfRecordsScene(which, 0x004B7228); + break; + case 17: + _vm->gameState().sceneNum = 17; + createHallOfRecordsScene(which, 0x004B7240); + break; + case 18: + _vm->gameState().sceneNum = 18; + createHallOfRecordsScene(which, 0x004B7258); + break; + case 19: + _vm->gameState().sceneNum = 19; + createHallOfRecordsScene(which, 0x004B7270); + break; + case 20: + _vm->gameState().sceneNum = 20; + createHallOfRecordsScene(which, 0x004B7288); + break; + case 21: + _vm->gameState().sceneNum = 21; + createHallOfRecordsScene(which, 0x004B72A0); + break; + case 22: + _vm->gameState().sceneNum = 22; + createHallOfRecordsScene(which, 0x004B72B8); + break; + case 23: + _vm->gameState().sceneNum = 23; + createHallOfRecordsScene(which, 0x004B72D0); + break; + case 24: + _vm->gameState().sceneNum = 24; + createHallOfRecordsScene(which, 0x004B72E8); + break; + case 25: + _vm->gameState().sceneNum = 25; + createHallOfRecordsScene(which, 0x004B7300); + break; + case 26: + _vm->gameState().sceneNum = 26; + createHallOfRecordsScene(which, 0x004B7318); + break; + case 27: + _vm->gameState().sceneNum = 27; + createHallOfRecordsScene(which, 0x004B7330); + break; + case 28: + _vm->gameState().sceneNum = 28; + createHallOfRecordsScene(which, 0x004B7348); + break; + case 29: + _vm->gameState().sceneNum = 29; + createHallOfRecordsScene(which, 0x004B7360); + break; + case 30: + _vm->gameState().sceneNum = 30; + createHallOfRecordsScene(which, 0x004B7378); + break; + case 31: + _vm->gameState().sceneNum = 31; + createHallOfRecordsScene(which, 0x004B7390); + break; + case 32: + _vm->gameState().sceneNum = 32; + createHallOfRecordsScene(which, 0x004B73A8); + break; + case 33: + _vm->gameState().sceneNum = 33; + createHallOfRecordsScene(which, 0x004B73C0); + break; + case 34: + _vm->gameState().sceneNum = 34; + createHallOfRecordsScene(which, 0x004B73D8); + break; + case 35: + _vm->gameState().sceneNum = 35; + createHallOfRecordsScene(which, 0x004B73F0); + break; + case 36: + _vm->gameState().sceneNum = 36; + createHallOfRecordsScene(which, 0x004B7408); + break; + case 37: + _vm->gameState().sceneNum = 37; + createHallOfRecordsScene(which, 0x004B7420); + break; + case 38: + _vm->gameState().sceneNum = 38; + createHallOfRecordsScene(which, 0x004B7438); + break; + case 39: + _vm->gameState().sceneNum = 39; + createHallOfRecordsScene(which, 0x004B7450); + break; + case 40: + _vm->gameState().sceneNum = 40; + createHallOfRecordsScene(which, 0x004B7468); + break; + case 41: + _vm->gameState().sceneNum = 41; + _childObject = new Scene2242(_vm, this, which); + break; + case 42: + _vm->gameState().sceneNum = 42; + createHallOfRecordsScene(which, 0x004B7480); + break; + case 43: + _vm->gameState().sceneNum = 43; + createHallOfRecordsScene(which, 0x004B7498); + break; + case 44: + _vm->gameState().sceneNum = 44; + createHallOfRecordsScene(which, 0x004B74B0); + break; + case 45: + _vm->gameState().sceneNum = 45; + createHallOfRecordsScene(which, 0x004B74C8); + break; + case 46: + _vm->gameState().sceneNum = 46; + _childObject = new Scene2247(_vm, this, which); + break; + case 47: + _vm->gameState().sceneNum = 47; + if (!getGlobalVar(V_WORLDS_JOINED)) { + if (getGlobalVar(V_LIGHTS_ON)) + createStaticScene(0x83110287, 0x10283839); + else + createStaticScene(0x83412B9D, 0x12B9983C); + } else { + if (getGlobalVar(V_LIGHTS_ON)) + createStaticScene(0x48632087, 0x3208348E); + else + createStaticScene(0x08C74886, 0x74882084); + } + break; + } + SetUpdateHandler(&Module2200::updateScene); + _childObject->handleUpdate(); +} + +#define HallOfRecordsSceneLink(nextSceneNum, prevSceneNum) \ + if (_moduleResult == 1) createScene(nextSceneNum, 0); else if (_moduleResult == 2) createScene(7, 0); else createScene(prevSceneNum, 1) + +void Module2200::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(2, 0); + else if (_moduleResult == 2) + createScene(1, 0); + else + leaveModule(0); + break; + case 1: + createScene(0, 2); + break; + case 2: + if (_moduleResult == 1) + createScene(4, 0); + else if (_moduleResult == 2) + createScene(3, 0); + else + createScene(0, 1); + break; + case 3: + createScene(2, 2); + break; + case 4: + if (_moduleResult == 1) + createScene(5, 0); + else if (_moduleResult == 2) + createScene(4, 2); + else + createScene(2, 1); + break; + case 5: + if (_moduleResult == 1) + createScene(46, 0); + else if (_moduleResult == 2) + createScene(6, 0); + else if (_moduleResult == 3) + createScene(8, 0); + else + createScene(4, 1); + break; + case 6: + createScene(5, 2); + break; + case 7: + createScene(_vm->gameState().which, 2); + break; + case 8: + createScene(5, 3); + break; + case 9: + HallOfRecordsSceneLink(10, 46); + break; + case 10: + HallOfRecordsSceneLink(11, 9); + break; + case 11: + HallOfRecordsSceneLink(12, 10); + break; + case 12: + HallOfRecordsSceneLink(13, 11); + break; + case 13: + HallOfRecordsSceneLink(14, 12); + break; + case 14: + HallOfRecordsSceneLink(15, 13); + break; + case 15: + HallOfRecordsSceneLink(16, 14); + break; + case 16: + HallOfRecordsSceneLink(17, 15); + break; + case 17: + HallOfRecordsSceneLink(18, 16); + break; + case 18: + HallOfRecordsSceneLink(19, 17); + break; + case 19: + HallOfRecordsSceneLink(20, 18); + break; + case 20: + HallOfRecordsSceneLink(21, 19); + break; + case 21: + HallOfRecordsSceneLink(22, 20); + break; + case 22: + HallOfRecordsSceneLink(23, 21); + break; + case 23: + HallOfRecordsSceneLink(24, 22); + break; + case 24: + HallOfRecordsSceneLink(25, 23); + break; + case 25: + HallOfRecordsSceneLink(26, 24); + break; + case 26: + HallOfRecordsSceneLink(27, 25); + break; + case 27: + HallOfRecordsSceneLink(28, 26); + break; + case 28: + HallOfRecordsSceneLink(29, 27); + break; + case 29: + HallOfRecordsSceneLink(30, 28); + break; + case 30: + HallOfRecordsSceneLink(31, 29); + break; + case 31: + HallOfRecordsSceneLink(32, 30); + break; + case 32: + HallOfRecordsSceneLink(33, 31); + break; + case 33: + HallOfRecordsSceneLink(34, 32); + break; + case 34: + HallOfRecordsSceneLink(42, 33); + break; + case 35: + HallOfRecordsSceneLink(36, 45); + break; + case 36: + HallOfRecordsSceneLink(37, 35); + break; + case 37: + HallOfRecordsSceneLink(38, 36); + break; + case 38: + HallOfRecordsSceneLink(39, 37); + break; + case 39: + HallOfRecordsSceneLink(40, 38); + break; + case 40: + HallOfRecordsSceneLink(41, 39); + break; + case 41: + HallOfRecordsSceneLink(47, 40); + break; + case 42: + HallOfRecordsSceneLink(43, 34); + break; + case 43: + HallOfRecordsSceneLink(44, 42); + break; + case 44: + HallOfRecordsSceneLink(45, 43); + break; + case 45: + HallOfRecordsSceneLink(35, 44); + break; + case 46: + HallOfRecordsSceneLink(9, 5); + break; + case 47: + createScene(41, 1); + break; + } + } +} + +#undef HallOfRecordsSceneLink + +void Module2200::createHallOfRecordsScene(int which, uint32 hallOfRecordsInfoId) { + _childObject = new HallOfRecordsScene(_vm, this, which, hallOfRecordsInfoId); +} + +// Scene2201 + +AsScene2201CeilingFan::AsScene2201CeilingFan(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + _x = 403; + _y = 259; + createSurface(100, 233, 96); + startAnimation(0x8600866, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); +} + +AsScene2201Door::AsScene2201Door(NeverhoodEngine *vm, Klaymen *klaymen, Sprite *ssDoorLight, bool isOpen) + : AnimatedSprite(vm, 1100), _klaymen(klaymen), _ssDoorLight(ssDoorLight), _countdown(0), _isOpen(isOpen) { + + _x = 408; + _y = 290; + createSurface(900, 63, 266); + SetUpdateHandler(&AsScene2201Door::update); + SetMessageHandler(&AsScene2201Door::handleMessage); + if (_isOpen) { + startAnimation(0xE2CB0412, -1, -1); + _countdown = 48; + _newStickFrameIndex = STICK_LAST_FRAME; + } else { + startAnimation(0xE2CB0412, 0, -1); + _newStickFrameIndex = 0; + _ssDoorLight->setVisible(false); + } +} + +void AsScene2201Door::update() { + if (_countdown != 0 && _isOpen && (--_countdown == 0)) + stCloseDoor(); + AnimatedSprite::update(); +} + +uint32 AsScene2201Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x11001090) { + if (_isOpen) + _ssDoorLight->setVisible(true); + } else if (param.asInteger() == 0x11283090) { + if (!_isOpen) + _ssDoorLight->setVisible(false); + } + break; + case 0x2000: + if (_isOpen) + _countdown = 144; + messageResult = _isOpen ? 1 : 0; + break; + case 0x3002: + gotoNextState(); + break; + case 0x4808: + _countdown = 144; + if (!_isOpen) + stOpenDoor(); + break; + } + return messageResult; +} + +void AsScene2201Door::stOpenDoor() { + _isOpen = true; + startAnimation(0xE2CB0412, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + playSound(0, calcHash("fxDoorOpen33")); +} + +void AsScene2201Door::stCloseDoor() { + _isOpen = false; + startAnimation(0xE2CB0412, -1, -1); + _playBackwards = true; + _newStickFrameIndex = 0; + playSound(0, calcHash("fxDoorClose33")); +} + +SsScene2201PuzzleCube::SsScene2201PuzzleCube(NeverhoodEngine *vm, uint32 positionIndex, uint32 cubeIndex) + : StaticSprite(vm, 900) { + + createSurface(100, 16, 16); + loadSprite(kSsScene2201PuzzleCubeFileHashes[cubeIndex], kSLFCenteredDrawOffset | kSLFSetPosition, 0, + kSsScene2201PuzzleCubePoints[positionIndex].x, kSsScene2201PuzzleCubePoints[positionIndex].y); +} + +Scene2201::Scene2201(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isSoundPlaying(false) { + + Sprite *tempSprite; + + _vm->gameModule()->initCubeSymbolsPuzzle(); + + SetMessageHandler(&Scene2201::handleMessage); + SetUpdateHandler(&Scene2201::update); + + loadDataResource(0x04104242); + loadHitRectList(); + setBackground(0x40008208); + setPalette(0x40008208); + insertScreenMouse(0x0820C408); + + _asTape = insertSprite<AsScene1201Tape>(this, 7, 1100, 459, 432, 0x9148A011); + addCollisionSprite(_asTape); + _ssDoorButton = insertSprite<SsCommonPressButton>(this, 0xE4A43E29, 0xE4A43E29, 100, 0); + + for (uint32 cubeIndex = 0; cubeIndex < 9; cubeIndex++) + if ((int16)getSubVar(VA_CUBE_POSITIONS, cubeIndex) >= 0) + insertSprite<SsScene2201PuzzleCube>(cubeIndex, (int16)getSubVar(VA_CUBE_POSITIONS, cubeIndex)); + + _clipRects[0].y1 = 0; + _clipRects[0].x2 = 640; + _clipRects[1].x2 = 640; + _clipRects[1].y2 = 480; + + if (!getGlobalVar(V_TILE_PUZZLE_SOLVED)) + insertStaticSprite(0x00026027, 900); + + tempSprite = insertStaticSprite(0x030326A0, 1100); + _clipRects[0].x1 = tempSprite->getDrawRect().x; + insertStaticSprite(0x811DA061, 1100); + tempSprite = insertStaticSprite(0x11180022, 1100); + _clipRects[1].x1 = tempSprite->getDrawRect().x; + tempSprite = insertStaticSprite(0x0D411130, 1100); + _clipRects[0].y2 = tempSprite->getDrawRect().y2(); + _clipRects[1].y1 = tempSprite->getDrawRect().y2(); + _ssDoorLight = insertStaticSprite(0xA4062212, 900); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2201>(300, 427, _clipRects, 2); + setMessageList(0x004B8118); + _asDoor = insertSprite<AsScene2201Door>(_klaymen, _ssDoorLight, false); + } else if (which == 1) { + // Klaymen entering from the back + insertKlaymen<KmScene2201>(412, 393, _clipRects, 2); + setMessageList(0x004B8130); + _asDoor = insertSprite<AsScene2201Door>(_klaymen, _ssDoorLight, false); + } else if (which == 2) { + // Klaymen returning from the puzzle + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene2201>(379, 427, _clipRects, 2); + _klaymen->setDoDeltaX(1); + } else + insertKlaymen<KmScene2201>(261, 427, _clipRects, 2); + setMessageList(0x004B8178); + _asDoor = insertSprite<AsScene2201Door>(_klaymen, _ssDoorLight, false); + } else { + // Klaymen entering from the left + NPoint pt = _dataResource.getPoint(0x0304D8DC); + insertKlaymen<KmScene2201>(pt.x, pt.y, _clipRects, 2); + setMessageList(0x004B8120); + _asDoor = insertSprite<AsScene2201Door>(_klaymen, _ssDoorLight, true); + } + + insertSprite<AsScene2201CeilingFan>(); + + _vm->_soundMan->addSound(0x04106220, 0x81212040); + +} + +Scene2201::~Scene2201() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); + _vm->_soundMan->deleteSoundGroup(0x04106220); +} + +void Scene2201::update() { + Scene::update(); + if (!_isSoundPlaying) { + _vm->_soundMan->playSoundLooping(0x81212040); + _isSoundPlaying = true; + } +} + +uint32 Scene2201::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssDoorButton); + else if (param.asInteger() == 0x35803198) { + if (sendMessage(_asDoor, 0x2000, 0)) + setMessageList(0x004B81A0); + else + setMessageList(0x004B81B8); + } else if (param.asInteger() == 0x51445010) { + if (getGlobalVar(V_TILE_PUZZLE_SOLVED)) + setMessageList(0x004B8108); + else + setMessageList(0x004B8150); + } else if (param.asInteger() == 0x1D203082) + setMessageList(0x004B8180); + else if (param.asInteger() == 0x00049091) { + if (getGlobalVar(V_TILE_PUZZLE_SOLVED)) + setMessageList(0x004B8138); + else + setMessageList(0x004B8108); + } + break; + case 0x480B: + if (sender == _ssDoorButton) + sendMessage(_asDoor, 0x4808, 0); + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B81C8); + } + break; + } + return 0; +} + +static const NPoint kSsScene2202PuzzleCubePoints[] = { + {196, 105}, {323, 102}, {445, 106}, + {192, 216}, {319, 220}, {446, 216}, + {188, 320}, {319, 319}, {443, 322} +}; + +static const uint32 kSsScene2202PuzzleCubeFileHashes1[] = { + 0xA500800C, 0x2182910C, 0x2323980C, + 0x23049084, 0x21008080, 0x2303900C, + 0x6120980C, 0x2504D808 +}; + +static const uint32 kSsScene2202PuzzleCubeFileHashes2[] = { + 0x0AAD8080, 0x0A290291, 0x0A2BA398, + 0x822B8490, 0x86298080, 0x0A2B8390, + 0x0A69A098, 0x0E2D84D8 +}; + +SsScene2202PuzzleCube::SsScene2202PuzzleCube(NeverhoodEngine *vm, Scene *parentScene, int16 cubePosition, int16 cubeSymbol) + : StaticSprite(vm, 900), _parentScene(parentScene), _cubeSymbol(cubeSymbol), _cubePosition(cubePosition), _isMoving(false) { + + int surfacePriority; + + SetUpdateHandler(&SsScene2202PuzzleCube::update); + SetMessageHandler(&SsScene2202PuzzleCube::handleMessage); + if (_cubePosition >= 0 && _cubePosition <= 2) + surfacePriority = 100; + else if (_cubePosition >= 3 && _cubePosition <= 5) + surfacePriority = 300; + else + surfacePriority = 500; + debug(1, "TODO: Unused SurfacePriority: %d", surfacePriority); + loadSprite(kSsScene2202PuzzleCubeFileHashes2[_cubeSymbol], kSLFCenteredDrawOffset | kSLFSetPosition | kSLFDefCollisionBoundsOffset, 0, + kSsScene2202PuzzleCubePoints[_cubePosition].x, kSsScene2202PuzzleCubePoints[_cubePosition].y); + loadSound(0, 0x40958621); + loadSound(1, 0x51108241); +} + +void SsScene2202PuzzleCube::update() { + handleSpriteUpdate(); + updatePosition(); +} + +uint32 SsScene2202PuzzleCube::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_isMoving && !getGlobalVar(V_TILE_PUZZLE_SOLVED)) + sendMessage(_parentScene, 0x2000, _cubePosition); + messageResult = 1; + break; + case 0x2001: + _isMoving = true; + moveCube(param.asInteger()); + break; + } + return messageResult; +} + +void SsScene2202PuzzleCube::suMoveCubeX() { + + bool done = false; + + if (_counterDirection) { + if (_counter > 2) + _counter -= 2; + } else { + if (_counter < 20) + _counter += 2; + } + + for (int16 i = 0; i < _counter; i++) { + _x += _xIncr; + _errValue += _yDelta; + if (_errValue >= _xDelta) { + _errValue -= _xDelta; + _y += _yIncr; + } + if (_x == _newX && _y == _newY) { + done = true; + break; + } + if (_x == _xFlagPos) + _counterDirection = true; + } + + if (done) + stopMoving(); + + updateBounds(); + +} + +void SsScene2202PuzzleCube::suMoveCubeY() { + + bool done = false; + + if (_counterDirection) { + if (_counter > 2) + _counter -= 2; + } else { + if (_counter < 20) + _counter += 2; + } + + for (int16 i = 0; i < _counter; i++) { + _y += _yIncr; + _errValue += _xDelta; + if (_errValue >= _yDelta) { + _errValue -= _yDelta; + _x += _xIncr; + } + if (_x == _newX && _y == _newY) { + done = true; + break; + } + if (_x == _xFlagPos) + _counterDirection = true; + } + + if (done) + stopMoving(); + + updateBounds(); + +} + +void SsScene2202PuzzleCube::moveCube(int16 newCubePosition) { + + loadSprite(kSsScene2202PuzzleCubeFileHashes1[_cubeSymbol], kSLFCenteredDrawOffset); + + setSubVar(VA_CUBE_POSITIONS, _cubePosition, (uint32)-1); + setSubVar(VA_CUBE_POSITIONS, newCubePosition, (uint32)_cubeSymbol); + + _cubePosition = newCubePosition; + _errValue = 0; + _counterDirection = false; + _counter = 0; + _newX = kSsScene2202PuzzleCubePoints[newCubePosition].x; + _newY = kSsScene2202PuzzleCubePoints[newCubePosition].y; + + if (_x == _newX && _y == _newY) + return; + + if (_x <= _newX) { + if (_y <= _newY) { + _xDelta = _newX - _x; + _yDelta = _newY - _y; + _xIncr = 1; + _yIncr = 1; + } else { + _xDelta = _newX - _x; + _yDelta = _y - _newY; + _xIncr = 1; + _yIncr = -1; + } + } else { + if (_y <= _newY) { + _xDelta = _x - _newX; + _yDelta = _newY - _y; + _xIncr = -1; + _yIncr = 1; + } else { + _xDelta = _x - _newX; + _yDelta = _y - _newY; + _xIncr = -1; + _yIncr = -1; + } + } + + if (_xDelta > _yDelta) { + SetSpriteUpdate(&SsScene2202PuzzleCube::suMoveCubeX); + if (_xIncr > 0) { + if (_newX - _x >= 180) + _xFlagPos = _newX - 90; + else + _xFlagPos = _x + _newX / 2; + } else { + if (_x - _newX >= 180) + _xFlagPos = _x + 90; + else + _xFlagPos = _x / 2 + _newX; + } + playSound(0); + } else { + SetSpriteUpdate(&SsScene2202PuzzleCube::suMoveCubeY); + if (_yIncr > 0) { + if (_newY - _y >= 180) + _xFlagPos = _newY - 90; + else + _xFlagPos = _y + _newY / 2; + } else { + if (_y - _newY >= 180) + _xFlagPos = _y + 90; + else + _xFlagPos = _y / 2 + _newY; + } + playSound(1); + } + +} + +void SsScene2202PuzzleCube::stopMoving() { + loadSprite(kSsScene2202PuzzleCubeFileHashes2[_cubeSymbol], kSLFCenteredDrawOffset); + SetSpriteUpdate(NULL); + _isMoving = false; + sendMessage(_parentScene, 0x2002, _cubePosition); +} + +Scene2202::Scene2202(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isSolved(false), _leaveScene(false), _isCubeMoving(false), + _ssMovingCube(NULL), _ssDoneMovingCube(NULL) { + + _vm->gameModule()->initCubeSymbolsPuzzle(); + + SetMessageHandler(&Scene2202::handleMessage); + SetUpdateHandler(&Scene2202::update); + + setBackground(0x08100A0C); + setPalette(0x08100A0C); + addEntity(_palette); + insertPuzzleMouse(0x00A08089, 20, 620); + + for (uint32 cubePosition = 0; cubePosition < 9; cubePosition++) { + int16 cubeSymbol = (int16)getSubVar(VA_CUBE_POSITIONS, cubePosition); + if (cubeSymbol >= 0) { + Sprite *puzzleCubeSprite = insertSprite<SsScene2202PuzzleCube>(this, cubePosition, cubeSymbol); + addCollisionSprite(puzzleCubeSprite); + } + } + + insertStaticSprite(0x55C043B8, 200); + insertStaticSprite(0x85500158, 400); + insertStaticSprite(0x25547028, 600); + + loadSound(0, 0x68E25540); + loadSound(1, 0x40400457); + + _vm->_soundMan->addSound(0x60400854, 0x8101A241); + _vm->_soundMan->playSoundLooping(0x8101A241); + +} + +Scene2202::~Scene2202() { + _vm->_soundMan->deleteSoundGroup(0x60400854); +} + +void Scene2202::update() { + Scene::update(); + + if (_leaveScene && !isSoundPlaying(1)) + leaveScene(0); + + if (_isSolved && !isSoundPlaying(0)) { + playSound(1); + _isSolved = false; + _leaveScene = true; + } + + if (_ssMovingCube && !_isCubeMoving) { + int16 freeCubePosition = getFreeCubePosition(_movingCubePosition); + if (freeCubePosition != -1) { + setSurfacePriority(_ssMovingCube->getSurface(), 700); + sendMessage(_ssMovingCube, 0x2001, freeCubePosition); + _ssMovingCube = NULL; + _isCubeMoving = true; + } + } + + if (_ssDoneMovingCube) { + setSurfacePriority(_ssDoneMovingCube->getSurface(), _surfacePriority); + _ssDoneMovingCube = NULL; + if (testIsSolved()) { + playSound(0); + setGlobalVar(V_TILE_PUZZLE_SOLVED, 1); + _isSolved = true; + } + } + +} + +uint32 Scene2202::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) + leaveScene(0); + break; + case 0x2000: + _movingCubePosition = (int16)param.asInteger(); + _ssMovingCube = (Sprite*)sender; + break; + case 0x2002: + _isCubeMoving = false; + _ssDoneMovingCube = (Sprite*)sender; + if (param.asInteger() <= 2) + _surfacePriority = 100; + else if (param.asInteger() >= 3 && param.asInteger() <= 5) + _surfacePriority = 300; + else + _surfacePriority = 500; + break; + } + return 0; +} + +int16 Scene2202::getFreeCubePosition(int16 cubePosition) { + if (cubePosition >= 3 && (int16)getSubVar(VA_CUBE_POSITIONS, cubePosition - 3) == -1) + return cubePosition - 3; + else if (cubePosition <= 5 && (int16)getSubVar(VA_CUBE_POSITIONS, cubePosition + 3) == -1) + return cubePosition + 3; + else if (cubePosition != 0 && cubePosition != 3 && cubePosition != 6 && (int16)getSubVar(VA_CUBE_POSITIONS, cubePosition - 1) == -1) + return cubePosition - 1; + else if (cubePosition != 2 && cubePosition != 5 && cubePosition != 8 && (int16)getSubVar(VA_CUBE_POSITIONS, cubePosition + 1) == -1) + return cubePosition + 1; + else + return -1; +} + +bool Scene2202::testIsSolved() { + return + getSubVar(VA_CUBE_POSITIONS, 0) == 0 && + getSubVar(VA_CUBE_POSITIONS, 2) == 2 && + getSubVar(VA_CUBE_POSITIONS, 3) == 3 && + getSubVar(VA_CUBE_POSITIONS, 4) == 4 && + getSubVar(VA_CUBE_POSITIONS, 5) == 5 && + getSubVar(VA_CUBE_POSITIONS, 6) == 6 && + getSubVar(VA_CUBE_POSITIONS, 8) == 7; +} + +static const uint32 kAsCommonKeyFileHashes[] = { + 0x2450D850, 0x0C9CE8D0, 0x2C58A152 +}; + +AsCommonKey::AsCommonKey(NeverhoodEngine *vm, Scene *parentScene, int keyIndex, int surfacePriority, int16 x, int16 y) + : AnimatedSprite(vm, kAsCommonKeyFileHashes[keyIndex], surfacePriority, x, y), _parentScene(parentScene), _keyIndex(keyIndex) { + + if (!getSubVar(VA_HAS_KEY, _keyIndex) && !getSubVar(VA_IS_KEY_INSERTED, _keyIndex)) { + SetMessageHandler(&AsCommonKey::handleMessage); + } else { + // If Klaymen already has the key or it's already inserted then don't show it + setVisible(false); + SetMessageHandler(NULL); + } +} + +uint32 AsCommonKey::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4806: + setSubVar(VA_HAS_KEY, _keyIndex, 1); + setVisible(false); + SetMessageHandler(NULL); + } + return messageResult; +} + +static const uint32 kAsScene2203DoorFileHashes[] = { + 0x7868AE10, 0x1A488110 +}; + +AsScene2203Door::AsScene2203Door(NeverhoodEngine *vm, Scene *parentScene, uint doorIndex) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _doorIndex(doorIndex) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2203Door::handleMessage); + _x = 320; + _y = 240; + createSurface1(kAsScene2203DoorFileHashes[_doorIndex], 900); + if (getGlobalVar(V_LARGE_DOOR_NUMBER) == _doorIndex) { + startAnimation(kAsScene2203DoorFileHashes[_doorIndex], -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + } else { + startAnimation(kAsScene2203DoorFileHashes[_doorIndex], 0, -1); + _newStickFrameIndex = 0; + } +} + +uint32 AsScene2203Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_doorIndex == getGlobalVar(V_LARGE_DOOR_NUMBER)) + sendMessage(_parentScene, 0x2002, 0); + else + sendMessage(_parentScene, 0x2001, 0); + messageResult = 1; + break; + case 0x2000: + _otherDoor = (Sprite*)param.asEntity(); + break; + case 0x3002: + if (_doorIndex == getGlobalVar(V_LARGE_DOOR_NUMBER)) + sendMessage(_parentScene, 0x4808, 0); + stopAnimation(); + break; + case 0x4808: + setGlobalVar(V_LARGE_DOOR_NUMBER, _doorIndex); + sendMessage(_otherDoor, 0x4809, 0); + openDoor(); + break; + case 0x4809: + closeDoor(); + sendMessage(_parentScene, 0x2003, 0); + break; + } + return messageResult; +} + +void AsScene2203Door::openDoor() { + playSound(0, 0x341014C4); + startAnimation(kAsScene2203DoorFileHashes[_doorIndex], 1, -1); +} + +void AsScene2203Door::closeDoor() { + startAnimation(kAsScene2203DoorFileHashes[_doorIndex], -1, -1); + _playBackwards = true; + _newStickFrameIndex = 0; +} + +Scene2203::Scene2203(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + if (getGlobalVar(V_HAS_FINAL_KEY) && getGlobalVar(V_KEY3_LOCATION) == 0) + setGlobalVar(V_KEY3_LOCATION, 1); + + SetMessageHandler(&Scene2203::handleMessage); + + setBackground(0x82C80334); + setPalette(0x82C80334); + insertScreenMouse(0x80330824); + setHitRects(0x004B8320); + setRectList(0x004B8420); + + if (getGlobalVar(V_KEY3_LOCATION) == 1) { + _asKey = insertSprite<AsCommonKey>(this, 2, 1100, 282, 432); + addCollisionSprite(_asKey); + } + + _asTape = insertSprite<AsScene1201Tape>(this, 1, 1100, 435, 432, 0x9148A011); + addCollisionSprite(_asTape); + _asLeftDoor = insertSprite<AsScene2203Door>(this, 0); + _asRightDoor = insertSprite<AsScene2203Door>(this, 1); + _ssSmallLeftDoor = insertStaticSprite(0x542CC072, 1100); + _ssSmallRightDoor = insertStaticSprite(0x0A2C0432, 1100); + _leftDoorClipRect.set(_ssSmallLeftDoor->getDrawRect().x, 0, 640, 480); + _rightDoorClipRect.set(0, 0, _ssSmallRightDoor->getDrawRect().x2(), 480); + sendEntityMessage(_asLeftDoor, 0x2000, _asRightDoor); + sendEntityMessage(_asRightDoor, 0x2000, _asLeftDoor); + addCollisionSprite(_asLeftDoor); + addCollisionSprite(_asRightDoor); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2203>(200, 427); + setMessageList(0x004B8340); + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene2203>(640, 427); + setMessageList(0x004B8350); + } else if (which == 2) { + // Klaymen returning from the displayer + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene2203>(362, 427); + _klaymen->setDoDeltaX(1); + } else + insertKlaymen<KmScene2203>(202, 427); + setMessageList(0x004B8358); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene2203>(0, 427); + setMessageList(0x004B8348); + } + + if (getGlobalVar(V_LARGE_DOOR_NUMBER)) { + _ssSmallLeftDoor->setVisible(false); + _klaymen->setClipRect(_rightDoorClipRect); + } else { + _ssSmallRightDoor->setVisible(false); + _klaymen->setClipRect(_leftDoorClipRect); + } + +} + +Scene2203::~Scene2203() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene2203::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + sendEntityMessage(_klaymen, 0x1014, sender); + if (sender == _asLeftDoor) + setMessageList2(0x004B83B0); + else + setMessageList2(0x004B83C8); + break; + case 0x2002: + if (sender == _asLeftDoor) + setMessageList2(0x004B8370); + else + setMessageList2(0x004B8360); + break; + case 0x2003: + if (sender == _asLeftDoor) + _ssSmallLeftDoor->setVisible(false); + else + _ssSmallRightDoor->setVisible(false); + break; + case 0x4808: + if (sender == _asLeftDoor) { + _ssSmallLeftDoor->setVisible(true); + _klaymen->setClipRect(_leftDoorClipRect); + } else { + _ssSmallRightDoor->setVisible(true); + _klaymen->setClipRect(_rightDoorClipRect); + } + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B83E0); + } else if (sender == _asKey) { + sendEntityMessage(_klaymen, 0x1014, _asKey); + setMessageList(0x004B83F0); + } + break; + } + return messageResult; +} + +SsScene2205DoorFrame::SsScene2205DoorFrame(NeverhoodEngine *vm) + : StaticSprite(vm, 900) { + + SetMessageHandler(&SsScene2205DoorFrame::handleMessage); + createSurface(1100, 45, 206); + loadSprite(getGlobalVar(V_LIGHTS_ON) ? 0x24306227 : 0xD90032A0, kSLFDefDrawOffset | kSLFDefPosition); +} + +uint32 SsScene2205DoorFrame::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + loadSprite(getGlobalVar(V_LIGHTS_ON) ? 0x24306227 : 0xD90032A0, kSLFDefDrawOffset | kSLFDefPosition); + break; + } + return messageResult; +} + +Scene2205::Scene2205(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene2205::handleMessage); + SetUpdateHandler(&Scene2205::update); + + setHitRects(0x004B0620); + if (getGlobalVar(V_LIGHTS_ON)) { + _isLightOn = true; + setBackground(0x0008028D); + setPalette(0x0008028D); + addEntity(_palette); + insertScreenMouse(0x80289008); + _ssLightSwitch = insertSprite<SsCommonPressButton>(this, 0x2D339030, 0x2D309030, 100, 0); + } else { + _isLightOn = false; + setBackground(0xD00A028D); + setPalette(0xD00A028D); + addEntity(_palette); + insertScreenMouse(0xA0289D08); + _ssLightSwitch = insertSprite<SsCommonPressButton>(this, 0x2D339030, 0xDAC86E84, 100, 0); + } + _palette->addBasePalette(0xD00A028D, 0, 256, 0); + _ssDoorFrame = insertSprite<SsScene2205DoorFrame>(); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2205>(320, 417); + setMessageList(0x004B0658); + if (!getGlobalVar(V_LIGHTS_ON)) + _palette->addPalette(0x68033B1C, 0, 65, 0); + _isKlaymenInLight = false; + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene2205>(640, 417); + setMessageList(0x004B0648); + if (!getGlobalVar(V_LIGHTS_ON)) + _palette->addPalette(0x68033B1C, 0, 65, 0); + _isKlaymenInLight = false; + } else { + // Klaymen entering from the left + insertKlaymen<KmScene2205>(0, 417); + setMessageList(0x004B0640); + _isKlaymenInLight = true; + } + + _klaymen->setClipRect(_ssDoorFrame->getDrawRect().x, 0, 640, 480); + _klaymen->setSoundFlag(true); + + loadDataResource(0x00144822); + +} + +void Scene2205::update() { + Scene::update(); + if (!_isLightOn && getGlobalVar(V_LIGHTS_ON)) { + _palette->addPalette(0x0008028D, 0, 256, 0); + changeBackground(0x0008028D); + _ssLightSwitch->setFileHashes(0x2D339030, 0x2D309030); + sendMessage(_ssDoorFrame, 0x2000, 0); + changeMouseCursor(0x80289008); + _isLightOn = true; + } else if (_isLightOn && !getGlobalVar(V_LIGHTS_ON)) { + _palette->addPalette(0xD00A028D, 0, 256, 0); + changeBackground(0xD00A028D); + _ssLightSwitch->setFileHashes(0x2D339030, 0xDAC86E84); + sendMessage(_ssDoorFrame, 0x2000, 0); + changeMouseCursor(0xA0289D08); + _isKlaymenInLight = true; + if (_klaymen->getX() > 85) { + _palette->addPalette(0x68033B1C, 0, 65, 0); + _isKlaymenInLight = false; + } + _isLightOn = false; + } + if (!getGlobalVar(V_LIGHTS_ON)) { + if (_isKlaymenInLight && _klaymen->getX() > 85) { + _palette->addBasePalette(0x68033B1C, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymenInLight = false; + } else if (!_isKlaymenInLight && _klaymen->getX() <= 85) { + _palette->addBasePalette(0xD00A028D, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymenInLight = true; + } + } +} + +uint32 Scene2205::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x6449569A) + setMessageList(0x004B0690); + else if (param.asInteger() == 0x2841369C) + setMessageList(0x004B0630); + else if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssLightSwitch); + break; + case 0x480B: + setGlobalVar(V_LIGHTS_ON, getGlobalVar(V_LIGHTS_ON) ? 0 : 1); + break; + } + return 0; +} + +static const int16 kScene2206XPositions[] = { + 384, 480, 572 +}; + +static const uint32 kScene2206MessageIds1[] = { + 0x004B8998, 0x004B89B8, 0x004B89D8 +}; + +static const uint32 kScene2206MessageIds2[] = { + 0x004B89F8, 0x004B8A20, 0x004B8A48 +}; + +static const int16 kAsScene2206DoorSpikesXDeltasOpen[] = { + -24, -28, -18, 6, 9, -8 +}; + +static const int16 kAsScene2206DoorSpikesXDeltasClose[] = { + -8, 7, 11, 26, 13, 14 +}; + +AsScene2206DoorSpikes::AsScene2206DoorSpikes(NeverhoodEngine *vm, uint32 fileHash) + : StaticSprite(vm, fileHash, 200) { + + if (getGlobalVar(V_SPIKES_RETRACTED)) + _x -= 63; + SetUpdateHandler(&AsScene2206DoorSpikes::update); + SetMessageHandler(&AsScene2206DoorSpikes::handleMessage); + SetSpriteUpdate(NULL); +} + +void AsScene2206DoorSpikes::update() { + handleSpriteUpdate(); + updatePosition(); +} + +uint32 AsScene2206DoorSpikes::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + _deltaIndex = 0; + playSound(0, 0x032746E0); + SetMessageHandler(NULL); + SetSpriteUpdate(&AsScene2206DoorSpikes::suOpen); + break; + case 0x4809: + _deltaIndex = 0; + playSound(0, 0x002642C0); + SetMessageHandler(NULL); + SetSpriteUpdate(&AsScene2206DoorSpikes::suClose); + break; + } + return messageResult; +} + +void AsScene2206DoorSpikes::suOpen() { + if (_deltaIndex < 6) { + _x += kAsScene2206DoorSpikesXDeltasOpen[_deltaIndex]; + _deltaIndex++; + } else { + SetMessageHandler(&AsScene2206DoorSpikes::handleMessage); + SetSpriteUpdate(NULL); + } +} + +void AsScene2206DoorSpikes::suClose() { + if (_deltaIndex < 6) { + _x += kAsScene2206DoorSpikesXDeltasClose[_deltaIndex]; + _deltaIndex++; + } else { + SetMessageHandler(&AsScene2206DoorSpikes::handleMessage); + SetSpriteUpdate(NULL); + } +} + +AsScene2206Platform::AsScene2206Platform(NeverhoodEngine *vm, uint32 fileHash) + : StaticSprite(vm, fileHash, 50) { + + SetUpdateHandler(&AsScene2206Platform::update); + SetMessageHandler(&AsScene2206Platform::handleMessage); + SetSpriteUpdate(NULL); +} + +void AsScene2206Platform::update() { + handleSpriteUpdate(); + updatePosition(); +} + +uint32 AsScene2206Platform::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4803: + _yDelta = 0; + SetMessageHandler(NULL); + SetSpriteUpdate(&AsScene2206Platform::suMoveDown); + break; + } + return messageResult; +} + +void AsScene2206Platform::suMoveDown() { + _yDelta++; + _y += _yDelta; +} + +SsScene2206TestTube::SsScene2206TestTube(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, uint32 fileHash) + : StaticSprite(vm, fileHash, surfacePriority), _parentScene(parentScene) { + + if (getGlobalVar(V_HAS_TEST_TUBE)) { + setVisible(false); + SetMessageHandler(NULL); + } else + SetMessageHandler(&SsScene2206TestTube::handleMessage); + _collisionBoundsOffset = _drawOffset; + updateBounds(); +} + +uint32 SsScene2206TestTube::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4806: + setGlobalVar(V_HAS_TEST_TUBE, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +Scene2206::Scene2206(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + uint32 fileHash; + + SetUpdateHandler(&Scene::update); + SetMessageHandler(&Scene2206::handleMessage); + + if (getGlobalVar(V_LIGHTS_ON)) { + fileHash = 0x41983216; + _sprite1 = insertStaticSprite(0x2201266A, 100); + _sprite2 = insertStaticSprite(0x3406A333, 300); + _sprite3 = insertStaticSprite(0x24A223A2, 100); + _asDoorSpikes = insertSprite<AsScene2206DoorSpikes>(0x26133023); + _asDoorSpikes->setClipRect(_sprite2->getDrawRect().x, 0, 640, 480); + setRectList(0x004B8AF8); + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x0E038022, 100, 0); + insertScreenMouse(0x83212411); + _ssTestTube = insertSprite<SsScene2206TestTube>(this, 1100, /*464, 433, */0x5E00E262); + _asPlatform = insertSprite<AsScene2206Platform>(0x085E25E0); + } else { + fileHash = 0xE0102A45; + _sprite1 = insertStaticSprite(0x1C1106B8, 100); + _sprite2 = insertStaticSprite(0x020462E0, 300); + _sprite3 = insertStaticSprite(0x900626A2, 100); + _asDoorSpikes = insertSprite<AsScene2206DoorSpikes>(0x544822A8); + _asDoorSpikes->setClipRect(_sprite2->getDrawRect().x, 0, 640, 480); + setRectList(0x004B8B58); + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x16882608, 100, 0); + insertScreenMouse(0x02A41E09); + _ssTestTube = insertSprite<SsScene2206TestTube>(this, 1100, /*464, 433, */0x52032563); + _asPlatform = insertSprite<AsScene2206Platform>(0x317831A0); + } + + _asPlatform->setClipRect(_sprite2->getDrawRect().x, 0, _sprite3->getDrawRect().x2(), _sprite1->getDrawRect().y2()); + setBackground(fileHash); + setPalette(fileHash); + addEntity(_palette); + _palette->addBasePalette(fileHash, 0, 256, 0); + if (!getGlobalVar(V_LIGHTS_ON)) + _palette->addPalette(0x0263D144, 0, 65, 0); + addCollisionSprite(_ssTestTube); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2206>(200, 430); + setMessageList(0x004B88A8); + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene2206>(640, 430); + setMessageList(0x004B88B8); + } else if (which == 2) { + // Klaymen entering from the back + insertKlaymen<KmScene2206>(205, 396); + setMessageList(0x004B88C8); + _palette->addPalette(getGlobalVar(V_LIGHTS_ON) ? 0xB103B604 : 0x0263D144, 0, 65, 0); + klaymenBehindSpikes(); + playSound(0, 0x53B8284A); + } else if (which == 3) { + // Klaymen entering from reading a text column + insertKlaymen<KmScene2206>(kScene2206XPositions[getGlobalVar(V_CLICKED_COLUMN_INDEX)], 430); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) + _klaymen->setDoDeltaX(1); + setMessageList(0x004B8A70); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene2206>(0, 430); + setMessageList(0x004B88B0); + } + + _klaymen->setSoundFlag(true); + _klaymen->setKlaymenIdleTable2(); + +} + +Scene2206::~Scene2206() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene2206::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x800C6694) + readClickedColumn(); + else if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssButton); + else if (param.asInteger() == 0x11C40840) { + if (getGlobalVar(V_SPIKES_RETRACTED)) + setMessageList(0x004B8948); + else + setMessageList(0x004B8970); + } + break; + case 0x4803: + sendMessage(_asPlatform, 0x4803, 0); + break; + case 0x480B: + if (sender == _ssButton) { + setGlobalVar(V_SPIKES_RETRACTED, getGlobalVar(V_SPIKES_RETRACTED) ? 0 : 1); + if (getGlobalVar(V_SPIKES_RETRACTED)) + sendMessage(_asDoorSpikes, 0x4808, 0); + else + sendMessage(_asDoorSpikes, 0x4809, 0); + } + break; + case 0x4826: + sendEntityMessage(_klaymen, 0x1014, _ssTestTube); + setMessageList(0x004B8988); + break; + case 0x482A: + klaymenBehindSpikes(); + break; + case 0x482B: + klaymenInFrontSpikes(); + break; + } + return messageResult; +} + +void Scene2206::klaymenInFrontSpikes() { + if (getGlobalVar(V_LIGHTS_ON)) { + _palette->addBasePalette(0x41983216, 0, 65, 0); + _palette->startFadeToPalette(12); + } + setSurfacePriority(_sprite1->getSurface(), 100); + setSurfacePriority(_sprite2->getSurface(), 300); + setSurfacePriority(_sprite3->getSurface(), 100); + setSurfacePriority(_asDoorSpikes->getSurface(), 200); + _klaymen->setClipRect(0, 0, 640, 480); +} + +void Scene2206::klaymenBehindSpikes() { + if (getGlobalVar(V_LIGHTS_ON)) { + _palette->addBasePalette(0xB103B604, 0, 65, 0); + _palette->startFadeToPalette(12); + } + setSurfacePriority(_sprite1->getSurface(), 1100); + setSurfacePriority(_sprite2->getSurface(), 1300); + setSurfacePriority(_sprite3->getSurface(), 1100); + setSurfacePriority(_asDoorSpikes->getSurface(), 1200); + _klaymen->setClipRect(_sprite2->getDrawRect().x, 0, _sprite3->getDrawRect().x2(), _sprite1->getDrawRect().y2()); +} + +void Scene2206::readClickedColumn() { + setGlobalVar(V_CLICKED_COLUMN_INDEX, (_mouseClickPos.x - 354) / 96); + if (getGlobalVar(V_CLICKED_COLUMN_INDEX) > 2) + setGlobalVar(V_CLICKED_COLUMN_INDEX, 2); + setGlobalVar(V_CLICKED_COLUMN_ROW, (_mouseClickPos.y - 183) / 7); + setGlobalVar(V_COLUMN_TEXT_NAME, calcHash("stLineagex")); + setGlobalVar(V_COLUMN_BACK_NAME, 0); + if (ABS(kScene2206XPositions[getGlobalVar(V_CLICKED_COLUMN_INDEX)] - _klaymen->getX()) >= 144) + setMessageList2(kScene2206MessageIds1[getGlobalVar(V_CLICKED_COLUMN_INDEX)]); + else + setMessageList2(kScene2206MessageIds2[getGlobalVar(V_CLICKED_COLUMN_INDEX)]); +} + +static const uint32 kScene2207FileHashes[] = { + 0x33B1E12E, 0x33D1E12E, 0x3311E12E, + 0x3291E12E, 0x3191E12E, 0x3791E12E, + 0x3B91E12E, 0x2391E12E, 0x1391E12E, + 0x3BB1E12E, 0x23B1E12E, 0x13B1E12E +}; + +AsScene2207Elevator::AsScene2207Elevator(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 900), _parentScene(parentScene), _pointIndex(0), _destPointIndex(0), _destPointIndexDelta(0) { + + NPoint pt; + + _dataResource.load(0x00524846); + _pointArray = _dataResource.getPointArray(0x005B02B7); + pt = _dataResource.getPoint(0x403A82B1); + _x = pt.x; + _y = pt.y; + createSurface(1100, 129, 103); + startAnimation(getGlobalVar(V_LIGHTS_ON) ? 0xC858CC19 : 0x294B3377, 0, 0); + _newStickFrameIndex = 0; + SetUpdateHandler(&AsScene2207Elevator::update); + SetMessageHandler(&AsScene2207Elevator::handleMessage); + SetSpriteUpdate(&AsScene2207Elevator::suSetPosition); +} + +AsScene2207Elevator::~AsScene2207Elevator() { + _vm->_soundMan->deleteSoundGroup(0x02700413); +} + +void AsScene2207Elevator::update() { + + if (_destPointIndex + _destPointIndexDelta > _pointIndex) { + _pointIndex++; + startAnimation(getGlobalVar(V_LIGHTS_ON) ? 0xC858CC19 : 0x294B3377, _pointIndex, _pointIndex); + _newStickFrameIndex = _pointIndex; + if (_destPointIndex + _destPointIndexDelta == _pointIndex) { + if (_destPointIndexDelta != 0) + _destPointIndexDelta = 0; + else { + _vm->_soundMan->deleteSound(0xD3B02847); + playSound(0, 0x53B8284A); + } + } + } + + if (_destPointIndex + _destPointIndexDelta < _pointIndex) { + _pointIndex--; + if (_pointIndex == 0) + sendMessage(_parentScene, 0x2003, 0); + startAnimation(getGlobalVar(V_LIGHTS_ON) ? 0xC858CC19 : 0x294B3377, _pointIndex, _pointIndex); + _newStickFrameIndex = _pointIndex; + if (_destPointIndex + _destPointIndexDelta == _pointIndex) { + if (_destPointIndexDelta != 0) + _destPointIndexDelta = 0; + else { + _vm->_soundMan->deleteSound(0xD3B02847); + playSound(0, 0x53B8284A); + } + } + } + + if (_pointIndex > 20 && _surface->getPriority() != 900) + sendMessage(_parentScene, 0x2002, 900); + else if (_pointIndex < 20 && _surface->getPriority() != 1100) + sendMessage(_parentScene, 0x2002, 1100); + + AnimatedSprite::update(); + + if (_destPointIndex + _destPointIndexDelta == _pointIndex && _isMoving) { + sendMessage(_parentScene, 0x2004, 0); + _isMoving = false; + } + +} + +void AsScene2207Elevator::suSetPosition() { + _x = (*_pointArray)[_pointIndex].x; + _y = (*_pointArray)[_pointIndex].y - 60; + updateBounds(); +} + +uint32 AsScene2207Elevator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + moveToY(param.asInteger()); + break; + } + return messageResult; +} + +void AsScene2207Elevator::moveToY(int16 y) { + int16 minDistance = 480; + + if (!_pointArray || _pointArray->size() == 0) + return; + + for (uint i = 0; i < _pointArray->size(); i++) { + int16 distance = ABS(y - (*_pointArray)[i].y); + if (distance < minDistance) { + minDistance = distance; + _destPointIndex = i; + } + } + + if (_destPointIndex != _pointIndex) { + if (_destPointIndex == 0 || _destPointIndex == (int)_pointArray->size() - 1) + _destPointIndexDelta = 0; + else if (_destPointIndex < _pointIndex) + _destPointIndexDelta = -2; + else + _destPointIndexDelta = 2; + _vm->_soundMan->addSound(0x02700413, 0xD3B02847); + _vm->_soundMan->playSoundLooping(0xD3B02847); + } + + _isMoving = true; + +} + +AsScene2207Lever::AsScene2207Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int doDeltaX) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + _x = x; + _y = y; + createSurface(1010, 71, 73); + setDoDeltaX(doDeltaX); + startAnimation(0x80880090, 0, -1); + _newStickFrameIndex = 0; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2207Lever::handleMessage); +} + +uint32 AsScene2207Lever::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x3002: + gotoNextState(); + stopAnimation(); + break; + case 0x4807: + stLeverUp(); + break; + case 0x480F: + stLeverDown(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +void AsScene2207Lever::stLeverDown() { + startAnimation(0x80880090, 1, -1); + playSound(0, 0x40581882); + FinalizeState(&AsScene2207Lever::stLeverDownEvent); +} + +void AsScene2207Lever::stLeverDownEvent() { + sendMessage(_parentScene, 0x480F, 0); +} + +void AsScene2207Lever::stLeverUp() { + startAnimation(0x80880090, 6, -1); + _playBackwards = true; + playSound(0, 0x40581882); + FinalizeState(&AsScene2207Lever::stLeverUpEvent); +} + +void AsScene2207Lever::stLeverUpEvent() { + sendMessage(_parentScene, 0x4807, 0); +} + +AsScene2207WallRobotAnimation::AsScene2207WallRobotAnimation(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1200), _idle(true) { + + _x = 309; + _y = 320; + createSurface1(0xCCFD6090, 100); + startAnimation(0xCCFD6090, 0, -1); + _newStickFrameIndex = 0; + loadSound(1, 0x40330872); + loadSound(2, 0x72A2914A); + loadSound(3, 0xD4226080); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2207WallRobotAnimation::handleMessage); +} + +AsScene2207WallRobotAnimation::~AsScene2207WallRobotAnimation() { + _vm->_soundMan->deleteSoundGroup(0x80D00820); +} + +uint32 AsScene2207WallRobotAnimation::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (!_idle) { + if (param.asInteger() == 0x3423093) { + _vm->_soundMan->addSound(0x80D00820, 0x12121943); + _vm->_soundMan->playSoundLooping(0x12121943); + } else if (param.asInteger() == 0x834AB011) { + stopSound(0); + stopSound(1); + stopSound(2); + stopSound(3); + _vm->_soundMan->deleteSound(0x12121943); + } else if (param.asInteger() == 0x3A980501) + playSound(1); + else if (param.asInteger() == 0x2A2AD498) + playSound(2); + else if (param.asInteger() == 0xC4980008) + playSound(3); + else if (param.asInteger() == 0x06B84228) + playSound(0, 0xE0702146); + } + break; + case 0x2006: + stStartAnimation(); + break; + case 0x2007: + stStopAnimation(); + break; + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene2207WallRobotAnimation::stStartAnimation() { + if (!_idle) { + NextState(NULL); + } else { + startAnimation(0xCCFD6090, 0, -1); + _idle = false; + setVisible(true); + } +} + +void AsScene2207WallRobotAnimation::stStopAnimation() { + NextState(&AsScene2207WallRobotAnimation::cbStopAnimation); +} + +void AsScene2207WallRobotAnimation::cbStopAnimation() { + stopAnimation(); + stopSound(0); + stopSound(1); + stopSound(2); + stopSound(3); + _vm->_soundMan->deleteSound(0x12121943); + _idle = true; + setVisible(false); +} + +AsScene2207WallCannonAnimation::AsScene2207WallCannonAnimation(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200), _idle(true) { + + _x = 309; + _y = 320; + createSurface1(0x8CAA0099, 100); + startAnimation(0x8CAA0099, 0, -1); + _newStickFrameIndex = 0; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2207WallCannonAnimation::handleMessage); +} + +uint32 AsScene2207WallCannonAnimation::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2006: + stStartAnimation(); + break; + case 0x2007: + stStopAnimation(); + break; + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene2207WallCannonAnimation::stStartAnimation() { + if (!_idle) { + NextState(NULL); + } else { + setVisible(true); + startAnimation(0x8CAA0099, 0, -1); + _idle = false; + } +} + +void AsScene2207WallCannonAnimation::stStopAnimation() { + NextState(&AsScene2207WallCannonAnimation::cbStopAnimation); +} + +void AsScene2207WallCannonAnimation::cbStopAnimation() { + stopAnimation(); + setVisible(false); + _idle = true; +} + +SsScene2207Symbol::SsScene2207Symbol(NeverhoodEngine *vm, uint32 fileHash, int index) + : StaticSprite(vm, fileHash, 100) { + + _x = 330; + _y = 246 + index * 50; + updatePosition(); +} + +Scene2207::Scene2207(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _klaymenAtElevator(true), _elevatorSurfacePriority(0) { + + _vm->gameModule()->initCannonSymbolsPuzzle(); + + if (!getSubVar(VA_IS_PUZZLE_INIT, 0x88460852)) + setSubVar(VA_IS_PUZZLE_INIT, 0x88460852, 1); + + SetMessageHandler(&Scene2207::handleMessage); + SetUpdateHandler(&Scene2207::update); + + insertKlaymen<KmScene2207>(0, 0); + _klaymen->setRepl(64, 0); + setMessageList(0x004B38E8); + _asElevator = insertSprite<AsScene2207Elevator>(this); + + if (getGlobalVar(V_LIGHTS_ON)) { + setBackground(0x88C00241); + setPalette(0x88C00241); + insertScreenMouse(0x00245884); + _ssMaskPart1 = insertStaticSprite(0xE20A28A0, 1200); + _ssMaskPart2 = insertStaticSprite(0x688F62A5, 1100); + _ssMaskPart3 = insertStaticSprite(0x0043B038, 1100); + _asTape = insertSprite<AsScene1201Tape>(this, 4, 1100, 277, 428, 0x9148A011); + addCollisionSprite(_asTape); + _asLever = insertSprite<AsScene2207Lever>(this, 527, 333, 0); + addCollisionSprite(_asLever); + _asWallRobotAnimation = insertSprite<AsScene2207WallRobotAnimation>(this); + _asWallCannonAnimation = insertSprite<AsScene2207WallCannonAnimation>(); + _asWallRobotAnimation->setVisible(false); + _asWallCannonAnimation->setVisible(false); + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x2C4061C4, 100, 0); + _asLever->setClipRect(0, 0, _ssMaskPart3->getDrawRect().x2(), 480); + _klaymen->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, _ssMaskPart2->getDrawRect().y2()); + _asElevator->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, _ssMaskPart2->getDrawRect().y2()); + } else { + setGlobalVar(V_SEEN_SYMBOLS_NO_LIGHT, 1); + setBackground(0x05C02A55); + setPalette(0x05C02A55); + insertScreenMouse(0x02A51054); + _ssMaskPart1 = insertStaticSprite(0x980E46A4, 1200); + insertSprite<SsScene2207Symbol>(kScene2207FileHashes[getSubVar(VA_GOOD_CANNON_SYMBOLS_1, 0)], 0); + insertSprite<SsScene2207Symbol>(kScene2207FileHashes[getSubVar(VA_GOOD_CANNON_SYMBOLS_1, 1)], 1); + insertSprite<SsScene2207Symbol>(kScene2207FileHashes[getSubVar(VA_GOOD_CANNON_SYMBOLS_1, 2)], 2); + _asTape = NULL; + _asLever = NULL; + _asWallRobotAnimation = NULL; + _asWallCannonAnimation = NULL; + _ssButton = NULL; + _klaymen->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, 480); + _asElevator->setClipRect(0, _ssMaskPart1->getDrawRect().y, 640, 480); + } + + _dataResource.load(0x00524846); + setRectList(0x004B38B8); + + sendEntityMessage(_klaymen, 0x1014, _asElevator); + sendMessage(_klaymen, 0x2001, 0); + sendMessage(_asElevator, 0x2000, 480); + + loadSound(1, calcHash("fxFogHornSoft")); + +} + +void Scene2207::update() { + Scene::update(); + if (_elevatorSurfacePriority != 0) { + setSurfacePriority(_asElevator->getSurface(), _elevatorSurfacePriority); + _elevatorSurfacePriority = 0; + } + if (_klaymen->getY() == 423) + _klaymenAtElevator = _klaymen->getX() > 459 && _klaymen->getX() < 525; +} + +uint32 Scene2207::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0014F275) { + if (_klaymenAtElevator) { + sendMessage(_asElevator, 0x2000, _mouseClickPos.y); + sendEntityMessage(_klaymen, 0x1014, _asElevator); + sendMessage(_klaymen, 0x2001, 0); + } else + cancelMessageList(); + } else if (param.asInteger() == 0x34569073) { + if (_klaymenAtElevator) { + _isKlaymenBusy = true; + sendMessage(_asElevator, 0x2000, 0); + sendEntityMessage(_klaymen, 0x1014, _asElevator); + sendMessage(_klaymen, 0x2001, 0); + } else + cancelMessageList(); + } else if (param.asInteger() == 0x4054C877) { + if (_klaymenAtElevator) { + sendMessage(_asElevator, 0x2000, 480); + sendEntityMessage(_klaymen, 0x1014, _asElevator); + sendMessage(_klaymen, 0x2001, 0); + } else + cancelMessageList(); + } else if (param.asInteger() == 0x0CBC6211) { + sendEntityMessage(_klaymen, 0x1014, _asElevator); + sendMessage(_klaymen, 0x2001, 0); + setRectList(0x004B38B8); + } else if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssButton); + else if (param.asInteger() == 0x231DA241) { + if (_ssButton) + setMessageList(0x004B38F0); + else + setMessageList(0x004B37D8); + } + break; + case 0x2002: + _elevatorSurfacePriority = param.asInteger(); + break; + case 0x2003: + _isKlaymenBusy = false; + break; + case 0x4807: + sendMessage(_asWallRobotAnimation, 0x2007, 0); + sendMessage(_asWallCannonAnimation, 0x2007, 0); + break; + case 0x480B: + if (sender == _ssButton) { + if (getSubVar(VA_LOCKS_DISABLED, 0x40119852)) { + setSubVar(VA_LOCKS_DISABLED, 0x40119852, 0); + playSound(0, calcHash("fx3LocksDisable")); + } else { + setSubVar(VA_LOCKS_DISABLED, 0x40119852, 1); + playSound(1); + } + } + break; + case 0x480F: + sendMessage(_asWallRobotAnimation, 0x2006, 0); + sendMessage(_asWallCannonAnimation, 0x2006, 0); + _asWallRobotAnimation->setVisible(true); + _asWallCannonAnimation->setVisible(true); + break; + case 0x4826: + if (sender == _asTape) { + if (_klaymen->getY() == 423) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B3958); + } + } else if (_klaymenAtElevator) { + SetMessageHandler(&Scene2207::handleMessage2); + sendMessage(_asElevator, 0x2000, 347); + sendEntityMessage(_klaymen, 0x1014, _asElevator); + sendMessage(_klaymen, 0x2001, 0); + } + break; + } + return messageResult; +} + +uint32 Scene2207::handleMessage2(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + _elevatorSurfacePriority = param.asInteger(); + break; + case 0x2004: + SetMessageHandler(&Scene2207::handleMessage); + sendMessage(_klaymen, 0x2005, 0); + sendEntityMessage(_klaymen, 0x1014, _asLever); + setMessageList(0x004B3920); + setRectList(0x004B3948); + break; + } + return messageResult; +} + +static const uint32 kScene2208FileHashes1[] = { + 0x041023CB, 0x041020CB, 0x041026CB, 0x04102ACB, + 0x041032CB, 0x041002CB +}; + +static const uint32 kScene2208FileHashes2[] = { + 0x091206C9, 0x091406C9, 0x091806C9, 0x090006C9, + 0x093006C9, 0x095006C9 +}; + +Scene2208::Scene2208(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _textResource(vm) { + + SpriteResource spriteResource(_vm); + const char *textStart, *textEnd; + + if (!getGlobalVar(V_COLUMN_TEXT_NAME)) + setGlobalVar(V_COLUMN_TEXT_NAME, calcHash("stLineagex")); + + _textResource.load(getGlobalVar(V_COLUMN_TEXT_NAME)); + + textStart = _textResource.getString(getGlobalVar(V_CLICKED_COLUMN_INDEX), textEnd); + while (textStart < textEnd) { + _strings.push_back(textStart); + textStart += strlen(textStart) + 1; + } + + _maxRowIndex = 8 + 10 * (3 - (getGlobalVar(V_COLUMN_TEXT_NAME) == calcHash("stLineagex") ? 1 : 0)); + + _background = new Background(_vm, 0); + _background->createSurface(0, 640, 528); + _background->getSpriteResource().getPosition().y = 480; + addBackground(_background); + setPalette(0x08100289); + addEntity(_palette); + insertPuzzleMouse(0x0028D089, 40, 600); + + _fontSurface = FontSurface::createFontSurface(_vm, 0x0800090C); + + _backgroundSurface = new BaseSurface(_vm, 0, 640, 480); + spriteResource.load(0x08100289, true); + _backgroundSurface->drawSpriteResourceEx(spriteResource, false, false, 0, 0); + + _topBackgroundSurface = new BaseSurface(_vm, 0, 640, 192); + spriteResource.load(!getGlobalVar(V_COLUMN_BACK_NAME) + ? kScene2208FileHashes1[getGlobalVar(V_CLICKED_COLUMN_INDEX) % 6] + : getGlobalVar(V_COLUMN_BACK_NAME), true); + _topBackgroundSurface->drawSpriteResourceEx(spriteResource, false, false, 0, 0); + + _bottomBackgroundSurface = new BaseSurface(_vm, 0, 640, 192); + spriteResource.load(kScene2208FileHashes2[getGlobalVar(V_CLICKED_COLUMN_INDEX) % 6], true); + _bottomBackgroundSurface->drawSpriteResourceEx(spriteResource, false, false, 0, 0); + + SetUpdateHandler(&Scene2208::update); + SetMessageHandler(&Scene2208::handleMessage); + + _visibleRowsCount = 10; + _newRowIndex = (int16)getGlobalVar(V_CLICKED_COLUMN_ROW); + if (_newRowIndex + _visibleRowsCount > _maxRowIndex) + _newRowIndex = _maxRowIndex - _visibleRowsCount; + if (_newRowIndex < 6) + _newRowIndex = 0; + _rowScrollY = 0; + _backgroundScrollY = 48 * _newRowIndex; + _currRowIndex = _newRowIndex; + + for (int16 rowIndex = 0; rowIndex < _visibleRowsCount; rowIndex++) + drawRow(_newRowIndex + rowIndex); + + _background->getSurface()->getSysRect().y = _backgroundScrollY; + +} + +Scene2208::~Scene2208() { + delete _fontSurface; + delete _backgroundSurface; + delete _topBackgroundSurface; + delete _bottomBackgroundSurface; +} + +void Scene2208::update() { + + int16 mouseY = _vm->getMouseY(); + + if (mouseY < 48) { + if (_currRowIndex > 0) + _newRowIndex = _currRowIndex - 1; + } else if (mouseY > 432) { + if (_currRowIndex < _maxRowIndex - _visibleRowsCount) + _newRowIndex = _currRowIndex + 1; + } else { + if (_currRowIndex > _newRowIndex) + _newRowIndex = _currRowIndex; + } + + if (_currRowIndex < _newRowIndex) { + if (_rowScrollY == 0) + drawRow(_currRowIndex + _visibleRowsCount); + _backgroundScrollY += 4; + _rowScrollY += 4; + if (_rowScrollY == 48) { + _rowScrollY = 0; + _currRowIndex++; + } + _background->getSurface()->getSysRect().y = _backgroundScrollY; + } else if (_currRowIndex > _newRowIndex || _rowScrollY > 0) { + if (_rowScrollY == 0) { + drawRow(_currRowIndex - 1); + _currRowIndex--; + } + _backgroundScrollY -= 4; + if (_rowScrollY == 0) + _rowScrollY = 48; + _rowScrollY -= 4; + _background->getSurface()->getSysRect().y = _backgroundScrollY; + } + + Scene::update(); + +} + +uint32 Scene2208::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 40 || param.asPoint().x >= 600) + leaveScene(0); + break; + } + return messageResult; +} + +void Scene2208::drawRow(int16 rowIndex) { + NDrawRect sourceRect; + int16 y = (rowIndex * 48) % 528; + if (rowIndex < 4) { + sourceRect.x = 0; + sourceRect.y = y; + sourceRect.width = 640; + sourceRect.height = 48; + _background->getSurface()->copyFrom(_topBackgroundSurface->getSurface(), 0, y, sourceRect); + } else if (rowIndex > _maxRowIndex - 5) { + sourceRect.x = 0; + sourceRect.y = (rowIndex - _maxRowIndex + 4) * 48; + sourceRect.width = 640; + sourceRect.height = 48; + _background->getSurface()->copyFrom(_bottomBackgroundSurface->getSurface(), 0, y, sourceRect); + } else { + rowIndex -= 4; + sourceRect.x = 0; + sourceRect.y = (rowIndex * 48) % 480; + sourceRect.width = 640; + sourceRect.height = 48; + _background->getSurface()->copyFrom(_backgroundSurface->getSurface(), 0, y, sourceRect); + if (rowIndex < (int)_strings.size()) { + const char *text = _strings[rowIndex]; + _fontSurface->drawString(_background->getSurface(), 95, y, (const byte*)text); + } + } +} + +static const int16 kScene2242XPositions[] = { + 68, 158 +}; + +static const uint32 kScene2242MessageListIds2[] = { + 0x004B3CB8, 0x004B3CD8 +}; + +static const uint32 kScene2242MessageListIds1[] = { + 0x004B3CF8, 0x004B3D20 +}; + +Scene2242::Scene2242(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isKlaymenInLight(false) { + + SetMessageHandler(&Scene2242::handleMessage); + SetUpdateHandler(&Scene2242::update); + + if (getGlobalVar(V_LIGHTS_ON)) { + setBackground(0x11840E24); + setPalette(0x11840E24); + insertScreenMouse(0x40E20110); + setRectList(0x004B3DC8); + } else { + setBackground(0x25848E24); + setPalette(0x25848E24); + addEntity(_palette); + _palette->copyBasePalette(0, 256, 0); + _palette->addPalette(0x68033B1C, 0, 65, 0); + insertScreenMouse(0x48E20250); + setRectList(0x004B3E18); + } + + _asTape = insertSprite<AsScene1201Tape>(this, 10, 1100, 464, 435, 0x9148A011); + addCollisionSprite(_asTape); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2242>(200, 430); + setMessageList(0x004B3C18); + } else if (which == 1) { + // Klaymen entering from looking through the window + insertKlaymen<KmScene2242>(530, 430); + setMessageList(0x004B3D60); + } else if (which == 2) { + // Klaymen returning from reading a text column + insertKlaymen<KmScene2242>(kScene2242XPositions[!getGlobalVar(V_CLICKED_COLUMN_INDEX) ? 0 : 1], 430); + setMessageList(0x004B3D48); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) + _klaymen->setDoDeltaX(1); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene2242>(0, 430); + setMessageList(0x004B3C20); + } + + _klaymen->setSoundFlag(true); + +} + +Scene2242::~Scene2242() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +void Scene2242::update() { + if (!getGlobalVar(V_LIGHTS_ON)) { + if (_isKlaymenInLight && _klaymen->getX() < 440) { + _palette->addBasePalette(0x68033B1C, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymenInLight = false; + } else if (!_isKlaymenInLight && _klaymen->getX() >= 440) { + _palette->addBasePalette(0x25848E24, 0, 65, 0); + _palette->startFadeToPalette(12); + _isKlaymenInLight = true; + } + } + Scene::update(); +} + +uint32 Scene2242::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x800C6694) + readClickedColumn(); + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B3D50); + } + break; + } + return messageResult; +} + +void Scene2242::readClickedColumn() { + int index; + if (_mouseClickPos.x < 108) { + setGlobalVar(V_COLUMN_TEXT_NAME, 0x04290188); + setGlobalVar(V_CLICKED_COLUMN_INDEX, 42); + setGlobalVar(V_COLUMN_BACK_NAME, calcHash("bgRecPanelStart1")); + index = 0; + } else { + setGlobalVar(V_COLUMN_TEXT_NAME, 0x04290188); + setGlobalVar(V_CLICKED_COLUMN_INDEX, 43); + setGlobalVar(V_COLUMN_BACK_NAME, calcHash("bgRecPanelStart2")); + index = 1; + } + setGlobalVar(V_CLICKED_COLUMN_ROW, (_mouseClickPos.y - 100) / 7); + if (ABS(_klaymen->getX() - kScene2242XPositions[index]) < 133) + setMessageList2(kScene2242MessageListIds1[index]); + else + setMessageList2(kScene2242MessageListIds2[index]); +} + +static const int16 kHallOfRecordsKlaymenXPos[] = { + 68, 157, 246, 335, + 424, 513, 602 +}; + +static const uint32 kHallOfRecordsSceneMessageListIds2[] = { + 0x004B2978, 0x004B2998, 0x004B29B8, 0x004B29D8, + 0x004B29F8, 0x004B2A18, 0x004B2A38 +}; + +static const uint32 kHallOfRecordsSceneMessageListIds1[] = { + 0x004B2A58, 0x004B2A80, 0x004B2AA8, 0x004B2AD0, + 0x004B2AF8, 0x004B2B20, 0x004B2B48 +}; + +HallOfRecordsScene::HallOfRecordsScene(NeverhoodEngine *vm, Module *parentModule, int which, uint32 hallOfRecordsInfoId) + : Scene(vm, parentModule) { + + _hallOfRecordsInfo = _vm->_staticData->getHallOfRecordsInfoItem(hallOfRecordsInfoId); + + SetMessageHandler(&HallOfRecordsScene::handleMessage); + SetUpdateHandler(&Scene::update); + + if (!getGlobalVar(V_LIGHTS_ON) && _hallOfRecordsInfo->bgFilename2) { + setRectList(0x004B2BF8); + setBackground(_hallOfRecordsInfo->bgFilename2); + setPalette(_hallOfRecordsInfo->bgFilename2); + insertScreenMouse(0x14320138); + } else { + setRectList(0x004B2BB8); + setBackground(_hallOfRecordsInfo->bgFilename1); + setPalette(_hallOfRecordsInfo->bgFilename1); + insertScreenMouse(0x63A40028); + } + + if (which < 0) { + // Restoring game + insertKlaymen<KmHallOfRecords>(200, 430); + setMessageList(0x004B2900); + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmHallOfRecords>(640, 430); + setMessageList(0x004B2910); + } else if (which == 2) { + // Klaymen returning from reading a text column + insertKlaymen<KmHallOfRecords>(kHallOfRecordsKlaymenXPos[getGlobalVar(V_CLICKED_COLUMN_INDEX) - _hallOfRecordsInfo->xPosIndex], 430); + setMessageList(0x004B2B70); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) + _klaymen->setDoDeltaX(1); + } else { + // Klaymen entering from the left + insertKlaymen<KmHallOfRecords>(0, 430); + setMessageList(0x004B2908); + } + + _klaymen->setSoundFlag(true); + _klaymen->setKlaymenIdleTable2(); + +} + +HallOfRecordsScene::~HallOfRecordsScene() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +uint32 HallOfRecordsScene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x800C6694) + readClickedColumn(); + break; + } + return messageResult; +} + +void HallOfRecordsScene::readClickedColumn() { + int16 index = (_mouseClickPos.x - 23) / 89; + if (index >= _hallOfRecordsInfo->count) + setMessageList2(0x004B2920); + else { + setGlobalVar(V_CLICKED_COLUMN_INDEX, _hallOfRecordsInfo->xPosIndex + index); + setGlobalVar(V_CLICKED_COLUMN_ROW, (_mouseClickPos.y - 100) / 7); + setGlobalVar(V_COLUMN_TEXT_NAME, _hallOfRecordsInfo->txFilename); + if (index == 0 && _hallOfRecordsInfo->bgFilename3) + setGlobalVar(V_COLUMN_BACK_NAME, _hallOfRecordsInfo->bgFilename3); + else + setGlobalVar(V_COLUMN_BACK_NAME, 0); + if (ABS(_klaymen->getX() - kHallOfRecordsKlaymenXPos[index]) < 133) + setMessageList2(kHallOfRecordsSceneMessageListIds1[index]); + else + setMessageList2(kHallOfRecordsSceneMessageListIds2[index]); + } +} + +static const int16 kScene2247XPositions[] = { + 513, 602 +}; + +static const uint32 kScene2247MessageListIds2[] = { + 0x004B54A0, 0x004B54C0 +}; + +static const uint32 kScene2247MessageListIds1[] = { + 0x004B54E0, 0x004B5508 +}; + +Scene2247::Scene2247(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene2247::handleMessage); + SetUpdateHandler(&Scene::update); + + if (getGlobalVar(V_LIGHTS_ON)) { + setRectList(0x004B5588); + setBackground(0x40339414); + setPalette(0x40339414); + insertScreenMouse(0x3941040B); + } else { + setRectList(0x004B55C8); + setBackground(0x071963E5); + setPalette(0x071963E5); + insertScreenMouse(0x14320138); + } + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2247>(200, 430); + setMessageList(0x004B5428); + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene2247>(640, 430); + setMessageList(0x004B5438); + } else if (which == 2) { + // Klaymen returning from reading a text column + insertKlaymen<KmScene2247>(kScene2247XPositions[getGlobalVar(V_COLUMN_TEXT_NAME) == 0x0008E486 ? 0 : 1], 430); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) + _klaymen->setDoDeltaX(1); + setMessageList(0x004B5530); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene2247>(0, 430); + setMessageList(0x004B5430); + } + + _klaymen->setSoundFlag(true); + +} + +Scene2247::~Scene2247() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene2247::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x800C6694) + readClickedColumn(); + break; + } + return messageResult; +} + +void Scene2247::readClickedColumn() { + int index; + if (_mouseClickPos.x < 553) { + setGlobalVar(V_COLUMN_TEXT_NAME, 0x0008E486); + setGlobalVar(V_COLUMN_BACK_NAME, calcHash("bgFatherHeader")); + index = 0; + } else { + setGlobalVar(V_COLUMN_TEXT_NAME, 0x03086004); + setGlobalVar(V_COLUMN_BACK_NAME, calcHash("bgQuaterHeader")); + index = 1; + } + setGlobalVar(V_CLICKED_COLUMN_INDEX, 0); + setGlobalVar(V_CLICKED_COLUMN_ROW, (_mouseClickPos.y - 100) / 7); + if (ABS(_klaymen->getX() - kScene2247XPositions[index]) < 133) + setMessageList2(kScene2247MessageListIds1[index]); + else + setMessageList2(kScene2247MessageListIds2[index]); +} + +} // End of namespace Neverhood |