diff options
Diffstat (limited to 'engines/neverhood/modules')
42 files changed, 24849 insertions, 0 deletions
diff --git a/engines/neverhood/modules/module1000.cpp b/engines/neverhood/modules/module1000.cpp new file mode 100644 index 0000000000..415d0f72b1 --- /dev/null +++ b/engines/neverhood/modules/module1000.cpp @@ -0,0 +1,1699 @@ +/* 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/module1000.h" + +namespace Neverhood { + +Module1000::Module1000(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + debug("Create Module1000(%d)", which); + + _musicFileHash = getGlobalVar(V_ENTRANCE_OPEN) ? 0x81106480 : 0x00103144; + + _vm->_soundMan->addMusic(0x03294419, 0x061880C6); + _vm->_soundMan->addMusic(0x03294419, _musicFileHash); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 0) + createScene(0, 0); + else if (which == 1) + createScene(1, 1); + +} + +Module1000::~Module1000() { + _vm->_soundMan->deleteMusicGroup(0x03294419); +} + +void Module1000::createScene(int sceneNum, int which) { + debug("Module1000::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _vm->_soundMan->startMusic(0x061880C6, 0, 0); + _childObject = new Scene1001(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _vm->_soundMan->startMusic(0x061880C6, 0, 0); + _childObject = new Scene1002(_vm, this, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->_soundMan->startMusic(0x061880C6, 0, 0); + createStaticScene(0xC084110C, 0x41108C00); + break; + case 3: + _vm->gameState().sceneNum = 3; + _vm->_soundMan->stopMusic(0x061880C6, 0, 2); + _childObject = new Scene1004(_vm, this, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->stopMusic(0x061880C6, 0, 0); + _vm->_soundMan->startMusic(_musicFileHash, 0, 0); + _childObject = new Scene1005(_vm, this, which); + break; + } + SetUpdateHandler(&Module1000::updateScene); + _childObject->handleUpdate(); +} + +void Module1000::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 2) + createScene(2, 0); + else + createScene(1, 0); + break; + case 1: + if (_moduleResult == 1) + leaveModule(0); + else if (_moduleResult == 2) { + if (_vm->isDemo()) + // Demo version returns to the same scene + createScene(1, 2); + else + createScene(3, 0); + } else + createScene(0, 1); + break; + case 2: + createScene(0, 2); + break; + case 3: + if (_moduleResult == 1) + createScene(4, 0); + else + createScene(1, 2); + break; + case 4: + _vm->_soundMan->stopMusic(_musicFileHash, 0, 1); + createScene(3, 1); + break; + } + } +} + +// Scene1001 + +AsScene1001Door::AsScene1001Door(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + createSurface(800, 137, 242); + _x = 726; + _y = 440; + stShowIdleDoor(); + loadSound(1, 0xED403E03); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Door::handleMessage); +} + +uint32 AsScene1001Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + hammerHitsDoor(); + break; + case 0x3002: + gotoNextState(); + break; + } + return 0; +} + +void AsScene1001Door::hammerHitsDoor() { + switch (getGlobalVar(V_DOOR_STATUS)) { + case 0: + case 1: + playSound(0, 0x65482F03); + startAnimation(0x624C0498, 1, 3); + NextState(&AsScene1001Door::stShowIdleDoor); + break; + case 2: + playSound(1); + startAnimation(0x624C0498, 6, 6); + NextState(&AsScene1001Door::stBustedDoorMove); + break; + default: + // Nothing + break; + } + incGlobalVar(V_DOOR_STATUS, 1); +} + +void AsScene1001Door::stShowIdleDoor() { + switch (getGlobalVar(V_DOOR_STATUS)) { + case 1: + startAnimation(0x624C0498, 4, -1); + _newStickFrameIndex = 4; + break; + case 2: + startAnimation(0x624C0498, 1, -1); + _newStickFrameIndex = 1; + break; + case 3: + stopAnimation(); + setVisible(false); + break; + default: + startAnimation(0x624C0498, 0, -1); + _newStickFrameIndex = 0; + break; + } +} + +void AsScene1001Door::stBustedDoorMove() { + setGlobalVar(V_DOOR_BUSTED, 1); + startAnimation(0x624C0498, 6, 6); + NextState(&AsScene1001Door::stBustedDoorGone); + _x = 30; +} + +void AsScene1001Door::stBustedDoorGone() { + playSound(0); + stopAnimation(); + setVisible(false); +} + +AsScene1001Hammer::AsScene1001Hammer(NeverhoodEngine *vm, Sprite *asDoor) + : AnimatedSprite(vm, 1100), _asDoor(asDoor) { + + _x = 547; + _y = 206; + createSurface(900, 177, 192); + startAnimation(0x022C90D4, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Hammer::handleMessage); +} + +uint32 AsScene1001Hammer::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x00352100) + sendMessage(_asDoor, 0x2000, 0); + else if (param.asInteger() == 0x0A1A0109) + playSound(0, 0x66410886); + break; + case 0x2000: + startAnimation(0x022C90D4, 1, -1); + playSound(0, 0xE741020A); + _newStickFrameIndex = STICK_LAST_FRAME; + break; + } + return 0; +} + +AsScene1001Window::AsScene1001Window(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200) { + + _x = 320; + _y = 240; + createSurface(100, 66, 129); + startAnimation(0xC68C2299, 0, -1); + _newStickFrameIndex = 0; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Window::handleMessage); +} + +uint32 AsScene1001Window::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0E0A1410) + playSound(0, 0x60803F10); + break; + case 0x2001: + startAnimation(0xC68C2299, 0, -1); + break; + case 0x3002: + SetMessageHandler(NULL); + setGlobalVar(V_WINDOW_OPEN, 1); + setVisible(false); + break; + } + return 0; +} + +AsScene1001Lever::AsScene1001Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int deltaXType) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + createSurface(1010, 71, 73); + setDoDeltaX(deltaXType); + startAnimation(0x04A98C36, 0, -1); + _newStickFrameIndex = 0; + _x = x; + _y = y; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1001Lever::handleMessage); +} + +uint32 AsScene1001Lever::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x00C0C444) + sendMessage(_parentScene, 0x480F, 0); + else if (param.asInteger() == 0xC41A02C0) + playSound(0, 0x40581882); + break; + case 0x1011: + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x3002: + startAnimation(0x04A98C36, 0, -1); + _newStickFrameIndex = 0; + break; + case 0x480F: + startAnimation(0x04A98C36, 0, -1); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +SsCommonButtonSprite::SsCommonButtonSprite(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, int surfacePriority, uint32 soundFileHash) + : StaticSprite(vm, fileHash, surfacePriority), _parentScene(parentScene), _countdown(0) { + + _priority = 1100; + _soundFileHash = soundFileHash ? soundFileHash : 0x44141000; + setVisible(false); + SetUpdateHandler(&SsCommonButtonSprite::update); + SetMessageHandler(&SsCommonButtonSprite::handleMessage); +} + +void SsCommonButtonSprite::update() { + if (_countdown != 0 && (--_countdown) == 0) + setVisible(false); +} + +uint32 SsCommonButtonSprite::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x480B: + sendMessage(_parentScene, 0x480B, 0); + setVisible(true); + _countdown = 8; + playSound(0, _soundFileHash); + break; + } + return messageResult; +} + +Scene1001::Scene1001(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _asDoor(NULL), _asWindow(NULL) { + + Sprite *tempSprite; + + SetMessageHandler(&Scene1001::handleMessage); + + setHitRects(0x004B4860); + setBackground(0x4086520E); + setPalette(0x4086520E); + insertScreenMouse(0x6520A400); + + if (which < 0) { + // Restoring game + setRectList(0x004B49F0); + insertKlaymen<KmScene1001>(200, 433); + setMessageList(0x004B4888); + } else if (which == 1) { + // Klaymen entering from the right + setRectList(0x004B49F0); + insertKlaymen<KmScene1001>(640, 433); + setMessageList(0x004B4898); + } else if (which == 2) { + // Klaymen returning from looking through the window + setRectList(0x004B49F0); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene1001>(390, 433); + _klaymen->setDoDeltaX(1); + } else { + insertKlaymen<KmScene1001>(300, 433); + } + setMessageList(0x004B4970); + } else { + // Klaymen sleeping + setRectList(0x004B4A00); + insertKlaymen<KmScene1001>(200, 433); + setMessageList(0x004B4890); + } + + tempSprite = insertStaticSprite(0x2080A3A8, 1300); + + _klaymen->setClipRect(0, 0, tempSprite->getDrawRect().x2(), 480); + + if (!getGlobalVar(V_DOOR_BUSTED)) { + _asDoor = insertSprite<AsScene1001Door>(); + _asDoor->setClipRect(0, 0, tempSprite->getDrawRect().x2(), 480); + } + + _asLever = insertSprite<AsScene1001Lever>(this, 150, 433, 1); + + insertStaticSprite(0x809861A6, 950); + insertStaticSprite(0x89C03848, 1100); + + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x15288120, 100, 0); + + if (!getGlobalVar(V_WINDOW_OPEN)) { + tempSprite = insertStaticSprite(0x8C066150, 200); + _asWindow = insertSprite<AsScene1001Window>(); + _asWindow->setClipRect(tempSprite->getDrawRect()); + } + + _asHammer = insertSprite<AsScene1001Hammer>(_asDoor); + +} + +Scene1001::~Scene1001() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX()); +} + +uint32 Scene1001::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x00342624) { + sendEntityMessage(_klaymen, 0x1014, _asLever); + setMessageList2(0x004B4910); + messageResult = 1; + } else if (param.asInteger() == 0x21E64A00) { + if (getGlobalVar(V_DOOR_BUSTED)) { + setMessageList(0x004B48A8); + } else { + setMessageList(0x004B48C8); + } + messageResult = 1; + } else if (param.asInteger() == 0x040424D0) { + sendEntityMessage(_klaymen, 0x1014, _ssButton); + } else if (param.asInteger() == 0x80006358) { + if (getGlobalVar(V_WINDOW_OPEN)) { + setMessageList(0x004B4938); + } else { + setMessageList(0x004B4960); + } + } + break; + case 0x2002: + setRectList(0x004B49F0); + break; + case 0x480B: + sendMessage(_asWindow, 0x2001, 0); + break; + case 0x480F: + sendMessage(_asHammer, 0x2000, 0); + break; + } + return messageResult; +} + +// Scene1002 + +AsScene1002Ring::AsScene1002Ring(NeverhoodEngine *vm, Scene *parentScene, bool isSpecial, int16 x, int16 y, int16 clipY1, bool isRingLow) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _isSpecial(isSpecial) { + + SetUpdateHandler(&AsScene1002Ring::update); + + if (_isSpecial) { + createSurface(990, 68, 314); + if (isRingLow) { + startAnimation(0x04103090, 0, -1); + SetMessageHandler(&AsScene1002Ring::hmRingHangingLow); + } else { + startAnimation(0xA85C4011, _vm->_rnd->getRandomNumber(15), -1); + SetMessageHandler(&AsScene1002Ring::hmRingIdle); + } + } else { + createSurface(990, 68, 138); + startAnimation(0xA85C4011, _vm->_rnd->getRandomNumber(15), -1); + SetMessageHandler(&AsScene1002Ring::hmRingIdle); + } + + setClipRect(0, clipY1, 640, 480); + + _x = x; + _y = y; + + setDoDeltaX(_vm->_rnd->getRandomNumber(1)); + +} + +void AsScene1002Ring::update() { + updateAnim(); + updatePosition(); +} + +uint32 AsScene1002Ring::hmRingIdle(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4806: + setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0); + sendMessage(_parentScene, 0x4806, 0); + SetMessageHandler(&AsScene1002Ring::hmRingPulled1); + startAnimation(_isSpecial ? 0x87502558 : 0x80DD4010, 0, -1); + break; + case 0x480F: + setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0); + sendMessage(_parentScene, 0x480F, 0); + SetMessageHandler(&AsScene1002Ring::hmRingPulled2); + startAnimation(0x861A2020, 0, -1); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::hmRingPulled1(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + startAnimation(_isSpecial ? 0x78D0A812 : 0xB85D2A10, 0, -1); + SetMessageHandler(&AsScene1002Ring::hmRingHangingLow); + break; + case 0x4807: + sendMessage(_parentScene, 0x4807, 0); + setDoDeltaX(_vm->_rnd->getRandomNumber(1)); + startAnimation(0x8258A030, 0, -1); + SetMessageHandler(&AsScene1002Ring::hmRingReleased); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::hmRingPulled2(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + startAnimation(0x04103090, 0, -1); + SetMessageHandler(&AsScene1002Ring::hmRingHangingLow); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::hmRingHangingLow(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4807: + sendMessage(_parentScene, 0x4807, 0); + setDoDeltaX(_vm->_rnd->getRandomNumber(1)); + startAnimation(0x8258A030, 0, -1); + SetMessageHandler(&AsScene1002Ring::hmRingReleased); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene1002Ring::hmRingReleased(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = hmRingIdle(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x05410F72) + playSound(0, 0x21EE40A9); + break; + case 0x3002: + startAnimation(0xA85C4011, 0, -1); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +AsScene1002Door::AsScene1002Door(NeverhoodEngine *vm, NRect &clipRect) + : StaticSprite(vm, 1200) { + + loadSprite(0x1052370F, kSLFDefDrawOffset | kSLFSetPosition, 800, 526, getGlobalVar(V_FLYTRAP_RING_DOOR) ? 49 : 239); + setClipRect(clipRect); + SetUpdateHandler(&AsScene1002Door::update); + SetMessageHandler(&AsScene1002Door::handleMessage); + SetSpriteUpdate(NULL); +} + +void AsScene1002Door::update() { + handleSpriteUpdate(); + updatePosition(); +} + +uint32 AsScene1002Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + setGlobalVar(V_FLYTRAP_RING_DOOR, 1); + SetSpriteUpdate(&AsScene1002Door::suOpenDoor); + break; + case 0x4809: + setGlobalVar(V_FLYTRAP_RING_DOOR, 0); + SetSpriteUpdate(&AsScene1002Door::suCloseDoor); + break; + } + return messageResult; +} + +void AsScene1002Door::suOpenDoor() { + if (_y > 49) { + _y -= 8; + if (_y < 49) { + SetSpriteUpdate(NULL); + _y = 49; + } + _needRefresh = true; + } +} + +void AsScene1002Door::suCloseDoor() { + if (_y < 239) { + _y += 8; + if (_y > 239) { + SetSpriteUpdate(NULL); + _y = 239; + } + _needRefresh = true; + } +} + +AsScene1002BoxingGloveHitEffect::AsScene1002BoxingGloveHitEffect(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1400) { + + createSurface(1025, 88, 165); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1002BoxingGloveHitEffect::handleMessage); +} + +uint32 AsScene1002BoxingGloveHitEffect::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2004: + _x = ((Sprite*)sender)->getX() - 98; + _y = ((Sprite*)sender)->getY() - 111; + startAnimation(0x0422255A, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +AsScene1002DoorSpy::AsScene1002DoorSpy(NeverhoodEngine *vm, NRect &clipRect, Scene *parentScene, Sprite *asDoor, Sprite *asScene1002BoxingGloveHitEffect) + : AnimatedSprite(vm, 1300), _clipRect(clipRect), _parentScene(parentScene), _asDoor(asDoor), _asBoxingGloveHitEffect(asScene1002BoxingGloveHitEffect) { + + createSurface(800, 136, 147); + setClipRect(clipRect); + suDoorSpy(); + loadSound(0, 0xC0C40298); + startAnimation(0x586C1D48, 0, 0); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1002DoorSpy::handleMessage); + SetSpriteUpdate(&AsScene1002DoorSpy::suDoorSpy); +} + +uint32 AsScene1002DoorSpy::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xA61CA1C2) + sendMessage(_asBoxingGloveHitEffect, 0x2004, 0); + else if (param.asInteger() == 0x14CE0620) + playSound(0); + break; + case 0x2003: + stDoorSpyBoxingGlove(); + break; + } + return messageResult; +} + +uint32 AsScene1002DoorSpy::hmDoorSpyAnimation(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1002DoorSpy::suDoorSpy() { + _x = _asDoor->getX() + 34; + _y = _asDoor->getY() + 175; +} + +void AsScene1002DoorSpy::stDoorSpyIdle() { + setClipRect(_clipRect); + _parentScene->setSurfacePriority(getSurface(), 800); + startAnimation(0x586C1D48, 0, 0); + SetMessageHandler(&AsScene1002DoorSpy::handleMessage); +} + +void AsScene1002DoorSpy::stDoorSpyBoxingGlove() { + setClipRect(0, 0, 640, 480); + _parentScene->setSurfacePriority(getSurface(), 1200); + startAnimation(0x586C1D48, 1, -1); + SetMessageHandler(&AsScene1002DoorSpy::hmDoorSpyAnimation); + NextState(&AsScene1002DoorSpy::stDoorSpyIdle); +} + +SsCommonPressButton::SsCommonPressButton(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash) + : StaticSprite(vm, 1100), _parentScene(parentScene), _status(0), _countdown(0) { + + _soundFileHash = soundFileHash != 0 ? soundFileHash : 0x44141000; + _fileHashes[0] = fileHash1; + _fileHashes[1] = fileHash2; + createSurface(surfacePriority, 40, 40); + loadSprite(fileHash1, kSLFDefDrawOffset | kSLFDefPosition); + setVisible(false); + SetUpdateHandler(&SsCommonPressButton::update); + SetMessageHandler(&SsCommonPressButton::handleMessage); +} + +void SsCommonPressButton::setFileHashes(uint32 fileHash1, uint32 fileHash2) { + _fileHashes[0] = fileHash1; + _fileHashes[1] = fileHash2; + loadSprite(_status == 2 ? fileHash2 : fileHash1, kSLFDefDrawOffset | kSLFDefPosition); +} + +void SsCommonPressButton::update() { + if (_countdown != 0 && (--_countdown) == 0) { + if (_status == 1) { + _status = 2; + loadSprite(_fileHashes[1], kSLFDefDrawOffset | kSLFDefPosition); + _countdown = 4; + } else if (_status == 2) { + _status = 3; + loadSprite(_fileHashes[0], kSLFDefDrawOffset | kSLFDefPosition); + _countdown = 4; + } else if (_status == 3) { + _status = 0; + setVisible(false); + } + } +} + +uint32 SsCommonPressButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x480B: + sendMessage(_parentScene, 0x480B, 0); + _status = 1; + _countdown = 4; + setVisible(true); + playSound(0, _soundFileHash); + break; + } + return messageResult; +} + +AsScene1002VenusFlyTrap::AsScene1002VenusFlyTrap(NeverhoodEngine *vm, Scene *parentScene, Sprite *klaymen, bool isSecond) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _klaymen(klaymen), _isSecond(isSecond), _countdown(0) { + + createSurface(995, 175, 195); + if (!_isSecond) { + if (getGlobalVar(V_FLYTRAP_RING_DOOR)) { + setDoDeltaX(1); + _x = 366; + _y = 435; + stRingGrabbed(); + } else { + _x = 174 + getGlobalVar(V_FLYTRAP_POSITION_1) * 32; + _y = 435; + stIdle(); + } + } else { + _x = 186 + getGlobalVar(V_FLYTRAP_POSITION_2) * 32; + _y = 364; + if (getGlobalVar(V_FLYTRAP_RING_BRIDGE) || getGlobalVar(V_FLYTRAP_RING_FENCE)) { + stRingGrabbed(); + } else { + stIdle(); + } + } + _flags = 4; + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); +} + +void AsScene1002VenusFlyTrap::update() { + if (_countdown != 0 && (--_countdown == 0)) + gotoNextState(); + AnimatedSprite::update(); +} + +void AsScene1002VenusFlyTrap::upIdle() { + if (_countdown == 0 && _klaymen->getX() - 20 > _x) + setDoDeltaX(1); + else if (_klaymen->getX() + 20 < _x) + setDoDeltaX(0); + update(); +} + +uint32 AsScene1002VenusFlyTrap::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x000890C4) + playSound(0, 0xC21190D8); + else if (param.asInteger() == 0x522200A0) + playSound(0, 0x931080C8); + break; + case 0x1011: + if (_isSecond) { + if (_x >= 154 && _x <= 346) { + sendMessage(_parentScene, 0x2000, 0); + messageResult = 1; + } + } else { + if (_x >= 174 && _x <= 430) { + sendMessage(_parentScene, 0x2000, 0); + messageResult = 1; + } + } + break; + case 0x480B: + setDoDeltaX(param.asInteger() != 0 ? 1 : 0); + if (!_isSecond) { + if (getGlobalVar(V_FLYTRAP_RING_DOOR)) + stRelease(); + else + stWalk(); + } else { + if (getGlobalVar(V_FLYTRAP_RING_BRIDGE) || getGlobalVar(V_FLYTRAP_RING_FENCE)) + stRelease(); + else + stWalk(); + } + break; + case 0x480C: + if (_isSecond) { + if (_x >= 154 && _x <= 346) + messageResult = 1; + else + messageResult = 0; + } else { + if (_x >= 174 && _x <= 430) + messageResult = 1; + else + messageResult = 0; + } + break; + case 0x480E: + if (param.asInteger() == 1) + stGrabRing(); + break; + case 0x4810: + swallowKlaymen(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 995); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1015); + break; + } + return messageResult; +} + +uint32 AsScene1002VenusFlyTrap::hmAnimationSimple(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +uint32 AsScene1002VenusFlyTrap::hmAnimationExt(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x000890C4) + playSound(0, 0xC21190D8); + else if (param.asInteger() == 0x41881801) { + if (_isSecond) { + if (_x > 330) + sendMessage(_klaymen, 0x4811, 2); + else + sendMessage(_klaymen, 0x4811, 0); + } else { + sendMessage(_klaymen, 0x4811, 0); + } + } else if (param.asInteger() == 0x522200A0) + playSound(0, 0x931080C8); + break; + case 0x3002: + gotoNextState(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 995); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1015); + break; + } + return messageResult; +} + +void AsScene1002VenusFlyTrap::stWalkBack() { + setDoDeltaX(2); + startAnimation(0xC4080034, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::hmAnimationExt); + NextState(&AsScene1002VenusFlyTrap::stIdle); +} + +void AsScene1002VenusFlyTrap::stWalk() { + startAnimation(0xC4080034, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::hmAnimationSimple); + NextState(&AsScene1002VenusFlyTrap::stIdle); +} + +void AsScene1002VenusFlyTrap::stRelease() { + sendMessage(_parentScene, 0x4807, 0); + startAnimation(0x82292851, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::hmAnimationSimple); + NextState(&AsScene1002VenusFlyTrap::stIdle); +} + +void AsScene1002VenusFlyTrap::stGrabRing() { + setDoDeltaX(1); + startAnimation(0x86A82A11, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::hmAnimationSimple); + NextState(&AsScene1002VenusFlyTrap::stRingGrabbed); +} + +void AsScene1002VenusFlyTrap::stRingGrabbed() { + startAnimation(0xB5A86034, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage); +} + +void AsScene1002VenusFlyTrap::stKlaymenInside() { + startAnimation(0x31303094, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(NULL); + NextState(&AsScene1002VenusFlyTrap::stKlaymenInsideMoving); + _countdown = 24; +} + +void AsScene1002VenusFlyTrap::stIdle() { + startAnimation(0xC8204250, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::upIdle); + SetMessageHandler(&AsScene1002VenusFlyTrap::handleMessage); + if (_isSecond) { + if (_x >= 154 && _x <= 346) + setGlobalVar(V_FLYTRAP_POSITION_2, (_x - 186) / 32); + else { + NextState(&AsScene1002VenusFlyTrap::stWalkBack); + _countdown = 12; + } + } else { + if (_x >= 174 && _x <= 430) + setGlobalVar(V_FLYTRAP_POSITION_1, (_x - 174) / 32); + else { + NextState(&AsScene1002VenusFlyTrap::stWalkBack); + _countdown = 12; + } + } +} + +void AsScene1002VenusFlyTrap::stKlaymenInsideMoving() { + startAnimation(0x152920C4, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::hmAnimationExt); + NextState(&AsScene1002VenusFlyTrap::stSpitOutKlaymen); +} + +void AsScene1002VenusFlyTrap::stSpitOutKlaymen() { + startAnimation(0x84001117, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::hmAnimationExt); + NextState(&AsScene1002VenusFlyTrap::stIdle); +} + +void AsScene1002VenusFlyTrap::swallowKlaymen() { + if (_x - 15 < _klaymen->getX() && _x + 15 > _klaymen->getX()) { + if (_isSecond) + setDoDeltaX(_x > 265 && _x < 330 ? 1 : 0); + else + setDoDeltaX(_x > 320 ? 1 : 0); + sendMessage(_klaymen, 0x2001, 0); + startAnimation(0x8C2C80D4, 0, -1); + SetUpdateHandler(&AsScene1002VenusFlyTrap::update); + SetMessageHandler(&AsScene1002VenusFlyTrap::hmAnimationExt); + NextState(&AsScene1002VenusFlyTrap::stKlaymenInside); + } +} + +AsScene1002OutsideDoorBackground::AsScene1002OutsideDoorBackground(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200), _countdown(0) { + + createSurface(850, 186, 212); + _x = 320; + _y = 240; + if (getGlobalVar(V_FLYTRAP_RING_DOOR)) { + startAnimation(0x004A4495, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + } else + setVisible(false); + SetUpdateHandler(&AsScene1002OutsideDoorBackground::update); + SetMessageHandler(&AsScene1002OutsideDoorBackground::handleMessage); +} + +void AsScene1002OutsideDoorBackground::update() { + if (_countdown != 0 && (--_countdown == 0)) { + if (_isDoorClosed) + stCloseDoor(); + else + stOpenDoor(); + } + AnimatedSprite::update(); +} + +uint32 AsScene1002OutsideDoorBackground::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageResult) { + case 0x4808: + _isDoorClosed = false; + _countdown = 2; + break; + case 0x4809: + _isDoorClosed = true; + _countdown = 2; + break; + } + return messageResult; +} + +uint32 AsScene1002OutsideDoorBackground::hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage(messageNum, param, sender); + switch (messageResult) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1002OutsideDoorBackground::stOpenDoor() { + startAnimation(0x004A4495, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + setVisible(true); + SetMessageHandler(&AsScene1002OutsideDoorBackground::handleMessage); +} + +void AsScene1002OutsideDoorBackground::stCloseDoor() { + startAnimation(0x004A4495, -1, -1); + _playBackwards = true; + setVisible(true); + SetMessageHandler(&AsScene1002OutsideDoorBackground::hmAnimation); + NextState(&AsScene1002OutsideDoorBackground::stDoorClosed); +} + +void AsScene1002OutsideDoorBackground::stDoorClosed() { + setVisible(false); + stopAnimation(); +} + +AsScene1002KlaymenLadderHands::AsScene1002KlaymenLadderHands(NeverhoodEngine *vm, Klaymen *klaymen) + : AnimatedSprite(vm, 1200), _klaymen(klaymen) { + + createSurface(1200, 40, 163); + setVisible(false); + SetUpdateHandler(&AsScene1002KlaymenLadderHands::update); + SetMessageHandler(&Sprite::handleMessage); +} + +void AsScene1002KlaymenLadderHands::update() { + if (_klaymen->getCurrAnimFileHash() == 0x3A292504) { + startAnimation(0xBA280522, _klaymen->getFrameIndex(), -1); + _newStickFrameIndex = _klaymen->getFrameIndex(); + setVisible(true); + _x = _klaymen->getX(); + _y = _klaymen->getY(); + setDoDeltaX(_klaymen->isDoDeltaX() ? 1 : 0); + } else if (_klaymen->getCurrAnimFileHash() == 0x122D1505) { + startAnimation(0x1319150C, _klaymen->getFrameIndex(), -1); + _newStickFrameIndex = _klaymen->getFrameIndex(); + setVisible(true); + _x = _klaymen->getX(); + _y = _klaymen->getY(); + setDoDeltaX(_klaymen->isDoDeltaX() ? 1 : 0); + } else + setVisible(false); + AnimatedSprite::update(); +} + +AsScene1002KlaymenPeekHand::AsScene1002KlaymenPeekHand(NeverhoodEngine *vm, Scene *parentScene, Klaymen *klaymen) + : AnimatedSprite(vm, 1200), _parentScene(parentScene), _klaymen(klaymen), + _isClipRectSaved(false) { + + createSurface(1000, 33, 41); + setVisible(false); + SetUpdateHandler(&AsScene1002KlaymenPeekHand::update); + SetMessageHandler(&AsScene1002KlaymenPeekHand::handleMessage); +} + +void AsScene1002KlaymenPeekHand::update() { + if (_klaymen->getCurrAnimFileHash() == 0xAC20C012 && _klaymen->getFrameIndex() < 50) { + startAnimation(0x9820C913, _klaymen->getFrameIndex(), -1); + _newStickFrameIndex = _klaymen->getFrameIndex(); + setVisible(true); + _x = _klaymen->getX(); + _y = _klaymen->getY(); + setDoDeltaX(_klaymen->isDoDeltaX() ? 1 : 0); + } else + setVisible(false); + AnimatedSprite::update(); +} + +uint32 AsScene1002KlaymenPeekHand::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4AB28209) { + sendMessage(_parentScene, 0x1022, 1200); + _isClipRectSaved = true; + _savedClipRect = _surface->getClipRect(); + setClipRect(0, 0, 640, 480); + } else if (param.asInteger() == 0x88001184) { + sendMessage(_parentScene, 0x1022, 1000); + if (_isClipRectSaved) + setClipRect(_savedClipRect); + } + break; + } + return messageResult; +} + +Scene1002::Scene1002(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isKlaymenFloor(false), _isClimbingLadder(false) { + + NRect tempClipRect; + Sprite *tempSprite; + + SetUpdateHandler(&Scene1002::update); + SetMessageHandler(&Scene1002::handleMessage); + + setHitRects(0x004B4138); + setBackground(0x12C23307); + setPalette(0x12C23307); + + insertStaticSprite(0x06149428, 1100); + insertStaticSprite(0x312C8774, 1100); + + _ssLadderArch = insertStaticSprite(0x152C1313, 1015); + _ssLadderArchPart1 = insertStaticSprite(0x060000A0, 1200); + _ssLadderArchPart2 = insertStaticSprite(0xB2A423B0, 1100); + _ssLadderArchPart3 = insertStaticSprite(0x316E0772, 1100); + + _ssCeiling = insertStaticSprite(0x316C4BB4, 1015); + + if (which < 0) { + // Restoring game + if (_vm->_gameState.which == 0) { + // Klaymen on top + insertKlaymen<KmScene1002>(90, 226); + _asKlaymenLadderHands = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + setMessageList(0x004B4270); + _klaymen->setClipRect(31, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart3->getDrawRect().y2()); + _asKlaymenLadderHands->getSurface()->getClipRect() = _klaymen->getSurface()->getClipRect(); + _klaymen->setRepl(64, 0); + } else { + // Klaymen on the floor + insertKlaymen<KmScene1002>(379, 435); + _asKlaymenLadderHands = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + setMessageList(0x004B4270); + _klaymen->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _asKlaymenLadderHands->setClipRect(_klaymen->getClipRect()); + } + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene1002>(650, 435); + _asKlaymenLadderHands = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + setMessageList(0x004B4478); + _klaymen->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _asKlaymenLadderHands->setClipRect(_klaymen->getClipRect()); + _vm->_gameState.which = 1; + } else if (which == 2) { + // Klaymen coming up the ladder + insertKlaymen<KmScene1002>(68, 645); + _asKlaymenLadderHands = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + setMessageList(0x004B4298); + _klaymen->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _asKlaymenLadderHands->setClipRect(_klaymen->getClipRect()); + _vm->_gameState.which = 1; + sendMessage(_klaymen, 0x4820, 0); + } else { + // Klaymen entering from the left, peeking + insertKlaymen<KmScene1002>(90, 226); + _asKlaymenLadderHands = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + setMessageList(0x004B4470); + _klaymen->setClipRect(31, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart3->getDrawRect().y2()); + _asKlaymenLadderHands->setClipRect(_klaymen->getClipRect()); + _asKlaymenPeekHand = insertSprite<AsScene1002KlaymenPeekHand>(this, _klaymen); + _asKlaymenPeekHand->setClipRect(_klaymen->getClipRect()); + _klaymen->setRepl(64, 0); + _vm->_gameState.which = 0; + } + + insertScreenMouse(0x23303124); + + tempSprite = insertStaticSprite(0xB3242310, 825); + tempClipRect.set(tempSprite->getDrawRect().x, tempSprite->getDrawRect().y, + _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart2->getDrawRect().y2()); + + _asRing1 = insertSprite<AsScene1002Ring>(this, false, 258, 191, _ssCeiling->getDrawRect().y, false); + _asRing2 = insertSprite<AsScene1002Ring>(this, false, 297, 189, _ssCeiling->getDrawRect().y, false); + _asRing3 = insertSprite<AsScene1002Ring>(this, true, 370, 201, _ssCeiling->getDrawRect().y, getGlobalVar(V_FLYTRAP_RING_DOOR)); + _asRing4 = insertSprite<AsScene1002Ring>(this, false, 334, 191, _ssCeiling->getDrawRect().y, false); + _asRing5 = insertSprite<AsScene1002Ring>(this, false, 425, 184, _ssCeiling->getDrawRect().y, false); + + _asDoor = insertSprite<AsScene1002Door>(tempClipRect); + tempSprite = insertSprite<AsScene1002BoxingGloveHitEffect>(); + _asDoorSpy = insertSprite<AsScene1002DoorSpy>(tempClipRect, this, _asDoor, tempSprite); + _ssPressButton = insertSprite<SsCommonPressButton>(this, 0x00412692, 0x140B60BE, 800, 0); + _asVenusFlyTrap = insertSprite<AsScene1002VenusFlyTrap>(this, _klaymen, false); + addCollisionSprite(_asVenusFlyTrap); + + sendEntityMessage(_klaymen, 0x2007, _asVenusFlyTrap); + + _asOutsideDoorBackground = insertSprite<AsScene1002OutsideDoorBackground>(); + + setRectList(0x004B43A0); + + loadSound(1, 0x60755842); + loadSound(2, 0x616D5821); + +} + +Scene1002::~Scene1002() { +} + +void Scene1002::update() { + Scene::update(); + if (!_isKlaymenFloor && _klaymen->getY() > 230) { + _klaymen->setClipRect(_ssLadderArch->getDrawRect().x, 0, _ssLadderArchPart2->getDrawRect().x2(), _ssLadderArchPart1->getDrawRect().y2()); + _asKlaymenLadderHands->setClipRect(_klaymen->getClipRect()); + deleteSprite(&_ssLadderArchPart3); + _klaymen->clearRepl(); + _isKlaymenFloor = true; + _vm->_gameState.which = 1; + } +} + +uint32 Scene1002::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xE6EE60E1) { + if (getGlobalVar(V_FLYTRAP_RING_DOOR)) + setMessageList(0x004B4428); + else + setMessageList(0x004B4448); + messageResult = 1; + } else if (param.asInteger() == 0x4A845A00) + sendEntityMessage(_klaymen, 0x1014, _asRing1); + else if (param.asInteger() == 0x43807801) + sendEntityMessage(_klaymen, 0x1014, _asRing2); + else if (param.asInteger() == 0x46C26A01) { + if (getGlobalVar(V_FLYTRAP_RING_DOOR)) { + setMessageList(0x004B44B8); + } else { + sendEntityMessage(_klaymen, 0x1014, _asRing3); + if (_asVenusFlyTrap->getX() - 10 < 366 && _asVenusFlyTrap->getX() + 10 > 366) { + setGlobalVar(V_FLYTRAP_RING_EATEN, 1); + setMessageList(0x004B44A8); + } else { + setMessageList(0x004B44A0); + } + } + messageResult = 1; + } else if (param.asInteger() == 0x468C7B11) + sendEntityMessage(_klaymen, 0x1014, _asRing4); + else if (param.asInteger() == 0x42845B19) + sendEntityMessage(_klaymen, 0x1014, _asRing5); + else if (param.asInteger() == 0xC0A07458) + sendEntityMessage(_klaymen, 0x1014, _ssPressButton); + break; + case 0x1024: + sendMessage(_parentModule, 0x1024, param.asInteger()); + break; + case 0x2000: + if (_isClimbingLadder) { + setMessageList2(0x004B43D0); + } else { + if (_klaymen->getY() > 420) { + sendEntityMessage(_klaymen, 0x1014, _asVenusFlyTrap); + setMessageList2(0x004B4480); + } else if (_klaymen->getY() > 227) { + setMessageList2(0x004B41E0); + } else { + setMessageList2(0x004B4148); + } + } + break; + case 0x2002: + _messageList = NULL; + break; + case 0x2005: + _isClimbingLadder = true; + setRectList(0x004B4418); + break; + case 0x2006: + _isClimbingLadder = false; + setRectList(0x004B43A0); + break; + case 0x4806: + if (sender == _asRing1) { + setGlobalVar(V_RADIO_ENABLED, 0); + playSound(0, 0x665198C0); + } else if (sender == _asRing2) { + setGlobalVar(V_RADIO_ENABLED, 0); + playSound(0, 0xE2D389C0); + } else if (sender == _asRing3) { + setGlobalVar(V_RADIO_ENABLED, 0); + playSound(1); + sendMessage(_asDoor, 0x4808, 0); + sendMessage(_asOutsideDoorBackground, 0x4808, 0); + } else if (sender == _asRing4) { + setGlobalVar(V_RADIO_ENABLED, 0); + playSound(0, 0xE0558848); + } else if (sender == _asRing5) { + setGlobalVar(V_RADIO_ENABLED, 1); + playSound(0, 0x44014282); + } + break; + case 0x4807: + if (sender == _asRing3) { + playSound(2); + sendMessage(_asDoor, 0x4809, 0); + sendMessage(_asOutsideDoorBackground, 0x4809, 0); + } else if (sender == _asVenusFlyTrap) { + if (getGlobalVar(V_FLYTRAP_RING_DOOR)) { + sendMessage(_asRing3, 0x4807, 0); + } + } + break; + case 0x480B: + sendEntityMessage(_klaymen, 0x1014, _asDoorSpy); + break; + case 0x480F: + setGlobalVar(V_RADIO_ENABLED, 0); + playSound(1); + sendMessage(_asDoor, 0x4808, 0); + sendMessage(_asOutsideDoorBackground, 0x4808, 0); + break; + case 0x8000: + setSpriteSurfacePriority(_ssCeiling, 995); + setSpriteSurfacePriority(_ssLadderArch, 995); + break; + case 0x8001: + setSpriteSurfacePriority(_ssCeiling, 1015); + setSpriteSurfacePriority(_ssLadderArch, 1015); + break; + } + return messageResult; +} + +// StaticScene + +StaticScene::StaticScene(NeverhoodEngine *vm, Module *parentModule, uint32 backgroundFileHash, uint32 cursorFileHash) + : Scene(vm, parentModule) { + + SetMessageHandler(&StaticScene::handleMessage); + + setBackground(backgroundFileHash); + setPalette(backgroundFileHash); + insertPuzzleMouse(cursorFileHash, 20, 620); +} + +uint32 StaticScene::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; + } + return 0; +} + +// Scene1004 + +AsScene1004TrashCan::AsScene1004TrashCan(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + _x = 330; + _y = 327; + createSurface(800, 56, 50); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1004TrashCan::handleMessage); +} + +uint32 AsScene1004TrashCan::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x225A8587) + playSound(0, 0x109AFC4C); + break; + case 0x2002: + startAnimation(0xEB312C11, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return 0; +} + +Scene1004::Scene1004(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _paletteAreaStatus(-1) { + + Sprite *tempSprite; + + SetUpdateHandler(&Scene1004::update); + SetMessageHandler(&Scene1004::handleMessage); + + setBackground(0x50C03005); + + if (getGlobalVar(V_ENTRANCE_OPEN)) { + setPalette(0xA30BA329); + _palette->addBasePalette(0xA30BA329, 0, 256, 0); + } else { + setPalette(0x50C03005); + _palette->addBasePalette(0x50C03005, 0, 256, 0); + } + addEntity(_palette); + + insertScreenMouse(0x03001504); + + if (which < 0) { + // Restoring game + setRectList(0x004B7C70); + insertKlaymen<KmScene1004>(330, 327); + setMessageList(0x004B7C18); + } else if (which == 1) { + // Klaymen returning from reading a note + setRectList(0x004B7C70); + insertKlaymen<KmScene1004>(330, 327); + setMessageList(0x004B7C08); + } else { + // Klaymen coming down the ladder + loadDataResource(0x01900A04); + insertKlaymen<KmScene1004>(_dataResource.getPoint(0x80052A29).x, 27); + setMessageList(0x004B7BF0); + } + + updatePaletteArea(); + + _asKlaymenLadderHands = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + + insertStaticSprite(0x800034A0, 1100); + insertStaticSprite(0x64402020, 1100); + insertStaticSprite(0x3060222E, 1300); + tempSprite = insertStaticSprite(0x0E002004, 1300); + + _klaymen->setClipRect(0, tempSprite->getDrawRect().y, 640, 480); + _asKlaymenLadderHands->setClipRect(_klaymen->getClipRect()); + + _asTrashCan = insertSprite<AsScene1004TrashCan>(); + +} + +void Scene1004::update() { + Scene::update(); + updatePaletteArea(); +} + +uint32 Scene1004::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x926500A1) { + setMessageList(0x004B7C20); + messageResult = 1; + } + break; + case 0x2000: + loadDataResource(0x01900A04); + break; + case 0x2001: + setRectList(0x004B7C70); + break; + case 0x2002: + sendMessage(_asTrashCan, 0x2002, 0); + break; + } + return messageResult; +} + +void Scene1004::updatePaletteArea() { + if (_klaymen->getY() < 150) { + if (_paletteAreaStatus != 0) { + _paletteAreaStatus = 0; + _palette->addBasePalette(0x406B0D10, 0, 64, 0); + _palette->startFadeToPalette(12); + } + } else { + if (_paletteAreaStatus != 1) { + _paletteAreaStatus = 1; + _palette->addBasePalette(0x24332243, 0, 64, 0); + _palette->startFadeToPalette(12); + } + } +} + +// Scene1005 + +Scene1005::Scene1005(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene1005::handleMessage); + + if (getGlobalVar(V_ENTRANCE_OPEN)) { + setBackground(0x2800E011); + setPalette(0x2800E011); + insertStaticSprite(0x492D5AD7, 100); + insertPuzzleMouse(0x0E015288, 20, 620); + } else { + setBackground(0x8870A546); + setPalette(0x8870A546); + insertStaticSprite(0x40D1E0A9, 100); + insertStaticSprite(0x149C00A6, 100); + insertPuzzleMouse(0x0A54288F, 20, 620); + } + + drawTextToBackground(); + +} + +uint32 Scene1005::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; + } + return 0; +} + +void Scene1005::drawTextToBackground() { + TextResource textResource(_vm); + const char *textStart, *textEnd; + int16 y = 36; + uint32 textIndex = getTextIndex(); + FontSurface *fontSurface = FontSurface::createFontSurface(_vm, getGlobalVar(V_ENTRANCE_OPEN) ? 0x283CE401 : 0xC6604282); + textResource.load(0x80283101); + textStart = textResource.getString(textIndex, textEnd); + while (textStart < textEnd) { + fontSurface->drawString(_background->getSurface(), 188, y, (const byte*)textStart); + y += 36; + textStart += strlen(textStart) + 1; + } + delete fontSurface; +} + +uint32 Scene1005::getTextIndex() { + uint32 textIndex; + textIndex = getTextIndex1(); + if (getGlobalVar(V_ENTRANCE_OPEN)) { + textIndex = getTextIndex2(); + } + if (getGlobalVar(V_TEXT_FLAG1) && getGlobalVar(V_TEXT_INDEX) == textIndex) { + textIndex = getTextIndex3(); + } else { + setGlobalVar(V_TEXT_FLAG1, 1); + setGlobalVar(V_TEXT_INDEX, textIndex); + } + return textIndex; +} + +uint32 Scene1005::getTextIndex1() { + uint32 textIndex; + if (getGlobalVar(V_WORLDS_JOINED)) { + if (!getGlobalVar(V_DOOR_PASSED)) + textIndex = 18; + else if (!getGlobalVar(V_ROBOT_TARGET)) + textIndex = 19; + else if (getGlobalVar(V_ROBOT_HIT)) { + if (!getGlobalVar(V_ENTRANCE_OPEN)) + textIndex = 23; + else if (!getSubVar(VA_HAS_KEY, 0) && !getSubVar(VA_IS_KEY_INSERTED, 0)) + textIndex = 24; + else if (!getGlobalVar(V_HAS_FINAL_KEY)) + textIndex = 26; + else if (!getSubVar(VA_HAS_KEY, 1) && !getSubVar(VA_IS_KEY_INSERTED, 1)) + textIndex = 27; + else if (!getGlobalVar(V_HAS_FINAL_KEY)) + textIndex = 28; + else + textIndex = 29; + } else if (!getGlobalVar(V_FELL_DOWN_HOLE)) + textIndex = 20; + else if (!getGlobalVar(V_SEEN_SYMBOLS_NO_LIGHT)) + textIndex = 21; + else + textIndex = 22; + } else if (getGlobalVar(V_BOLT_DOOR_UNLOCKED)) { + if (!getGlobalVar(V_WALL_BROKEN)) + textIndex = 12; + else if (!getGlobalVar(V_STAIRS_DOWN_ONCE)) + textIndex = 13; + else if (!getGlobalVar(V_RADIO_ENABLED)) + textIndex = 50; + else if (!getGlobalVar(V_UNUSED)) + textIndex = 14; + else if (!getGlobalVar(V_BEEN_SHRINKING_ROOM)) + textIndex = 15; + else if (!getGlobalVar(V_BEEN_STATUE_ROOM)) + textIndex = 16; + else + textIndex = 17; + } else if (!getGlobalVar(V_FLYTRAP_RING_EATEN)) { + textIndex = 0; + } else if (getGlobalVar(V_CREATURE_EXPLODED)) { + if (!getGlobalVar(V_TILE_PUZZLE_SOLVED)) + textIndex = 4; + else if (!getGlobalVar(V_HAS_TEST_TUBE)) + textIndex = 5; + else if (!getSubVar(VA_LOCKS_DISABLED, 0x40119852)) + textIndex = 6; + else if (!getGlobalVar(V_WATER_RUNNING)) + textIndex = 7; + else if (!getGlobalVar(V_NOTES_PUZZLE_SOLVED)) + textIndex = 8; + else if (!getSubVar(VA_LOCKS_DISABLED, 0x304008D2)) + textIndex = 9; + else if (!getSubVar(VA_LOCKS_DISABLED, 0x01180951)) + textIndex = 10; + else + textIndex = 11; + } else if (!getGlobalVar(V_CREATURE_ANGRY)) { + textIndex = 1; + } else if (getGlobalVar(V_TNT_DUMMY_BUILT)) { + textIndex = 3; + } else { + textIndex = 2; + } + return textIndex; +} + +uint32 Scene1005::getTextIndex2() { + uint32 textIndex = getGlobalVar(V_TEXT_COUNTING_INDEX1); + if (textIndex + 1 >= 10) { + setGlobalVar(V_TEXT_COUNTING_INDEX1, 0); + textIndex = 0; + } else { + setGlobalVar(V_TEXT_COUNTING_INDEX1, textIndex + 1); + } + return textIndex + 40; +} + +uint32 Scene1005::getTextIndex3() { + uint32 textIndex = getGlobalVar(V_TEXT_COUNTING_INDEX2); + if (textIndex + 1 >= 10) { + setGlobalVar(V_TEXT_COUNTING_INDEX2, 0); + textIndex = 0; + } else { + setGlobalVar(V_TEXT_COUNTING_INDEX2, textIndex + 1); + } + return textIndex + 30; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1000.h b/engines/neverhood/modules/module1000.h new file mode 100644 index 0000000000..9977590a6a --- /dev/null +++ b/engines/neverhood/modules/module1000.h @@ -0,0 +1,300 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1000_H +#define NEVERHOOD_MODULES_MODULE1000_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1000 + +class Module1000 : public Module { +public: + Module1000(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1000(); +protected: + int _sceneNum; + uint32 _musicFileHash; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1001 + +class AsScene1001Door : public AnimatedSprite { +public: + AsScene1001Door(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void hammerHitsDoor(); + void stShowIdleDoor(); + void stBustedDoorMove(); + void stBustedDoorGone(); +}; + +class AsScene1001Hammer : public AnimatedSprite { +public: + AsScene1001Hammer(NeverhoodEngine *vm, Sprite *asDoor); +protected: + Sprite *_asDoor; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1001Window : public AnimatedSprite { +public: + AsScene1001Window(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1001Lever : public AnimatedSprite { +public: + AsScene1001Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int deltaXType); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsCommonButtonSprite : public StaticSprite { +public: + SsCommonButtonSprite(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, int surfacePriority, uint32 soundFileHash); +protected: + Scene *_parentScene; + uint32 _soundFileHash; + int16 _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1001 : public Scene { +public: + Scene1001(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1001(); +protected: + Sprite *_asHammer; + Sprite *_asDoor; + Sprite *_asWindow; + Sprite *_asLever; + Sprite *_ssButton; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// TODO: Move this to some common file since it's used several times + +class StaticScene : public Scene { +public: + StaticScene(NeverhoodEngine *vm, Module *parentModule, uint32 backgroundFileHash, uint32 cursorFileHash); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1002 + +class AsScene1002Ring : public AnimatedSprite { +public: + AsScene1002Ring(NeverhoodEngine *vm, Scene *parentScene, bool isSpecial, int16 x, int16 y, int16 clipY1, bool isRingLow); +protected: + Scene *_parentScene; + bool _isSpecial; + void update(); + uint32 hmRingIdle(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmRingPulled1(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmRingPulled2(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmRingHangingLow(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmRingReleased(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1002Door : public StaticSprite { +public: + AsScene1002Door(NeverhoodEngine *vm, NRect &clipRect); +protected: + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suOpenDoor(); + void suCloseDoor(); +}; + +class AsScene1002BoxingGloveHitEffect : public AnimatedSprite { +public: + AsScene1002BoxingGloveHitEffect(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1002DoorSpy : public AnimatedSprite { +public: + AsScene1002DoorSpy(NeverhoodEngine *vm, NRect &clipRect, Scene *parentScene, Sprite *asDoor, Sprite *asScene1002BoxingGloveHitEffect); +protected: + Scene *_parentScene; + Sprite *_asDoor; + Sprite *_asBoxingGloveHitEffect; + NRect _clipRect; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmDoorSpyAnimation(int messageNum, const MessageParam ¶m, Entity *sender); + void suDoorSpy(); + void stDoorSpyIdle(); + void stDoorSpyBoxingGlove(); +}; + +class SsCommonPressButton : public StaticSprite { +public: + SsCommonPressButton(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash); + void setFileHashes(uint32 fileHash1, uint32 fileHash2); +protected: + Scene *_parentScene; + uint32 _soundFileHash; + uint32 _fileHashes[2]; + int _status; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1002VenusFlyTrap : public AnimatedSprite { +public: + AsScene1002VenusFlyTrap(NeverhoodEngine *vm, Scene *parentScene, Sprite *klaymen, bool isSecond); +protected: + Scene *_parentScene; + Sprite *_klaymen; + int _countdown; + bool _isSecond; + void update(); + void upIdle(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmAnimationSimple(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmAnimationExt(int messageNum, const MessageParam ¶m, Entity *sender); + void stWalkBack(); + void stWalk(); + void stRelease(); + void stGrabRing(); + void stRingGrabbed(); + void stKlaymenInside(); + void stIdle(); + void stKlaymenInsideMoving(); + void stSpitOutKlaymen(); + void swallowKlaymen(); +}; + +class AsScene1002OutsideDoorBackground : public AnimatedSprite { +public: + AsScene1002OutsideDoorBackground(NeverhoodEngine *vm); +protected: + int _countdown; + bool _isDoorClosed; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender); + void stOpenDoor(); + void stCloseDoor(); + void stDoorClosed(); +}; + +class AsScene1002KlaymenLadderHands : public AnimatedSprite { +public: + AsScene1002KlaymenLadderHands(NeverhoodEngine *vm, Klaymen *klaymen); +protected: + Klaymen *_klaymen; + void update(); +}; + +class AsScene1002KlaymenPeekHand : public AnimatedSprite { +public: + AsScene1002KlaymenPeekHand(NeverhoodEngine *vm, Scene *parentScene, Klaymen *klaymen); +protected: + Scene *_parentScene; + Klaymen *_klaymen; + bool _isClipRectSaved; + NRect _savedClipRect; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1002 : public Scene { +public: + Scene1002(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1002(); +protected: + Sprite *_asRing1; + Sprite *_asRing2; + Sprite *_asRing3; + Sprite *_asRing4; + Sprite *_asRing5; + Sprite *_asDoor; + Sprite *_asDoorSpy; + Sprite *_asVenusFlyTrap; + Sprite *_ssLadderArch; + Sprite *_ssLadderArchPart1; + Sprite *_ssLadderArchPart2; + Sprite *_ssLadderArchPart3; + Sprite *_ssCeiling; + Sprite *_asKlaymenLadderHands; + Sprite *_asKlaymenPeekHand; + Sprite *_asOutsideDoorBackground; + Sprite *_ssPressButton; + bool _isKlaymenFloor; + bool _isClimbingLadder; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1004 + +class AsScene1004TrashCan : public AnimatedSprite { +public: + AsScene1004TrashCan(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1004 : public Scene { +public: + Scene1004(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_asKlaymenLadderHands; + Sprite *_asTrashCan; + int _paletteAreaStatus; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void updatePaletteArea(); +}; + +// Scene1005 + +class Scene1005 : public Scene { +public: + Scene1005(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void drawTextToBackground(); + uint32 getTextIndex(); + uint32 getTextIndex1(); + uint32 getTextIndex2(); + uint32 getTextIndex3(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1000_H */ diff --git a/engines/neverhood/modules/module1100.cpp b/engines/neverhood/modules/module1100.cpp new file mode 100644 index 0000000000..5a5e52e5b0 --- /dev/null +++ b/engines/neverhood/modules/module1100.cpp @@ -0,0 +1,707 @@ +/* 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/module1100.h" +#include "neverhood/gamemodule.h" +#include "neverhood/navigationscene.h" + +namespace Neverhood { + +static const uint32 kModule1100SoundList[] = { + 0x90805C50, + 0xB288D450, + 0x98C05840, + 0x98A01500, + 0xB4005E50, + 0x92025040, + 0x90035066, + 0x74E01054, + 0 +}; + +Module1100::Module1100(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which < 0) { + createScene(_vm->gameState().sceneNum, -1); + } else if (which == 1) { + createScene(8, 1); + } else { + createScene(8, 3); + } + + _vm->_soundMan->addSoundList(0x0002C818, kModule1100SoundList); + _vm->_soundMan->setSoundListParams(kModule1100SoundList, true, 50, 600, 20, 250); + _vm->_soundMan->setSoundParams(0x74E01054, false, 100, 200, 10, 20); + _vm->_soundMan->setSoundVolume(0x74E01054, 60); + _vm->_soundMan->playTwoSounds(0x0002C818, 0x41861371, 0x43A2507F, 0); + +} + +Module1100::~Module1100() { + _vm->_soundMan->deleteGroup(0x0002C818); +} + +void Module1100::createScene(int sceneNum, int which) { + static const uint32 kSmackerFileHashList06[] = {0x10880805, 0x1088081D, 0}; + static const uint32 kSmackerFileHashList07[] = {0x00290321, 0x01881000, 0}; + debug("Module1100::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _countdown = 65; + createNavigationScene(0x004B8430, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _countdown = 50; + createNavigationScene(0x004B8460, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + if (getGlobalVar(V_ROBOT_TARGET)) { + createNavigationScene(0x004B84F0, which); + } else { + createNavigationScene(0x004B8490, which); + } + break; + case 3: + _vm->gameState().sceneNum = 3; + if (getGlobalVar(V_ROBOT_TARGET)) { + createNavigationScene(0x004B8580, which); + } else { + createNavigationScene(0x004B8550, which); + } + break; + case 4: + _vm->gameState().sceneNum = 4; + _childObject = new Scene1105(_vm, this); + break; + case 5: + _vm->gameState().sceneNum = 5; + if (getGlobalVar(V_ROBOT_TARGET)) + createSmackerScene(0x04180001, true, false, false); + else + createSmackerScene(0x04180007, true, false, false); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->_soundMan->deleteSoundGroup(0x0002C818); + createSmackerScene(kSmackerFileHashList06, true, true, false); + break; + case 7: + _vm->gameState().sceneNum = 7; + _vm->_soundMan->setSoundParams(0x74E01054, false, 0, 0, 0, 0); + createSmackerScene(kSmackerFileHashList07, true, true, false); + break; + case 8: + _vm->gameState().sceneNum = 8; + _childObject = new Scene1109(_vm, this, which); + break; + case 1002: + _vm->gameState().sceneNum = 2; + _countdown = 40; + _vm->_soundMan->setTwoSoundsPlayFlag(true); + createSmackerScene(0x00012211, true, true, false); + break; + } + SetUpdateHandler(&Module1100::updateScene); + _childObject->handleUpdate(); +} + +void Module1100::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + _countdown = 0; + _vm->_soundMan->playTwoSounds(0x0002C818, 0x48498E46, 0x50399F64, 0); + _vm->_soundMan->setSoundVolume(0x48498E46, 65); + _vm->_soundMan->setSoundVolume(0x50399F64, 65); + if (_moduleResult == 0) + createScene(1, 0); + else if (_moduleResult == 1) + createScene(8, 0); + break; + case 1: + _vm->_soundMan->playTwoSounds(0x0002C818, 0x41861371, 0x43A2507F, 0); + if (getGlobalVar(V_ROBOT_HIT)) { + if (_moduleResult == 0) + createScene(6, -1); + else if (_moduleResult == 1) + createScene(0, 1); + } else { + if (_moduleResult == 0) + createScene(2, 0); + else if (_moduleResult == 1) + createScene(0, 1); + } + break; + case 2: + _vm->_soundMan->setSoundParams(0x74E01054, false, 0, 0, 0, 0); + if (_navigationAreaType == 3) + createScene(7, -1); + else if (_moduleResult == 1) + createScene(3, 0); + else if (_moduleResult == 2) + createScene(1002, -1); + break; + case 3: + if (_moduleResult == 0) + createScene(4, 0); + else if (_moduleResult == 1) + createScene(2, 3); + break; + case 4: + if (_moduleResult == 0) + createScene(3, 0); + else if (_moduleResult == 1) + createScene(5, -1); + break; + case 5: + _vm->_soundMan->setTwoSoundsPlayFlag(false); + if (getGlobalVar(V_ROBOT_TARGET)) + createScene(3, 0); + else + createScene(4, 0); + break; + case 6: + _vm->_soundMan->setTwoSoundsPlayFlag(false); + leaveModule(1); + break; + case 7: + _vm->_soundMan->setTwoSoundsPlayFlag(false); + createScene(2, 2); + break; + case 8: + if (_moduleResult == 0) + createScene(0, 0); + else if (_moduleResult == 1) + leaveModule(0); + break; + case 1002: + _vm->_soundMan->setTwoSoundsPlayFlag(false); + _countdown = 0; + _vm->_soundMan->playTwoSounds(0x0002C818, 0x48498E46, 0x50399F64, 0); + createScene(1, 1); + break; + } + } else { + switch (_sceneNum) { + case 0: + if (navigationScene()->isWalkingForward() && _countdown != 0 && (--_countdown == 0)) { + _vm->_soundMan->playTwoSounds(0x0002C818, 0x48498E46, 0x50399F64, 0); + _vm->_soundMan->setSoundVolume(0x48498E46, 65); + _vm->_soundMan->setSoundVolume(0x50399F64, 65); + } + break; + case 1: + if (navigationScene()->isWalkingForward() && _countdown != 0 && (--_countdown == 0)) + _vm->_soundMan->playTwoSounds(0x0002C818, 0x41861371, 0x43A2507F, 0); + break; + case 2: + _vm->_soundMan->setSoundParams(0x74E01054, !navigationScene()->isWalkingForward(), 0, 0, 0, 0); + break; + case 5: + case 6: + case 7: + case 1002: + if (_countdown != 0 && (--_countdown == 0)) { + _vm->_soundMan->playTwoSounds(0x0002C818, 0x48498E46, 0x50399F64, 0); + _vm->_soundMan->setSoundVolume(0x48498E46, 65); + _vm->_soundMan->setSoundVolume(0x50399F64, 65); + } + break; + } + } +} + +static const uint32 kScene1105FileHashes[] = { + 0x00028006, + 0x0100A425, + 0x63090415, + 0x082100C4, + 0x0068C607, + 0x00018344, + 0x442090E4, + 0x0400E004, + 0x5020A054, + 0xB14A891E +}; + +static const uint32 kScene1105BackgroundFileHashes[] = { + 0x20018662, + 0x20014202, + 0x20012202, + 0x20010002 // CHECKME: This used ?? +}; + +static const uint32 kSsScene1105SymbolDieFileHashes[] = { + 0, + 0x90898414, + 0x91098414, + 0x92098414, + 0x94098414, + 0x98098414, + 0x80098414, + 0xB0098414, + 0xD0098414, + 0x10098414 +}; + +SsScene1105Button::SsScene1105Button(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, NRect &collisionBounds) + : StaticSprite(vm, fileHash, 200), _parentScene(parentScene), _countdown(0) { + + _collisionBounds = collisionBounds; + SetMessageHandler(&SsScene1105Button::handleMessage); + SetUpdateHandler(&SsScene1105Button::update); + setVisible(false); +} + +void SsScene1105Button::update() { + if (_countdown != 0 && (--_countdown == 0)) { + sendMessage(_parentScene, 0x4807, 0); + setVisible(false); + } +} + +uint32 SsScene1105Button::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0) { + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + } + break; + case 0x480B: + _countdown = 8; + setVisible(true); + playSound(0, 0x44141000); + break; + } + return messageResult; +} + +SsScene1105Symbol::SsScene1105Symbol(NeverhoodEngine *vm, uint32 fileHash, int16 x, int16 y) + : StaticSprite(vm, 0) { + + loadSprite(fileHash, kSLFCenteredDrawOffset | kSLFSetPosition, 200, x, y); +} + +void SsScene1105Symbol::hide() { + setVisible(false); + _needRefresh = true; + updatePosition(); +} + +SsScene1105SymbolDie::SsScene1105SymbolDie(NeverhoodEngine *vm, uint dieIndex, int16 x, int16 y) + : StaticSprite(vm, 1100), _dieIndex(dieIndex) { + + _x = x; + _y = y; + createSurface(200, 50, 50); + loadSymbolSprite(); + SetMessageHandler(&SsScene1105SymbolDie::handleMessage); +} + +uint32 SsScene1105SymbolDie::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + loadSymbolSprite(); + break; + } + return messageResult; +} + +void SsScene1105SymbolDie::loadSymbolSprite() { + loadSprite(kSsScene1105SymbolDieFileHashes[getSubVar(VA_CURR_DICE_NUMBERS, _dieIndex)], kSLFCenteredDrawOffset); +} + +void SsScene1105SymbolDie::hide() { + setVisible(false); + _needRefresh = true; + updatePosition(); +} + +AsScene1105TeddyBear::AsScene1105TeddyBear(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + createSurface(100, 556, 328); + _x = 320; + _y = 240; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1105TeddyBear::handleMessage); + startAnimation(0x65084002, 0, -1); + _newStickFrameIndex = 0; + setVisible(false); + _needRefresh = true; + updatePosition(); + loadSound(0, 0xCE840261); + loadSound(1, 0xCCA41A62); +} + +uint32 AsScene1105TeddyBear::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + if (getGlobalVar(V_ROBOT_TARGET)) { + startAnimation(0x6B0C0432, 0, -1); + playSound(0); + } else { + startAnimation(0x65084002, 0, -1); + playSound(1); + } + break; + case 0x3002: + sendMessage(_parentScene, 0x2003, 0); + stopAnimation(); + break; + } + return messageResult; +} + +void AsScene1105TeddyBear::show() { + setVisible(true); + _needRefresh = true; + updatePosition(); +} + +void AsScene1105TeddyBear::hide() { + setVisible(false); + _needRefresh = true; + updatePosition(); +} + +SsScene1105OpenButton::SsScene1105OpenButton(NeverhoodEngine *vm, Scene *parentScene) + : StaticSprite(vm, 900), _parentScene(parentScene), _countdown(0), _isClicked(false) { + + loadSprite(0x8228A46C, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400); + setVisible(false); + loadSound(0, 0x44045140); + SetUpdateHandler(&SsScene1105OpenButton::update); + SetMessageHandler(&SsScene1105OpenButton::handleMessage); +} + +void SsScene1105OpenButton::update() { + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) { + setVisible(false); + sendMessage(_parentScene, 0x2001, 0); + } +} + +uint32 SsScene1105OpenButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0 && !_isClicked) { + playSound(0); + setVisible(true); + _isClicked = true; + _countdown = 4; + } + messageResult = 1; + break; + } + return messageResult; +} + +Scene1105::Scene1105(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _countdown(0), _isPanelOpen(false), _isActionButtonClicked(false), _doMoveTeddy(false), + _isClosePanelDone(false), _leaveResult(0), _backgroundIndex(0) { + + Sprite *ssOpenButton; + + _vm->gameModule()->initMemoryPuzzle(); + + SetUpdateHandler(&Scene1105::update); + SetMessageHandler(&Scene1105::handleMessage); + + setBackground(0x20010002); + setPalette(0x20010002); + + _asTeddyBear = insertSprite<AsScene1105TeddyBear>(this); + ssOpenButton = insertSprite<SsScene1105OpenButton>(this); + addCollisionSprite(ssOpenButton); + insertPuzzleMouse(0x10006208, 20, 620); + + loadSound(0, 0x48442057); + loadSound(1, 0xC025014F); + loadSound(2, 0x68E25540); + +} + +uint32 Scene1105::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + if (!_isActionButtonClicked && _backgroundIndex == 0) { + if (_isPanelOpen) { + _isPanelOpen = false; + _backgroundIndex = 15; + SetUpdateHandler(&Scene1105::upClosePanel); + } else + _isPanelOpen = true; + _leaveResult = 0; + } + } + break; + case 0x2001: + showMouse(false); + _backgroundIndex = 24; + SetUpdateHandler(&Scene1105::upOpenPanel); + break; + case 0x2003: + _backgroundIndex = 24; + _leaveResult = 1; + SetUpdateHandler(&Scene1105::upClosePanel); + break; + case 0x4807: + if (sender == _ssActionButton) { + if (getSubVar(VA_GOOD_DICE_NUMBERS, 0) == getSubVar(VA_CURR_DICE_NUMBERS, 0) && + getSubVar(VA_GOOD_DICE_NUMBERS, 1) == getSubVar(VA_CURR_DICE_NUMBERS, 1) && + getSubVar(VA_GOOD_DICE_NUMBERS, 2) == getSubVar(VA_CURR_DICE_NUMBERS, 2)) { + setGlobalVar(V_ROBOT_TARGET, 1); + playSound(2); + _doMoveTeddy = true; + } else { + sendMessage(_asTeddyBear, 0x2002, 0); + } + showMouse(false); + _isActionButtonClicked = true; + } + break; + case 0x4826: + if (_isPanelOpen) { + if (sender == _ssActionButton) { + sendMessage(_ssActionButton, 0x480B, 0); + _isPanelOpen = false; + } else if (!getGlobalVar(V_ROBOT_TARGET)) { + if (sender == _ssSymbol1UpButton) { + if (getSubVar(VA_CURR_DICE_NUMBERS, 0) < 9) { + incSubVar(VA_CURR_DICE_NUMBERS, 0, +1); + sendMessage(_ssSymbol1UpButton, 0x480B, 0); + sendMessage(_ssSymbolDice[0], 0x2000, 0); + } + } else if (sender == _ssSymbol1DownButton) { + if (getSubVar(VA_CURR_DICE_NUMBERS, 0) > 1) { + incSubVar(VA_CURR_DICE_NUMBERS, 0, -1); + sendMessage(_ssSymbol1DownButton, 0x480B, 0); + sendMessage(_ssSymbolDice[0], 0x2000, 0); + } + } else if (sender == _ssSymbol2UpButton) { + if (getSubVar(VA_CURR_DICE_NUMBERS, 1) < 9) { + incSubVar(VA_CURR_DICE_NUMBERS, 1, +1); + sendMessage(_ssSymbol2UpButton, 0x480B, 0); + sendMessage(_ssSymbolDice[1], 0x2000, 0); + } + } else if (sender == _ssSymbol2DownButton) { + if (getSubVar(VA_CURR_DICE_NUMBERS, 1) > 1) { + incSubVar(VA_CURR_DICE_NUMBERS, 1, -1); + sendMessage(_ssSymbol2DownButton, 0x480B, 0); + sendMessage(_ssSymbolDice[1], 0x2000, 0); + } + } else if (sender == _ssSymbol3UpButton) { + if (getSubVar(VA_CURR_DICE_NUMBERS, 2) < 9) { + incSubVar(VA_CURR_DICE_NUMBERS, 2, +1); + sendMessage(_ssSymbol3UpButton, 0x480B, 0); + sendMessage(_ssSymbolDice[2], 0x2000, 0); + } + } else if (sender == _ssSymbol3DownButton) { + if (getSubVar(VA_CURR_DICE_NUMBERS, 2) > 1) { + incSubVar(VA_CURR_DICE_NUMBERS, 2, -1); + sendMessage(_ssSymbol3DownButton, 0x480B, 0); + sendMessage(_ssSymbolDice[2], 0x2000, 0); + } + } + } + } + break; + } + return messageResult; +} + +void Scene1105::createObjects() { + _ssSymbols[0] = insertSprite<SsScene1105Symbol>(kScene1105FileHashes[getSubVar(VA_DICE_MEMORY_SYMBOLS, 0)], 161, 304); + _ssSymbols[1] = insertSprite<SsScene1105Symbol>(kScene1105FileHashes[getSubVar(VA_DICE_MEMORY_SYMBOLS, 1)], 294, 304); + _ssSymbols[2] = insertSprite<SsScene1105Symbol>(kScene1105FileHashes[getSubVar(VA_DICE_MEMORY_SYMBOLS, 2)], 440, 304); + + _ssSymbolDice[0] = insertSprite<SsScene1105SymbolDie>(0, 206, 304); + _ssSymbolDice[1] = insertSprite<SsScene1105SymbolDie>(1, 339, 304); + _ssSymbolDice[2] = insertSprite<SsScene1105SymbolDie>(2, 485, 304); + + _ssSymbol1UpButton = insertSprite<SsScene1105Button>(this, 0x08002860, NRect(146, 362, 192, 403)); + addCollisionSprite(_ssSymbol1UpButton); + _ssSymbol1DownButton = insertSprite<SsScene1105Button>(this, 0x42012460, NRect(147, 404, 191, 442)); + addCollisionSprite(_ssSymbol1DownButton); + _ssSymbol2UpButton = insertSprite<SsScene1105Button>(this, 0x100030A0, NRect(308, 361, 355, 402)); + addCollisionSprite(_ssSymbol2UpButton); + _ssSymbol2DownButton = insertSprite<SsScene1105Button>(this, 0x840228A0, NRect(306, 406, 352, 445)); + addCollisionSprite(_ssSymbol2DownButton); + _ssSymbol3UpButton = insertSprite<SsScene1105Button>(this, 0x20000120, NRect(476, 358, 509, 394)); + addCollisionSprite(_ssSymbol3UpButton); + _ssSymbol3DownButton = insertSprite<SsScene1105Button>(this, 0x08043121, NRect(463, 401, 508, 438)); + addCollisionSprite(_ssSymbol3DownButton); + _ssActionButton = insertSprite<SsScene1105Button>(this, 0x8248AD35, NRect(280, 170, 354, 245)); + addCollisionSprite(_ssActionButton); + + _isPanelOpen = true; + + _asTeddyBear->show(); + + insertPuzzleMouse(0x18666208, 20, 620); + +} + +void Scene1105::upOpenPanel() { + Scene::update(); + if (_backgroundIndex != 0) { + _backgroundIndex--; + if (_backgroundIndex < 6 && _backgroundIndex % 2 == 0) { + uint32 backgroundFileHash = kScene1105BackgroundFileHashes[_backgroundIndex / 2]; + changeBackground(backgroundFileHash); + _palette->addPalette(backgroundFileHash, 0, 256, 0); + } + if (_backgroundIndex == 10) + playSound(0); + if (_backgroundIndex == 0) { + SetUpdateHandler(&Scene1105::update); + _countdown = 2; + } + } +} + +void Scene1105::upClosePanel() { + Scene::update(); + if (_backgroundIndex != 0) { + _backgroundIndex--; + if (_backgroundIndex == 14) { + showMouse(false); + _ssSymbols[0]->hide(); + _ssSymbols[1]->hide(); + _ssSymbols[2]->hide(); + _ssSymbolDice[0]->hide(); + _ssSymbolDice[1]->hide(); + _ssSymbolDice[2]->hide(); + } + if (_backgroundIndex < 6 && _backgroundIndex % 2 == 0) { + uint32 backgroundFileHash = kScene1105BackgroundFileHashes[3 - _backgroundIndex / 2]; // CHECKME + if (_backgroundIndex == 4) { + playSound(1); + _asTeddyBear->hide(); + } + changeBackground(backgroundFileHash); + _palette->addPalette(backgroundFileHash, 0, 256, 0); + } + if (_backgroundIndex == 0) { + SetUpdateHandler(&Scene1105::update); + _isClosePanelDone = true; + } + } +} + +void Scene1105::update() { + + // DEBUG: Show the correct code + debug("(%d, %d) (%d, %d) (%d, %d)", + getSubVar(VA_GOOD_DICE_NUMBERS, 0), getSubVar(VA_CURR_DICE_NUMBERS, 0), + getSubVar(VA_GOOD_DICE_NUMBERS, 1), getSubVar(VA_CURR_DICE_NUMBERS, 1), + getSubVar(VA_GOOD_DICE_NUMBERS, 2), getSubVar(VA_CURR_DICE_NUMBERS, 2)); + + Scene::update(); + if (_countdown != 0 && (--_countdown == 0)) + createObjects(); + if (_isClosePanelDone && !isSoundPlaying(1)) + leaveScene(_leaveResult); + if (_doMoveTeddy && !isSoundPlaying(2)) { + sendMessage(_asTeddyBear, 0x2002, 0); + _doMoveTeddy = false; + } +} + +Scene1109::Scene1109(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene1109::handleMessage); + + setBackground(0x8449E02F); + setPalette(0x8449E02F); + insertScreenMouse(0x9E02B84C); + + _sprite1 = insertStaticSprite(0x600CEF01, 1100); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1109>(140, 436); + setMessageList(0x004B6260); + sendMessage(this, 0x2000, 0); + } else if (which == 1) { + // Klaymen teleporting in + insertKlaymen<KmScene1109>(450, 436); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B6268, false); + sendMessage(this, 0x2000, 1); + } else if (which == 2) { + // Klaymen teleporting out + insertKlaymen<KmScene1109>(450, 436); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B6318, false); + sendMessage(this, 0x2000, 1); + } else if (which == 3) { + // Klaymen returning from teleporter console + insertKlaymen<KmScene1109>(450, 436); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B6278, false); + sendMessage(this, 0x2000, 1); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene1109>(0, 436); + setMessageList(0x004B6258); + sendMessage(this, 0x2000, 0); + } + + _klaymen->setClipRect(0, 0, _sprite1->getDrawRect().x2(), 480); + +} + +uint32 Scene1109::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) { + setRectList(0x004B63A8); + _klaymen->setKlaymenIdleTable3(); + } else { + setRectList(0x004B6398); + _klaymen->setKlaymenIdleTable1(); + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1100.h b/engines/neverhood/modules/module1100.h new file mode 100644 index 0000000000..373f6b703f --- /dev/null +++ b/engines/neverhood/modules/module1100.h @@ -0,0 +1,130 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1100_H +#define NEVERHOOD_MODULES_MODULE1100_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1100 + +class Module1100 : public Module { +public: + Module1100(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1100(); +protected: + int _sceneNum; + int _countdown; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class SsScene1105Button : public StaticSprite { +public: + SsScene1105Button(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash, NRect &collisionBounds); +protected: + Scene *_parentScene; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene1105Symbol : public StaticSprite { +public: + SsScene1105Symbol(NeverhoodEngine *vm, uint32 fileHash, int16 x, int16 y); + void hide(); +}; + +class SsScene1105SymbolDie : public StaticSprite { +public: + SsScene1105SymbolDie(NeverhoodEngine *vm, uint dieIndex, int16 x, int16 y); + void hide(); +protected: + uint _dieIndex; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void loadSymbolSprite(); +}; + +class AsScene1105TeddyBear : public AnimatedSprite { +public: + AsScene1105TeddyBear(NeverhoodEngine *vm, Scene *parentScene); + void show(); + void hide(); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene1105OpenButton : public StaticSprite { +public: + SsScene1105OpenButton(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + int _countdown; + bool _isClicked; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1105 : public Scene { +public: + Scene1105(NeverhoodEngine *vm, Module *parentModule); +protected: + int _countdown; + int _backgroundIndex; + bool _isPanelOpen; + bool _isActionButtonClicked; + bool _doMoveTeddy; + bool _isClosePanelDone; + int _leaveResult; + AsScene1105TeddyBear *_asTeddyBear; + SsScene1105Symbol *_ssSymbols[3]; + SsScene1105SymbolDie *_ssSymbolDice[3]; + Sprite *_ssSymbol1UpButton; + Sprite *_ssSymbol1DownButton; + Sprite *_ssSymbol2UpButton; + Sprite *_ssSymbol2DownButton; + Sprite *_ssSymbol3UpButton; + Sprite *_ssSymbol3DownButton; + Sprite *_ssActionButton; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createObjects(); + void upOpenPanel(); + void upClosePanel(); + void update(); +}; + +class Scene1109 : public Scene { +public: + Scene1109(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1100_H */ diff --git a/engines/neverhood/modules/module1200.cpp b/engines/neverhood/modules/module1200.cpp new file mode 100644 index 0000000000..3be3635645 --- /dev/null +++ b/engines/neverhood/modules/module1200.cpp @@ -0,0 +1,1102 @@ +/* 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/module1200.h" + +namespace Neverhood { + +Module1200::Module1200(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + SetMessageHandler(&Module1200::handleMessage); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 1) + createScene(0, 2); + else + createScene(0, 0); + + _vm->_soundMan->addMusic(0x00478311, 0x62222CAE); + _vm->_soundMan->startMusic(0x62222CAE, 0, 0); +} + +Module1200::~Module1200() { + _vm->_soundMan->deleteMusicGroup(0x00478311); +} + +void Module1200::createScene(int sceneNum, int which) { + debug("Module1200::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _childObject = new Scene1201(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _childObject = new Scene1202(_vm, this); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->_soundMan->stopMusic(0x62222CAE, 0, 0); + createSmackerScene(0x31890001, true, true, false); + setGlobalVar(V_SEEN_CREATURE_EXPLODE_VID, 1); + break; + } + SetUpdateHandler(&Module1200::updateScene); + _childObject->handleUpdate(); +} + +void Module1200::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(1, 0); + else if (_moduleResult == 2) { + if (getGlobalVar(V_CREATURE_EXPLODED) && !getGlobalVar(V_SEEN_CREATURE_EXPLODE_VID)) + createScene(2, -1); + else + leaveModule(1); + } else + leaveModule(0); + break; + case 1: + createScene(0, 1); + break; + case 2: + _vm->_soundMan->startMusic(0x62222CAE, 0, 0); + createScene(0, 3); + break; + } + } +} + +// Scene1201 + +static const uint32 kScene1201InitArray[] = { + 1, 0, 2, 4, 5, 3, 6, 7, 8, 10, 9, 11, 13, 14, 12, 16, 17, 15 +}; + +static const NPoint kScene1201PointArray[] = { + {218, 193}, {410, 225}, {368, 277}, + {194, 227}, {366, 174}, {458, 224}, + {242, 228}, {512, 228}, {458, 277}, + {217, 233}, {458, 173}, {410, 276}, + {203, 280}, {371, 226}, {508, 279}, + {230, 273}, {410, 171}, {493, 174} +}; + +static const uint32 kScene1201TntFileHashList1[] = { + 0x2098212D, 0x1600437E, 0x1600437E, + 0x00A840E3, 0x1A1830F6, 0x1A1830F6, + 0x00212062, 0x384010B6, 0x384010B6, + 0x07A01080, 0xD80C2837, 0xD80C2837, + 0x03A22092, 0xD8802CB6, 0xD8802CB6, + 0x03A93831, 0xDA460476, 0xDA460476 +}; + +static const uint32 kScene1201TntFileHashList2[] = { + 0x3040C676, 0x10914448, 0x10914448, + 0x3448A066, 0x1288C049, 0x1288C049, + 0x78C0E026, 0x3098D05A, 0x3098D05A, + 0x304890E6, 0x1284E048, 0x1284E048, + 0xB140A1E6, 0x5088A068, 0x5088A068, + 0x74C4C866, 0x3192C059, 0x3192C059 +}; + +SsScene1201Tnt::SsScene1201Tnt(NeverhoodEngine *vm, uint32 elemIndex, uint32 pointIndex, int16 clipY2) + : StaticSprite(vm, 900) { + + int16 x = kScene1201PointArray[pointIndex].x; + int16 y = kScene1201PointArray[pointIndex].y; + if (x < 300) + loadSprite(kScene1201TntFileHashList1[elemIndex], kSLFDefDrawOffset | kSLFDefPosition, 50); + else + loadSprite(kScene1201TntFileHashList2[elemIndex], kSLFCenteredDrawOffset | kSLFSetPosition, 50, x, y); + setClipRect(0, 0, 640, clipY2); +} + +AsScene1201Tape::AsScene1201Tape(NeverhoodEngine *vm, Scene *parentScene, uint32 nameHash, int surfacePriority, int16 x, int16 y, uint32 fileHash) + : AnimatedSprite(vm, fileHash, surfacePriority, x, y), _parentScene(parentScene), _nameHash(nameHash) { + + if (!getSubVar(VA_HAS_TAPE, _nameHash) && !getSubVar(VA_IS_TAPE_INSERTED, _nameHash)) { + SetMessageHandler(&AsScene1201Tape::handleMessage); + } else { + setVisible(false); + SetMessageHandler(NULL); + } +} + +uint32 AsScene1201Tape::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_TAPE, _nameHash, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +AsScene1201TntManRope::AsScene1201TntManRope(NeverhoodEngine *vm, bool isDummyHanging) + : AnimatedSprite(vm, 1200) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1201TntManRope::handleMessage); + createSurface(10, 34, 149); + _x = 202; + _y = -32; + if (isDummyHanging) { + startAnimation(0x928F0C10, 15, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + } else { + startAnimation(0x928F0C10, 0, -1); + _newStickFrameIndex = 0; + } +} + +uint32 AsScene1201TntManRope::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02060018) + playSound(0, 0x47900E06); + break; + case 0x2006: + startAnimation(0x928F0C10, 1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + break; + } + return messageResult; +} + +AsScene1201RightDoor::AsScene1201RightDoor(NeverhoodEngine *vm, Sprite *klaymen, bool isOpen) + : AnimatedSprite(vm, 1100), _klaymen(klaymen), _countdown(0) { + + createSurface1(0xD088AC30, 100); + _x = 320; + _y = 240; + SetUpdateHandler(&AsScene1201RightDoor::update); + SetMessageHandler(&AsScene1201RightDoor::handleMessage); + _newStickFrameIndex = STICK_LAST_FRAME; + if (isOpen) { + startAnimation(0xD088AC30, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + _countdown = 25; + } else { + stopAnimation(); + setVisible(false); + } +} + +void AsScene1201RightDoor::update() { + if (_countdown != 0 && (--_countdown == 0)) + stCloseDoor(); + AnimatedSprite::update(); +} + +uint32 AsScene1201RightDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + case 0x4829: + stOpenDoor(); + break; + } + return messageResult; +} + +void AsScene1201RightDoor::stOpenDoor() { + startAnimation(0xD088AC30, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + setVisible(true); + playSound(0, calcHash("fxDoorOpen20")); +} + +void AsScene1201RightDoor::stCloseDoor() { + startAnimation(0xD088AC30, -1, -1); + _playBackwards = true; + setVisible(true); + playSound(0, calcHash("fxDoorClose20")); + NextState(&AsScene1201RightDoor::stCloseDoorDone); +} + +void AsScene1201RightDoor::stCloseDoorDone() { + stopAnimation(); + setVisible(false); +} + +AsScene1201KlaymenHead::AsScene1201KlaymenHead(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200) { + + createSurface(1200, 69, 98); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1201KlaymenHead::handleMessage); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); + setVisible(false); +} + +uint32 AsScene1201KlaymenHead::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2006: + _x = 436; + _y = 339; + startAnimation(0xA060C599, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + gotoNextState(); + break; + } + return messageResult; +} + +AsScene1201TntMan::AsScene1201TntMan(NeverhoodEngine *vm, Scene *parentScene, Sprite *asTntManRope, bool isComingDown) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _asTntManRope(asTntManRope), + _isMoving(false) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1201TntMan::handleMessage); + createSurface(990, 106, 181); + _x = 201; + if (isComingDown) { + _y = 297; + stComingDown(); + } else { + _y = 334; + stStanding(); + } +} + +AsScene1201TntMan::~AsScene1201TntMan() { + _vm->_soundMan->deleteSoundGroup(0x01D00560); +} + +uint32 AsScene1201TntMan::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x092870C0) + sendMessage(_asTntManRope, 0x2006, 0); + else if (param.asInteger() == 0x11CA0144) + playSound(0, 0x51800A04); + break; + case 0x1011: + sendMessage(_parentScene, 0x2002, 0); + messageResult = 1; + break; + case 0x480B: + if (!_isMoving) { + _sprite = (Sprite*)sender; + stMoving(); + } + break; + } + return messageResult; + +} + +uint32 AsScene1201TntMan::hmComingDown(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = AsScene1201TntMan::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1201TntMan::suMoving() { + _x = _sprite->getX() + 100; +} + +void AsScene1201TntMan::stStanding() { + startAnimation(0x654913D0, 0, -1); + SetMessageHandler(&AsScene1201TntMan::handleMessage); + SetSpriteUpdate(NULL); +} + +void AsScene1201TntMan::stComingDown() { + startAnimation(0x356803D0, 0, -1); + SetMessageHandler(&AsScene1201TntMan::hmComingDown); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); + NextState(&AsScene1201TntMan::stStanding); +} + +void AsScene1201TntMan::stMoving() { + _vm->_soundMan->addSound(0x01D00560, 0x4B044624); + _vm->_soundMan->playSoundLooping(0x4B044624); + _isMoving = true; + startAnimation(0x85084190, 0, -1); + SetMessageHandler(&AsScene1201TntMan::handleMessage); + SetSpriteUpdate(&AsScene1201TntMan::suMoving); + _newStickFrameIndex = STICK_LAST_FRAME; +} + +AsScene1201TntManFlame::AsScene1201TntManFlame(NeverhoodEngine *vm, Sprite *asTntMan) + : AnimatedSprite(vm, 1200), _asTntMan(asTntMan) { + + createSurface1(0x828C0411, 995); + SetUpdateHandler(&AsScene1201TntManFlame::update); + SetMessageHandler(&Sprite::handleMessage); + SetSpriteUpdate(&AsScene1201TntManFlame::suUpdate); + startAnimation(0x828C0411, 0, -1); + setVisible(false); +} + +AsScene1201TntManFlame::~AsScene1201TntManFlame() { + _vm->_soundMan->deleteSoundGroup(0x041080A4); +} + +void AsScene1201TntManFlame::update() { + AnimatedSprite::update(); + if (getGlobalVar(V_TNT_DUMMY_FUSE_LIT)) { + setVisible(true); + SetUpdateHandler(&AnimatedSprite::update); + _vm->_soundMan->addSound(0x041080A4, 0x460A1050); + _vm->_soundMan->playSoundLooping(0x460A1050); + } +} + +void AsScene1201TntManFlame::suUpdate() { + _x = _asTntMan->getX() - 18; + _y = _asTntMan->getY() - 158; +} + +AsScene1201Match::AsScene1201Match(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _countdown(0) { + + createSurface(1100, 57, 60); + SetUpdateHandler(&AsScene1201Match::update); + SetMessageHandler(&AsScene1201Match::hmOnDoorFrameAboutToMove); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); + switch (getGlobalVar(V_MATCH_STATUS)) { + case 0: + _x = 521; + _y = 112; + _status = 0; + stIdleOnDoorFrame(); + break; + case 1: + _x = 521; + _y = 112; + _status = 2; + stOnDoorFrameAboutToMove(); + loadSound(0, 0xD00230CD); + break; + case 2: + setDoDeltaX(1); + _x = 403; + _y = 337; + _status = 0; + stIdleOnFloor(); + break; + } +} + +void AsScene1201Match::update() { + if (_countdown != 0 && (--_countdown == 0)) + gotoNextState(); + updateAnim(); + handleSpriteUpdate(); + updatePosition(); +} + +uint32 AsScene1201Match::hmOnDoorFrameAboutToMove(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x86668011) + playSound(0); + break; + } + return messageResult; +} + +uint32 AsScene1201Match::hmOnDoorFrameMoving(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = hmOnDoorFrameAboutToMove(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +uint32 AsScene1201Match::hmIdle(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = hmOnDoorFrameAboutToMove(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x2001, 0); + messageResult = 1; + break; + case 0x4806: + setVisible(false); + setGlobalVar(V_MATCH_STATUS, 3); + break; + } + return messageResult; +} + +void AsScene1201Match::stOnDoorFrameMoving() { + startAnimation(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::hmOnDoorFrameMoving); + if (_status == 0) { + NextState(&AsScene1201Match::stFallingFromDoorFrame); + } else { + NextState(&AsScene1201Match::stOnDoorFrameAboutToMove); + } +} + +void AsScene1201Match::stFallingFromDoorFrame() { + setGlobalVar(V_MATCH_STATUS, 2); + _x -= 199; + _y += 119; + startAnimation(0x018D0240, 0, -1); + SetMessageHandler(&AsScene1201Match::hmOnDoorFrameMoving); + NextState(&AsScene1201Match::stIdleOnFloor); +} + +void AsScene1201Match::stOnDoorFrameAboutToMove() { + startAnimation(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::hmOnDoorFrameAboutToMove); + _newStickFrameIndex = 0; + if (_status != 0) { + _countdown = 36; + _status--; + NextState(&AsScene1201Match::stOnDoorFrameMoving); + } +} + +void AsScene1201Match::stIdleOnDoorFrame() { + startAnimation(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::hmIdle); + _newStickFrameIndex = 0; +} + +void AsScene1201Match::stIdleOnFloor() { + setDoDeltaX(1); + _x = 403; + _y = 337; + startAnimation(0x00842374, 0, -1); + SetMessageHandler(&AsScene1201Match::hmIdle); + _newStickFrameIndex = 0; +} + +AsScene1201Creature::AsScene1201Creature(NeverhoodEngine *vm, Scene *parentScene, Sprite *klaymen) + : AnimatedSprite(vm, 900), _parentScene(parentScene), _klaymen(klaymen), _klaymenTooClose(false) { + + // NOTE: _countdown2 and _countdown3 were unused/without effect and thus removed + + createSurface(1100, 203, 199); + SetUpdateHandler(&AsScene1201Creature::update); + SetMessageHandler(&AsScene1201Creature::hmWaiting); + _x = 540; + _y = 320; + stWaiting(); +} + +void AsScene1201Creature::update() { + bool oldKlaymenTooClose = _klaymenTooClose; + _klaymenTooClose = _klaymen->getX() >= 385; + if (_klaymenTooClose != oldKlaymenTooClose) + stWaiting(); + if (_countdown != 0 && (--_countdown == 0)) + gotoNextState(); + updateAnim(); + handleSpriteUpdate(); + updatePosition(); +} + +uint32 AsScene1201Creature::hmWaiting(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02060018) + playSound(0, 0xCD298116); + break; + case 0x2004: + GotoState(&AsScene1201Creature::stStartReachForTntDummy); + break; + case 0x2006: + GotoState(&AsScene1201Creature::stPincerSnapKlaymen); + break; + } + return messageResult; +} + +uint32 AsScene1201Creature::hmPincerSnap(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = hmWaiting(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +uint32 AsScene1201Creature::hmPincerSnapKlaymen(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02060018) { + playSound(0, 0xCD298116); + sendMessage(_parentScene, 0x4814, 0); + sendMessage(_klaymen, 0x4814, 0); + } + break; + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1201Creature::stWaiting() { + startAnimation(0x08081513, 0, -1); + SetMessageHandler(&AsScene1201Creature::hmWaiting); + NextState(&AsScene1201Creature::stPincerSnap); + _countdown = 36; +} + +void AsScene1201Creature::stPincerSnap() { + if (!_klaymenTooClose) { + startAnimation(0xCA287133, 0, -1); + SetMessageHandler(&AsScene1201Creature::hmPincerSnap); + NextState(&AsScene1201Creature::stWaiting); + } +} + +void AsScene1201Creature::stStartReachForTntDummy() { + startAnimation(0x08081513, 0, -1); + SetMessageHandler(&AsScene1201Creature::hmWaiting); + NextState(&AsScene1201Creature::stReachForTntDummy); + _countdown = 48; +} + +void AsScene1201Creature::stReachForTntDummy() { + startAnimation(0x5A201453, 0, -1); + SetMessageHandler(&AsScene1201Creature::hmWaiting); + _countdown = 0; +} + +void AsScene1201Creature::stPincerSnapKlaymen() { + startAnimation(0xCA287133, 0, -1); + SetMessageHandler(&AsScene1201Creature::hmPincerSnapKlaymen); + NextState(&AsScene1201Creature::stWaiting); + _countdown = 0; +} + +AsScene1201LeftDoor::AsScene1201LeftDoor(NeverhoodEngine *vm, Sprite *klaymen) + : AnimatedSprite(vm, 1100), _klaymen(klaymen) { + + _x = 320; + _y = 240; + createSurface(800, 55, 199); + if (_klaymen->getX() < 100) { + startAnimation(0x508A111B, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + playSound(0, calcHash("fxDoorOpen03")); + } else { + startAnimation(0x508A111B, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + } + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1201LeftDoor::handleMessage); +} + +uint32 AsScene1201LeftDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4809: + stCloseDoor(); + break; + } + return messageResult; +} + +void AsScene1201LeftDoor::stCloseDoor() { + startAnimation(0x508A111B, -1, -1); + _playBackwards = true; + _newStickFrameIndex = 0; +} + +Scene1201::Scene1201(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _creatureExploded(false), _asMatch(NULL), _asTntMan(NULL), + _asCreature(NULL), _asTntManRope(NULL), _asLeftDoor(NULL), _asRightDoor(NULL), _asTape(NULL) { + + int16 topY1, topY2, topY3, topY4; + int16 x1, x2; + Sprite *tempSprite; + + SetUpdateHandler(&Scene1201::update); + SetMessageHandler(&Scene1201::handleMessage); + + setHitRects(0x004AEBD0); + + if (!getSubVar(VA_IS_PUZZLE_INIT, 0xE8058B52)) { + setSubVar(VA_IS_PUZZLE_INIT, 0xE8058B52, 1); + for (uint32 index = 0; index < 18; index++) + setSubVar(VA_TNT_POSITIONS, index, kScene1201InitArray[index]); + } + + insertScreenMouse(0x9A2C0409); + + _asTape = insertSprite<AsScene1201Tape>(this, 3, 1100, 243, 340, 0x9148A011); + addCollisionSprite(_asTape); + + tempSprite = insertStaticSprite(0x03C82530, 100); + topY1 = tempSprite->getY() + tempSprite->getDrawRect().height; + + tempSprite = insertStaticSprite(0x88182069, 200); + topY2 = tempSprite->getY() + tempSprite->getDrawRect().height; + + tempSprite = insertStaticSprite(0x476014E0, 300); + topY3 = tempSprite->getY() + tempSprite->getDrawRect().height; + + tempSprite = insertStaticSprite(0x04063110, 500); + topY4 = tempSprite->getY() + 1; + + _asTntManRope = insertSprite<AsScene1201TntManRope>(getGlobalVar(V_TNT_DUMMY_BUILT) && which != 1); + _asTntManRope->setClipRect(0, topY4, 640, 480); + + insertStaticSprite(0x400B04B0, 1200); + + tempSprite = insertStaticSprite(0x40295462, 1200); + x1 = tempSprite->getX(); + + tempSprite = insertStaticSprite(0xA29223FA, 1200); + x2 = tempSprite->getX() + tempSprite->getDrawRect().width; + + _asKlaymenHead = insertSprite<AsScene1201KlaymenHead>(); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1201>(364, 333); + setMessageList(0x004AEC08); + } else if (which == 3) { + // Klaymen standing after the weasel exploded + insertKlaymen<KmScene1201>(400, 329); + setMessageList(0x004AEC08); + } else if (which == 2) { + // Klaymen entering from the right + if (getGlobalVar(V_CREATURE_ANGRY) && !getGlobalVar(V_CREATURE_EXPLODED)) { + insertKlaymen<KmScene1201>(374, 333); + setMessageList(0x004AEC08); + } else { + insertKlaymen<KmScene1201>(640, 329); + setMessageList(0x004AEC20); + } + } else if (which == 1) { + // Klaymen returning from the TNT console + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene1201>(364, 333); + _klaymen->setDoDeltaX(1); + } else { + insertKlaymen<KmScene1201>(246, 333); + } + setMessageList(0x004AEC30); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene1201>(0, 336); + setMessageList(0x004AEC10); + } + + _klaymen->setClipRect(x1, 0, x2, 480); + _klaymen->setRepl(64, 0); + + if (getGlobalVar(V_CREATURE_ANGRY) && !getGlobalVar(V_CREATURE_EXPLODED)) { + setBackground(0x4019A2C4); + setPalette(0x4019A2C4); + _asRightDoor = NULL; + } else { + setBackground(0x40206EC5); + setPalette(0x40206EC5); + _asRightDoor = insertSprite<AsScene1201RightDoor>(_klaymen, which == 2); + } + + if (getGlobalVar(V_TNT_DUMMY_BUILT)) { + insertStaticSprite(0x10002ED8, 500); + if (!getGlobalVar(V_CREATURE_EXPLODED)) { + _asTntMan = insertSprite<AsScene1201TntMan>(this, _asTntManRope, which == 1); + _asTntMan->setClipRect(x1, 0, x2, 480); + _asTntMan->setRepl(64, 0); + addCollisionSprite(_asTntMan); + tempSprite = insertSprite<AsScene1201TntManFlame>(_asTntMan); + tempSprite->setClipRect(x1, 0, x2, 480); + } + + uint32 tntIndex = 1; + while (tntIndex < 18) { + uint32 elemIndex = getSubVar(VA_TNT_POSITIONS, tntIndex); + int16 clipY2; + if (kScene1201PointArray[elemIndex].y < 175) + clipY2 = topY1; + else if (kScene1201PointArray[elemIndex].y < 230) + clipY2 = topY2; + else + clipY2 = topY3; + insertSprite<SsScene1201Tnt>(tntIndex, getSubVar(VA_TNT_POSITIONS, tntIndex), clipY2); + elemIndex = getSubVar(VA_TNT_POSITIONS, tntIndex + 1); + if (kScene1201PointArray[elemIndex].y < 175) + clipY2 = topY1; + else if (kScene1201PointArray[elemIndex].y < 230) + clipY2 = topY2; + else + clipY2 = topY3; + insertSprite<SsScene1201Tnt>(tntIndex + 1, getSubVar(VA_TNT_POSITIONS, tntIndex + 1), clipY2); + tntIndex += 3; + } + + if (getGlobalVar(V_CREATURE_ANGRY) && !getGlobalVar(V_CREATURE_EXPLODED)) { + setRectList(0x004AEE58); + } else { + setRectList(0x004AEDC8); + } + + } else { + + insertStaticSprite(0x8E8A1981, 900); + + uint32 tntIndex = 0; + while (tntIndex < 18) { + uint32 elemIndex = getSubVar(VA_TNT_POSITIONS, tntIndex); + int16 clipY2; + if (kScene1201PointArray[elemIndex].x < 300) { + clipY2 = 480; + } else { + if (kScene1201PointArray[elemIndex].y < 175) + clipY2 = topY1; + else if (kScene1201PointArray[elemIndex].y < 230) + clipY2 = topY2; + else + clipY2 = topY3; + } + insertSprite<SsScene1201Tnt>(tntIndex, getSubVar(VA_TNT_POSITIONS, tntIndex), clipY2); + tntIndex++; + } + + if (getGlobalVar(V_CREATURE_ANGRY) && !getGlobalVar(V_CREATURE_EXPLODED)) + setRectList(0x004AEE18); + else + setRectList(0x004AED88); + + } + + tempSprite = insertStaticSprite(0x63D400BC, 900); + + _asLeftDoor = insertSprite<AsScene1201LeftDoor>(_klaymen); + _asLeftDoor->setClipRect(x1, tempSprite->getDrawRect().y, tempSprite->getDrawRect().x2(), 480); + + if (getGlobalVar(V_CREATURE_ANGRY) && getGlobalVar(V_MATCH_STATUS) == 0) + setGlobalVar(V_MATCH_STATUS, 1); + + _asMatch = NULL; + + if (getGlobalVar(V_MATCH_STATUS) < 3) { + _asMatch = insertSprite<AsScene1201Match>(this); + addCollisionSprite(_asMatch); + } + + if (getGlobalVar(V_CREATURE_ANGRY) && getGlobalVar(V_CREATURE_EXPLODED) == 0) { + _asCreature = insertSprite<AsScene1201Creature>(this, _klaymen); + _asCreature->setClipRect(x1, 0, x2, 480); + } + +} + +Scene1201::~Scene1201() { + if (_creatureExploded) + setGlobalVar(V_CREATURE_EXPLODED, 1); + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +void Scene1201::update() { + Scene::update(); + if (_asMatch && getGlobalVar(V_MATCH_STATUS) == 3) + deleteSprite(&_asMatch); +} + +uint32 Scene1201::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x07053000) { + _creatureExploded = true; + sendMessage(_asCreature, 0x2004, 0); + } else if (param.asInteger() == 0x140E5744) + sendMessage(_asCreature, 0x2005, 0); + else if (param.asInteger() == 0x40253C40) { + _canAcceptInput = false; + sendMessage(_asCreature, 0x2006, 0); + } else if (param.asInteger() == 0x090EB048) { + if (_klaymen->getX() < 572) + setMessageList2(0x004AEC90); + else + setMessageList2(0x004AEC20); + } + break; + case 0x2001: + if (getGlobalVar(V_MATCH_STATUS) == 0) + setMessageList2(0x004AECB0); + else { + sendEntityMessage(_klaymen, 0x1014, _asMatch); + setMessageList2(0x004AECC0); + } + break; + case 0x2002: + if (getGlobalVar(V_TNT_DUMMY_FUSE_LIT)) { + // Move the TNT dummy if the fuse is burning + sendEntityMessage(_klaymen, 0x1014, _asTntMan); + setMessageList2(0x004AECF0, false); + } else if (getGlobalVar(V_MATCH_STATUS) == 3) { + // Light the TNT dummy if we have the match + sendEntityMessage(_klaymen, 0x1014, _asTntMan); + if (_klaymen->getX() > _asTntMan->getX()) + setMessageList(0x004AECD0); + else + setMessageList(0x004AECE0); + } + break; + case 0x4814: + cancelMessageList(); + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004AED38); + } + break; + case 0x4829: + sendMessage(_asRightDoor, 0x4829, 0); + break; + case 0x8000: + sendMessage(_asKlaymenHead, 0x2006, 0); + break; + } + return messageResult; +} + +// Scene1202 + +static const uint32 kScene1202Table[] = { + 1, 2, 0, 4, 5, 3, 7, 8, 6, 10, 11, 9, 13, 14, 12, 16, 17, 15 +}; + +static const NPoint kScene1202Points[] = { + {203, 140}, {316, 212}, {277, 264}, + {176, 196}, {275, 159}, {366, 212}, + {230, 195}, {412, 212}, {368, 263}, + {204, 192}, {365, 164}, {316, 262}, + {191, 255}, {280, 213}, {406, 266}, + {214, 254}, {316, 158}, {402, 161} +}; + +static const uint32 kScene1202FileHashes[] = { + 0x1AC00B8, 0x1AC14B8, 0x1AC14B8, + 0x1AC30B8, 0x1AC14B8, 0x1AC14B8, + 0x1AC00B8, 0x1AC14B8, 0x1AC14B8, + 0x1AC90B8, 0x1AC18B8, 0x1AC18B8, + 0x1AC30B8, 0x1AC14B8, 0x1AC14B8, + 0x1AC50B8, 0x1AC14B8, 0x1AC14B8 +}; + +AsScene1202TntItem::AsScene1202TntItem(NeverhoodEngine *vm, Scene *parentScene, int itemIndex) + : AnimatedSprite(vm, 900), _parentScene(parentScene), _itemIndex(itemIndex) { + + int positionIndex; + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1202TntItem::hmShowIdle); + positionIndex = getSubVar(VA_TNT_POSITIONS, _itemIndex); + createSurface(900, 37, 67); + _x = kScene1202Points[positionIndex].x; + _y = kScene1202Points[positionIndex].y; + stShowIdle(); +} + +uint32 AsScene1202TntItem::hmShowIdle(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x2000, _itemIndex); + messageResult = 1; + break; + case 0x2001: + _newPosition = (int)param.asInteger(); + stChangePositionFadeOut(); + break; + } + return messageResult; +} + +uint32 AsScene1202TntItem::hmChangePosition(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1202TntItem::stShowIdle() { + startAnimation(kScene1202FileHashes[_itemIndex], 0, -1); + SetMessageHandler(&AsScene1202TntItem::hmShowIdle); + _newStickFrameIndex = 0; +} + +void AsScene1202TntItem::stChangePositionFadeOut() { + startAnimation(kScene1202FileHashes[_itemIndex], 0, -1); + SetMessageHandler(&AsScene1202TntItem::hmChangePosition); + NextState(&AsScene1202TntItem::stChangePositionFadeIn); +} + +void AsScene1202TntItem::stChangePositionFadeIn() { + _x = kScene1202Points[_newPosition].x; + _y = kScene1202Points[_newPosition].y; + startAnimation(kScene1202FileHashes[_itemIndex], 6, -1); + _playBackwards = true; + SetMessageHandler(&AsScene1202TntItem::hmChangePosition); + NextState(&AsScene1202TntItem::stChangePositionDone); +} + +void AsScene1202TntItem::stChangePositionDone() { + sendMessage(_parentScene, 0x2002, _itemIndex); + stShowIdle(); +} + +Scene1202::Scene1202(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _paletteResource(vm), + _soundToggle(true), _isPuzzleSolved(false), _counter(0), _clickedIndex(-1) { + + SetMessageHandler(&Scene1202::handleMessage); + SetUpdateHandler(&Scene1202::update); + + setBackground(0x60210ED5); + setPalette(0x60210ED5); + addEntity(_palette); + + _paletteResource.load(0x60250EB5); + _paletteResource.copyPalette(_paletteData); + + insertPuzzleMouse(0x10ED160A, 20, 620); + + for (int tntIndex = 0; tntIndex < 18; tntIndex++) { + _asTntItems[tntIndex] = insertSprite<AsScene1202TntItem>(this, tntIndex); + addCollisionSprite(_asTntItems[tntIndex]); + } + + insertStaticSprite(0x8E8419C1, 1100); + + if (getGlobalVar(V_TNT_DUMMY_BUILT)) + SetMessageHandler(&Scene1202::hmSolved); + + playSound(0, 0x40106542); + loadSound(1, 0x40005446); + loadSound(2, 0x40005446); // Same sound as slot 1 + loadSound(3, 0x68E25540); + +} + +Scene1202::~Scene1202() { + if (isSolved()) + setGlobalVar(V_TNT_DUMMY_BUILT, 1); +} + +void Scene1202::update() { + Scene::update(); + if (_isPuzzleSolved) { + if (!isSoundPlaying(3)) + leaveScene(0); + } else if (_counter == 0 && isSolved()) { + _clickedIndex = 0; + SetMessageHandler(&Scene1202::hmSolved); + setGlobalVar(V_TNT_DUMMY_BUILT, 1); + _palette->copyToBasePalette(_paletteData); + _palette->startFadeToPalette(24); + playSound(3); + _isPuzzleSolved = true; + } else if (_clickedIndex >= 0 && _counter == 0) { + // Swap TNT positions + int destIndex = kScene1202Table[_clickedIndex]; + sendMessage(_asTntItems[_clickedIndex], 0x2001, getSubVar(VA_TNT_POSITIONS, destIndex)); + sendMessage(_asTntItems[destIndex], 0x2001, getSubVar(VA_TNT_POSITIONS, _clickedIndex)); + int temp = getSubVar(VA_TNT_POSITIONS, destIndex); + setSubVar(VA_TNT_POSITIONS, destIndex, getSubVar(VA_TNT_POSITIONS, _clickedIndex)); + setSubVar(VA_TNT_POSITIONS, _clickedIndex, temp); + _counter = 2; + _clickedIndex = -1; + playSound(_soundToggle ? 1 : 2); + _soundToggle = !_soundToggle; + } +} + +uint32 Scene1202::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !_isPuzzleSolved) + leaveScene(0); + break; + case 0x2000: + _clickedIndex = (int)param.asInteger(); + break; + case 0x2002: + _counter--; + break; + } + return messageResult; +} + +uint32 Scene1202::hmSolved(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; + } + return 0; +} + +bool Scene1202::isSolved() { + return + getSubVar(VA_TNT_POSITIONS, 0) == 0 && getSubVar(VA_TNT_POSITIONS, 3) == 3 && + getSubVar(VA_TNT_POSITIONS, 6) == 6 && getSubVar(VA_TNT_POSITIONS, 9) == 9 && + getSubVar(VA_TNT_POSITIONS, 12) == 12 && getSubVar(VA_TNT_POSITIONS, 15) == 15; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1200.h b/engines/neverhood/modules/module1200.h new file mode 100644 index 0000000000..c97dc98986 --- /dev/null +++ b/engines/neverhood/modules/module1200.h @@ -0,0 +1,216 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1200_H +#define NEVERHOOD_MODULES_MODULE1200_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1200 + +class Module1200 : public Module { +public: + Module1200(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1200(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1201 + +class AsScene1201Tape : public AnimatedSprite { +public: + AsScene1201Tape(NeverhoodEngine *vm, Scene *parentScene, uint32 nameHash, int surfacePriority, int16 x, int16 y, uint32 fileHash); +protected: + Scene *_parentScene; + uint32 _nameHash; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1201TntManRope : public AnimatedSprite { +public: + AsScene1201TntManRope(NeverhoodEngine *vm, bool isDummyHanging); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1201RightDoor : public AnimatedSprite { +public: + AsScene1201RightDoor(NeverhoodEngine *vm, Sprite *klaymen, bool isOpen); +protected: + Sprite *_klaymen; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stOpenDoor(); + void stCloseDoor(); + void stCloseDoorDone(); +}; + +class AsScene1201KlaymenHead : public AnimatedSprite { +public: + AsScene1201KlaymenHead(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1201TntMan : public AnimatedSprite { +public: + AsScene1201TntMan(NeverhoodEngine *vm, Scene *parentScene, Sprite *asTntManRope, bool isDown); + virtual ~AsScene1201TntMan(); +protected: + Scene *_parentScene; + Sprite *_asTntManRope; + Sprite *_sprite; + bool _isMoving; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmComingDown(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoving(); + void stStanding(); + void stComingDown(); + void stMoving(); +}; + +class AsScene1201TntManFlame : public AnimatedSprite { +public: + AsScene1201TntManFlame(NeverhoodEngine *vm, Sprite *asTntMan); + ~AsScene1201TntManFlame(); +protected: + Sprite *_asTntMan; + void update(); + void suUpdate(); +}; + +class AsScene1201Match : public AnimatedSprite { +public: + AsScene1201Match(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + int _countdown; + int _status; + void update(); + uint32 hmOnDoorFrameAboutToMove(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmOnDoorFrameMoving(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmIdle(int messageNum, const MessageParam ¶m, Entity *sender); + void stOnDoorFrameMoving(); + void stFallingFromDoorFrame(); + void stOnDoorFrameAboutToMove(); + void stIdleOnDoorFrame(); + void stIdleOnFloor(); +}; + +class AsScene1201Creature : public AnimatedSprite { +public: + AsScene1201Creature(NeverhoodEngine *vm, Scene *parentScene, Sprite *klaymen); +protected: + Scene *_parentScene; + Sprite *_klaymen; + int _countdown; + bool _klaymenTooClose; + void update(); + uint32 hmWaiting(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmPincerSnap(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmPincerSnapKlaymen(int messageNum, const MessageParam ¶m, Entity *sender); + void stWaiting(); + void stPincerSnap(); + void stStartReachForTntDummy(); + void stReachForTntDummy(); + void stPincerSnapKlaymen(); +}; + +class AsScene1201LeftDoor : public AnimatedSprite { +public: + AsScene1201LeftDoor(NeverhoodEngine *vm, Sprite *klaymen); +protected: + Sprite *_klaymen; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stCloseDoor(); +}; + +class SsScene1201Tnt : public StaticSprite { +public: + SsScene1201Tnt(NeverhoodEngine *vm, uint32 elemIndex, uint32 pointIndex, int16 clipY2); +protected: + uint32 _elemIndex; +}; + +class Scene1201 : public Scene { +public: + Scene1201(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1201(); +protected: + Sprite *_asMatch; + AsScene1201TntMan *_asTntMan; + Sprite *_asCreature; + Sprite *_asTntManRope; + Sprite *_asLeftDoor; + Sprite *_asRightDoor; + Sprite *_asTape; + Sprite *_asKlaymenHead; + bool _creatureExploded; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1202 + +class AsScene1202TntItem : public AnimatedSprite { +public: + AsScene1202TntItem(NeverhoodEngine *vm, Scene *parentScene, int index); +protected: + Scene *_parentScene; + int _itemIndex, _newPosition; + uint32 hmShowIdle(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmChangePosition(int messageNum, const MessageParam ¶m, Entity *sender); + void stShowIdle(); + void stChangePositionFadeOut(); + void stChangePositionFadeIn(); + void stChangePositionDone(); +}; + +class Scene1202 : public Scene { +public: + Scene1202(NeverhoodEngine *vm, Module *parentModule); + virtual ~Scene1202(); +protected: + PaletteResource _paletteResource; + Sprite *_asTntItems[18]; + int _counter; + int _clickedIndex; + byte _paletteData[1024]; + bool _isPuzzleSolved; + bool _soundToggle; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmSolved(int messageNum, const MessageParam ¶m, Entity *sender); + bool isSolved(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1200_H */ diff --git a/engines/neverhood/modules/module1300.cpp b/engines/neverhood/modules/module1300.cpp new file mode 100644 index 0000000000..8dbfcf616c --- /dev/null +++ b/engines/neverhood/modules/module1300.cpp @@ -0,0 +1,1835 @@ +/* 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/module1300.h" +#include "neverhood/modules/module1000.h" +#include "neverhood/modules/module1200.h" +#include "neverhood/modules/module1400.h" +#include "neverhood/modules/module2200.h" +#include "neverhood/gamemodule.h" +#include "neverhood/diskplayerscene.h" +#include "neverhood/menumodule.h" +#include "neverhood/navigationscene.h" +#include "neverhood/smackerscene.h" + +namespace Neverhood { + +static const uint32 kModule1300SoundList[] = { + 0x16805648, + 0x16805C48, + 0xB4964448, + 0x96A05481, + 0xD0E14441, + 0x90815450, + 0 +}; + +Module1300::Module1300(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + _vm->_soundMan->addMusic(0x61C090, 0x00203197); + _vm->_soundMan->addSoundList(0x61C090, kModule1300SoundList); + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 50, 600, 20, 150); + _vm->_soundMan->playTwoSounds(0x61C090, 0x48498E46, 0x50399F64, 0); + _vm->_soundMan->setSoundVolume(0x48498E46, 70); + _vm->_soundMan->setSoundVolume(0x50399F64, 70); + + if (which < 0) { + if (_vm->gameState().sceneNum >= 1 && _vm->gameState().sceneNum <= 17) + createScene(_vm->gameState().sceneNum, -1); + else + createScene(11, 0); + } else { + switch (which) { + case 0: + createScene(11, 0); + break; + case 1: + createScene(13, 0); + break; + case 2: + createScene(14, 0); + break; + case 3: + createScene(15, 0); + break; + case 4: + createScene(7, 0); + break; + case 5: + createScene(5, 1); + break; + case 6: + createScene(5, 5); + break; + case 7: + createScene(3, 0); + break; + case 8: + createScene(1, 0); + break; + case 9: + createScene(2, 0); + break; + case 10: + createScene(6, 0); + break; + case 11: + createScene(4, 0); + break; + default: + createScene(12, 0); + break; + } + } + +} + +Module1300::~Module1300() { + _vm->_soundMan->deleteGroup(0x61C090); +} + +void Module1300::createScene(int sceneNum, int which) { + debug("Module1300::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 1: + _vm->gameState().sceneNum = 1; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->startMusic(0x00203197, 0, 2); + _childObject = new Scene1302(_vm, this, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + _childObject = new Scene1303(_vm, this); + break; + case 3: + _vm->gameState().sceneNum = 3; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + _childObject = new Scene1304(_vm, this, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->startMusic(0x00203197, 0, 2); + _childObject = new Scene1305(_vm, this, which); + break; + case 5: + _vm->gameState().sceneNum = 5; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->startMusic(0x00203197, 0, 2); + _childObject = new Scene1306(_vm, this, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->startMusic(0x00203197, 0, 2); + _childObject = new Scene1307(_vm, this); + break; + case 7: + _vm->gameState().sceneNum = 7; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->startMusic(0x00203197, 0, 2); + _childObject = new Scene1308(_vm, this, which); + break; + case 8: + _vm->gameState().sceneNum = 8; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 1); + break; + case 9: + _vm->gameState().sceneNum = 9; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + createSmackerScene(0x20082818, true, true, false); + break; + case 10: + _vm->gameState().sceneNum = 10; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + createSmackerScene(0x20082828, true, true, false); + break; + case 11: + _vm->gameState().sceneNum = 11; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, true, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + createNavigationScene(0x004B27A8, which); + break; + case 12: + _vm->gameState().sceneNum = 12; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, true, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + createNavigationScene(0x004B2718, which); + break; + case 13: + _vm->gameState().sceneNum = 13; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, true, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + createNavigationScene(0x004B27D8, which); + break; + case 14: + _vm->gameState().sceneNum = 14; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, true, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + createNavigationScene(0x004B2808, which); + break; + case 15: + _vm->gameState().sceneNum = 15; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, true, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + createNavigationScene(0x004B2838, which); + break; + case 16: + _vm->gameState().sceneNum = 16; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + _childObject = new Scene1317(_vm, this); + break; + case 17: + _vm->gameState().sceneNum = 17; + _vm->_soundMan->setSoundListParams(kModule1300SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->stopMusic(0x00203197, 0, 2); + _childObject = new CreditsScene(_vm, this, false); + break; + } + SetUpdateHandler(&Module1300::updateScene); + _childObject->handleUpdate(); +} + +void Module1300::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 1: + if (_moduleResult == 1) + createScene(4, 0); + else + createScene(7, 1); + break; + case 2: + createScene(5, 3); + break; + case 3: + createScene(15, 0); + break; + case 4: + createScene(16, -1); + break; + case 5: + if (_moduleResult == 2) + createScene(8, 0); + else if (_moduleResult == 3) + createScene(2, 0); + else if (_moduleResult == 0) + leaveModule(0); + else if (_moduleResult == 1) + createScene(10, -1); + break; + case 6: + createScene(7, 2); + break; + case 7: + if (_moduleResult == 0) + createScene(13, 0); + else if (_moduleResult == 1) + createScene(1, 0); + else if (_moduleResult == 2) + createScene(6, 0); + break; + case 8: + createScene(5, 2); + break; + case 9: + createScene(5, 0); + break; + case 10: + createScene(14, 0); + break; + case 11: + if (_moduleResult == 0) + createScene(12, 0); + else if (_moduleResult == 1) + createScene(11, 1); + break; + case 12: + if (_moduleResult == 0) + createScene(14, 1); + else if (_moduleResult == 1) + createScene(15, 1); + else if (_moduleResult == 3) + createScene(11, 1); + else if (_moduleResult == 5) + createScene(13, 1); + break; + case 13: + if (_moduleResult == 0) + createScene(12, 2); + else if (_moduleResult == 1) + createScene(7, 0); + break; + case 14: + if (_moduleResult == 0) + createScene(12, 3); + else if (_moduleResult == 1) + createScene(9, -1); + break; + case 15: + if (_moduleResult == 0) + createScene(12, 4); + else if (_moduleResult == 1) + createScene(3, 0); + break; + case 16: + createScene(17, -1); + break; + case 17: + leaveModule(1); + break; + } + } +} + +AsScene1302Bridge::AsScene1302Bridge(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + _x = 320; + _y = 240; + createSurface1(0x88148150, 500); + if (!getGlobalVar(V_FLYTRAP_RING_BRIDGE)) { + startAnimation(0x88148150, 0, -1); + _newStickFrameIndex = 0; + } else { + startAnimation(0x88148150, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + } + loadSound(0, 0x68895082); + loadSound(1, 0x689BD0C1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1302Bridge::handleMessage); +} + +uint32 AsScene1302Bridge::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + case 0x4808: + stLowerBridge(); + break; + case 0x4809: + stRaiseBridge(); + break; + } + return messageResult; +} + +void AsScene1302Bridge::stLowerBridge() { + startAnimation(0x88148150, 0, -1); + playSound(1); + NextState(&AsScene1302Bridge::cbLowerBridgeEvent); +} + +void AsScene1302Bridge::stRaiseBridge() { + startAnimation(0x88148150, 7, -1); + _playBackwards = true; + _newStickFrameIndex = 0; + playSound(0); +} + +void AsScene1302Bridge::cbLowerBridgeEvent() { + sendMessage(_parentScene, 0x2032, 0); + startAnimation(0x88148150, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; +} + +SsScene1302Fence::SsScene1302Fence(NeverhoodEngine *vm) + : StaticSprite(vm, 0x11122122, 200) { + + _firstY = _y; + if (getGlobalVar(V_FLYTRAP_RING_FENCE)) + _y += 152; + loadSound(0, 0x7A00400C); + loadSound(1, 0x78184098); + SetUpdateHandler(&SsScene1302Fence::update); + SetMessageHandler(&SsScene1302Fence::handleMessage); + SetSpriteUpdate(NULL); +} + +void SsScene1302Fence::update() { + handleSpriteUpdate(); + updatePosition(); +} + +uint32 SsScene1302Fence::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + playSound(0); + SetMessageHandler(NULL); + SetSpriteUpdate(&SsScene1302Fence::suMoveDown); + break; + case 0x4809: + playSound(1); + SetMessageHandler(NULL); + SetSpriteUpdate(&SsScene1302Fence::suMoveUp); + break; + } + return messageResult; +} + +void SsScene1302Fence::suMoveDown() { + if (_y < _firstY + 152) + _y += 8; + else { + SetMessageHandler(&SsScene1302Fence::handleMessage); + SetSpriteUpdate(NULL); + } +} + +void SsScene1302Fence::suMoveUp() { + if (_y > _firstY) + _y -= 8; + else { + SetMessageHandler(&SsScene1302Fence::handleMessage); + SetSpriteUpdate(NULL); + } +} + +Scene1302::Scene1302(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene1302::handleMessage); + + setHitRects(0x004B0858); + setRectList(0x004B0A38); + setBackground(0x420643C4); + setPalette(0x420643C4); + insertScreenMouse(0x643C0428); + + _class595 = insertStaticSprite(0xB0420130, 1015); + _sprite1 = insertStaticSprite(0x942FC224, 300); + _sprite2 = insertStaticSprite(0x70430830, 1200); + _sprite2->setVisible(false); + _sprite3 = insertStaticSprite(0x16E01E20, 1100); + _asRing1 = insertSprite<AsScene1002Ring>(this, false, 218, 122, _class595->getDrawRect().y, false); + _asRing2 = insertSprite<AsScene1002Ring>(this, true, 218 + 32, 132, _class595->getDrawRect().y, getGlobalVar(V_FLYTRAP_RING_BRIDGE)); + _asRing3 = insertSprite<AsScene1002Ring>(this, false, 218 + 32 + 32, 122, _class595->getDrawRect().y, false); + _asRing4 = insertSprite<AsScene1002Ring>(this, true, 218 + 32 + 32 + 32, 132, _class595->getDrawRect().y, getGlobalVar(V_FLYTRAP_RING_FENCE)); + _asRing5 = insertSprite<AsScene1002Ring>(this, false, 218 + 32 + 32 + 32 + 32, 115, _class595->getDrawRect().y, false); + _asBridge = insertSprite<AsScene1302Bridge>(this); + _ssFence = insertSprite<SsScene1302Fence>(); + _ssFence->setClipRect(0, 0, 640, _sprite1->getDrawRect().y2()); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1002>(380, 364); + setMessageList(0x004B0868); + } else { + // Klaymen entering from back + insertKlaymen<KmScene1002>(293, 330); + setMessageList(0x004B0870); + } + + _klaymen->setClipRect(0, 0, _sprite3->getDrawRect().x2(), 480); + + _asVenusFlyTrap = insertSprite<AsScene1002VenusFlyTrap>(this, _klaymen, true); + addCollisionSprite(_asVenusFlyTrap); + + sendEntityMessage(_klaymen, 0x2007, _asVenusFlyTrap); + +} + +uint32 Scene1302::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x4A845A00) + sendEntityMessage(_klaymen, 0x1014, _asRing1); + else if (param.asInteger() == 0x43807801) { + if (!getGlobalVar(V_FLYTRAP_RING_BRIDGE)) { + sendEntityMessage(_klaymen, 0x1014, _asRing2); + if (_asVenusFlyTrap->getX() - 10 < 218 + 32 && _asVenusFlyTrap->getX() + 10 > 218 + 32) + setMessageList(0x004B0940); + else + setMessageList(0x004B0938); + } else + setMessageList(0x004B0950); + messageResult = 1; + } else if (param.asInteger() == 0x46C26A01) + sendEntityMessage(_klaymen, 0x1014, _asRing3); + else if (param.asInteger() == 0x468C7B11) { + if (!getGlobalVar(V_FLYTRAP_RING_FENCE)) { + sendEntityMessage(_klaymen, 0x1014, _asRing4); + if (_asVenusFlyTrap->getX() - 10 < 218 + 32 + 32 + 32 && _asVenusFlyTrap->getX() + 10 > 218 + 32 + 32 + 32) + setMessageList(0x004B0940); + else + setMessageList(0x004B0938); + } else + setMessageList(0x004B0950); + messageResult = 1; + } else if (param.asInteger() == 0x42845B19) + sendEntityMessage(_klaymen, 0x1014, _asRing5); + else if (param.asInteger() == 0x430A6060) { + if (getGlobalVar(V_FLYTRAP_RING_BRIDGE)) + setMessageList2(0x004B0910); + else + cancelMessageList(); + } else if (param.asInteger() == 0x012E2070) { + if (getGlobalVar(V_FLYTRAP_RING_BRIDGE)) + setMessageList2(0x004B0968); + else + cancelMessageList(); + } else if (param.asInteger() == 0x11C40840) { + if (_asVenusFlyTrap->getX() >= 260 && _asVenusFlyTrap->getX() <= 342) + setMessageList(0x004B0878); + else + setMessageList(0x004B0978); + } + break; + case 0x2000: + if (_klaymen->getY() > 360) { + sendEntityMessage(_klaymen, 0x1014, _asVenusFlyTrap); + setMessageList2(0x004B08F0); + } else + setMessageList2(0x004B0920); + break; + case 0x2002: + if (_klaymen->getX() > 545) + leaveScene(1); + break; + case 0x2032: + _sprite2->setVisible(true); + break; + case 0x4806: + sendMessage(_parentModule, 0x1024, 2); + if (sender == _asRing1) + playSound(0, 0x665198C0); + else if (sender == _asRing2) { + sendMessage(_asBridge, 0x4808, 0); + setGlobalVar(V_FLYTRAP_RING_BRIDGE, 1); + } else if (sender == _asRing3) + playSound(0, 0xE2D389C0); + else if (sender == _asRing4) { + sendMessage(_ssFence, 0x4808, 0); + setGlobalVar(V_FLYTRAP_RING_FENCE, 1); + } else if (sender == _asRing5) + playSound(0, 0x40428A09); + break; + case 0x4807: + if (sender == _asRing2) { + sendMessage(_asBridge, 0x4809, 0); + setGlobalVar(V_FLYTRAP_RING_BRIDGE, 0); + _sprite2->setVisible(false); + } else if (sender == _asRing4) { + sendMessage(_ssFence, 0x4809, 0); + setGlobalVar(V_FLYTRAP_RING_FENCE, 0); + } else if (sender == _asVenusFlyTrap) { + if (getGlobalVar(V_FLYTRAP_RING_BRIDGE)) + sendMessage(_asRing2, 0x4807, 0); + else + sendMessage(_asRing4, 0x4807, 0); + } + break; + case 0x480F: + if (sender == _asRing2) { + playSound(0, 0x60755842); + sendMessage(_asBridge, 0x4808, 0); + setGlobalVar(V_FLYTRAP_RING_BRIDGE, 1); + } else if (sender == _asRing4) { + playSound(0, 0x60755842); + sendMessage(_ssFence, 0x4808, 0); + setGlobalVar(V_FLYTRAP_RING_FENCE, 1); + } + break; + case 0x482A: + sendMessage(_asVenusFlyTrap, 0x482B, 0); + break; + case 0x482B: + sendMessage(_asVenusFlyTrap, 0x482A, 0); + break; + case 0x8000: + setSpriteSurfacePriority(_class595, 995); + break; + case 0x8001: + setSpriteSurfacePriority(_class595, 1015); + break; + } + return messageResult; +} + +AsScene1303Balloon::AsScene1303Balloon(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + createSurface(200, 128, 315); + _x = 289; + _y = 390; + startAnimation(0x800278D2, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1303Balloon::handleMessage); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); +} + +uint32 AsScene1303Balloon::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 0x2000: + stPopBalloon(); + break; + } + return messageResult; +} + +uint32 AsScene1303Balloon::hmBalloonPopped(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x020B0003) + playSound(0, 0x742B0055); + break; + case 0x3002: + playSound(0, 0x470007EE); + stopAnimation(); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +void AsScene1303Balloon::stPopBalloon() { + startAnimation(0xAC004CD0, 0, -1); + SetMessageHandler(&AsScene1303Balloon::hmBalloonPopped); +} + +Scene1303::Scene1303(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene1303::handleMessage); + + setRectList(0x004AF9E8); + setBackground(0x01581A9C); + setPalette(0x01581A9C); + insertScreenMouse(0x81A9801D); + + if (!getGlobalVar(V_BALLOON_POPPED)) { + _asBalloon = insertSprite<AsScene1303Balloon>(this); + addCollisionSprite(_asBalloon); + } + + _sprite1 = insertStaticSprite(0xA014216B, 1100); + + insertKlaymen<KmScene1303>(207, 332); + setMessageList(0x004AF9A0); + + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + +} + +uint32 Scene1303::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + setGlobalVar(V_BALLOON_POPPED, 1); + sendMessage(_asBalloon, 0x2000, 0); + break; + case 0x4826: + if (sender == _asBalloon && getGlobalVar(V_HAS_NEEDLE)) + setMessageList(0x004AF9B8); + break; + } + return 0; +} + +AsScene1304Needle::AsScene1304Needle(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, int16 x, int16 y) + : AnimatedSprite(vm, 0x548E9411, surfacePriority, x, y), _parentScene(parentScene) { + + // NOTE: Skipped check if Klaymen already has the needle since that's done in the scene itself + SetMessageHandler(&AsScene1304Needle::handleMessage); +} + +uint32 AsScene1304Needle::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_NEEDLE, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +Scene1304::Scene1304(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _asNeedle(NULL) { + + SetMessageHandler(&Scene1304::handleMessage); + + setRectList(0x004B91A8); + setBackground(0x062C0214); + setPalette(0x062C0214); + insertScreenMouse(0xC021006A); + + if (getGlobalVar(V_BALLOON_POPPED)) { + _asKey = insertSprite<AsCommonKey>(this, 0, 1100, 278, 347); + addCollisionSprite(_asKey); + } else { + _asKey = insertSprite<AnimatedSprite>(0x80106018, 100, 279, 48); + } + + if (!getGlobalVar(V_HAS_NEEDLE)) { + _asNeedle = insertSprite<AsScene1304Needle>(this, 1100, 278, 347); + addCollisionSprite(_asNeedle); + } + + _sprite1 = insertStaticSprite(0x0562E621, 1100); + insertStaticSprite(0x012AE033, 1100); + insertStaticSprite(0x090AF033, 1100); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1304>(217, 347); + setMessageList(0x004B90E8); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene1304>(100, 347); + setMessageList(0x004B90F0); + } + + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + +} + +uint32 Scene1304::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x415634A4) { + if (getGlobalVar(V_BALLOON_POPPED)) + cancelMessageList(); + else + setMessageList(0x004B9158); + } + break; + case 0x4826: + if (sender == _asNeedle) { + sendEntityMessage(_klaymen, 0x1014, _asNeedle); + setMessageList(0x004B9130); + } else if (sender == _asKey) { + sendEntityMessage(_klaymen, 0x1014, _asKey); + setMessageList(0x004B9140); + } + break; + } + return 0; +} + +Scene1305::Scene1305(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene1305::handleMessage); + + setRectList(0x004B6E98); + setBackground(0x28801B64); + setPalette(0x28801B64); + insertScreenMouse(0x01B60280); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1305>(212, 441); + setMessageList(0x004B6E40); + } else { + // Klaymen enters falling + insertKlaymen<KmScene1305>(212, 441); + setMessageList(0x004B6E48); + } + +} + +uint32 Scene1305::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + return Scene::handleMessage(messageNum, param, sender); +} + +AsScene1306Elevator::AsScene1306Elevator(NeverhoodEngine *vm, Scene *parentScene, AnimatedSprite *asElevatorDoor) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _asElevatorDoor(asElevatorDoor), _isUp(false), _isDown(true), + _countdown(0) { + + _x = 320; + _y = 240; + createSurface1(0x043B0270, 100); + startAnimation(0x043B0270, 0, -1); + _newStickFrameIndex = 0; + loadSound(0, 0x1C100E83); + loadSound(1, 0x1C08CEC5); + loadSound(2, 0x5D011E87); + SetMessageHandler(&AsScene1306Elevator::handleMessage); +} + +void AsScene1306Elevator::update() { + if (_isUp && _countdown != 0 && (--_countdown == 0)) + stGoingDown(); + AnimatedSprite::update(); + if (_currFrameIndex == 7) { + playSound(1); + _asElevatorDoor->setVisible(false); + } +} + +void AsScene1306Elevator::upGoingDown() { + AnimatedSprite::update(); + if (_currFrameIndex == 5) + _asElevatorDoor->setVisible(true); +} + +uint32 AsScene1306Elevator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + if (_isUp) + _countdown = 144; + messageResult = _isUp ? 1 : 0; + break; + case 0x3002: + gotoNextState(); + break; + case 0x4808: + if (_isDown) + stGoingUp(); + break; + } + return messageResult; +} + +void AsScene1306Elevator::stGoingUp() { + setVisible(true); + _isDown = false; + startAnimation(0x043B0270, 0, -1); + playSound(0); + SetUpdateHandler(&AsScene1306Elevator::update); + NextState(&AsScene1306Elevator::cbGoingUpEvent); +} + +void AsScene1306Elevator::cbGoingUpEvent() { + sendMessage(_parentScene, 0x4808, 0); + _isUp = true; + _countdown = 144; + stopAnimation(); + setVisible(false); + SetUpdateHandler(&AsScene1306Elevator::update); +} + +void AsScene1306Elevator::stGoingDown() { + _isUp = false; + setVisible(true); + startAnimation(0x043B0270, -1, -1); + _playBackwards = true; + playSound(1); + SetUpdateHandler(&AsScene1306Elevator::upGoingDown); + NextState(&AsScene1306Elevator::cbGoingDownEvent); +} + +void AsScene1306Elevator::cbGoingDownEvent() { + _isDown = true; + sendMessage(_parentScene, 0x4809, 0); + stopAnimation(); + SetUpdateHandler(&AsScene1306Elevator::update); +} + +Scene1306::Scene1306(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + if (getGlobalVar(V_HAS_FINAL_KEY) && getGlobalVar(V_KEY3_LOCATION) == 0) + setGlobalVar(V_KEY3_LOCATION, 4); + + SetMessageHandler(&Scene1306::handleMessage); + + setBackground(0x05303114); + setPalette(0x05303114); + insertScreenMouse(0x0311005B); + + if (getGlobalVar(V_KEY3_LOCATION) == 4) { + _asKey = insertSprite<AsCommonKey>(this, 2, 1100, 435, 445); + addCollisionSprite(_asKey); + } + + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x404A36A0, 100, 0x440C1000); + _asTape = insertSprite<AsScene1201Tape>(this, 19, 1100, 359, 445, 0x9148A011); + _asElevatorDoor = insertSprite<AnimatedSprite>(0x043B0270, 90, 320, 240); + _asElevatorDoor->startAnimation(0x043B0270, 6, -1); + _asElevatorDoor->setNewHashListIndex(6); + _asElevator = insertSprite<AsScene1306Elevator>(this, _asElevatorDoor); + _sprite1 = insertStaticSprite(0x036A1EE0, 80); + insertStaticSprite(0x00042313, 1100); + + if (which < 0) { + // Resoring game + insertKlaymen<KmScene1306>(380, 440); + setMessageList(0x004AFAD0); + sendMessage(this, 0x2000, 0); + addCollisionSprite(_asTape); + } else if (which == 1) { + // Klaymen teleporting in + insertKlaymen<KmScene1306>(136, 440); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004AFAF0); + sendMessage(this, 0x2000, 1); + addCollisionSprite(_asTape); + } else if (which == 2) { + // Klaymen returning from diskplayer + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene1306>(515, 440); + _klaymen->setDoDeltaX(1); + } else { + insertKlaymen<KmScene1306>(355, 440); + } + setMessageList(0x004AFBC8); + sendMessage(this, 0x2000, 0); + addCollisionSprite(_asTape); + } else if (which == 3) { + // Klaymen returning from window + insertKlaymen<KmScene1306>(534, 440); + setMessageList(0x004AFC30); + sendMessage(this, 0x2000, 0); + addCollisionSprite(_asTape); + } else if (which == 4) { + // Klaymen teleporting out + insertKlaymen<KmScene1306>(136, 440); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004AFC38); + sendMessage(this, 0x2000, 1); + addCollisionSprite(_asTape); + } else if (which == 5) { + // Klaymen returning from teleporter + insertKlaymen<KmScene1306>(136, 440); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004AFB00); + sendMessage(this, 0x2000, 1); + addCollisionSprite(_asTape); + } else { + // Klaymen coming up in elevator + insertKlaymen<KmScene1306>(286, 408); + setSurfacePriority(_asElevator->getSurface(), 1100); + setSurfacePriority(_asElevatorDoor->getSurface(), 1090); + setSurfacePriority(_sprite1->getSurface(), 1080); + sendMessage(this, 0x2000, 0); + SetMessageHandler(&Scene1306::handleMessage416EB0); + clearRectList(); + sendMessage(_asElevator, 0x4808, 0); + } + +} + +Scene1306::~Scene1306() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene1306::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssButton); + else if (param.asInteger() == 0x01C66840) { + if (sendMessage(_asElevator, 0x2001, 0) != 0) + setMessageList(0x004AFBD8); + else + setMessageList(0x004AFAE0); + } else if (param.asInteger() == 0x8E646E00) { + setMessageList(0x004AFAD8); + clearRectList(); + SetMessageHandler(&Scene1306::handleMessage416EB0); + } + break; + case 0x2000: + if (param.asInteger() != 0) { + setRectList(0x004AFD28); + _klaymen->setKlaymenIdleTable3(); + } else { + setRectList(0x004AFD18); + _klaymen->setKlaymenIdleTable1(); + } + break; + case 0x480B: + if (sender == _ssButton) + sendMessage(_asElevator, 0x4808, 0); + break; + case 0x4826: + if (sender == _asKey) { + if (_klaymen->getX() >= 249) { + sendEntityMessage(_klaymen, 0x1014, _asKey); + setMessageList(0x004AFC58); + } + } else if (sender == _asTape) { + if (_klaymen->getX() >= 249) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004AFC68); + } + } + break; + case 0x482A: + setSurfacePriority(_asElevator->getSurface(), 1100); + setSurfacePriority(_asElevatorDoor->getSurface(), 1090); + setSurfacePriority(_sprite1->getSurface(), 1080); + break; + case 0x482B: + setSurfacePriority(_asElevator->getSurface(), 100); + setSurfacePriority(_asElevatorDoor->getSurface(), 90); + setSurfacePriority(_sprite1->getSurface(), 80); + sendMessage(this, 0x2000, 0); + addCollisionSprite(_asTape); + break; + } + return 0; +} + +uint32 Scene1306::handleMessage416EB0(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4808: + setMessageList(0x004AFBD0); + SetMessageHandler(&Scene1306::handleMessage); + break; + case 0x4809: + leaveScene(1); + break; + case 0x482A: + setSurfacePriority(_asElevator->getSurface(), 1100); + setSurfacePriority(_asElevatorDoor->getSurface(), 1090); + setSurfacePriority(_sprite1->getSurface(), 1080); + break; + case 0x482B: + setSurfacePriority(_asElevator->getSurface(), 100); + setSurfacePriority(_asElevatorDoor->getSurface(), 90); + setSurfacePriority(_sprite1->getSurface(), 80); + sendMessage(this, 0x2000, 0); + addCollisionSprite(_asTape); + break; + } + return 0; +} + +static const uint32 kAsScene1307KeyResourceList1[] = { + 0x0438069C, 0x45B0023C, 0x05700217 +}; + +static const uint32 kAsScene1307KeyResourceList2[] = { + 0x04441334, 0x061433F0, 0x06019390 +}; + +static const uint32 kAsScene1307KeyResourceList3[] = { + 0x11A80030, 0x178812B1, 0x1488121C +}; + +static const uint32 *kAsScene1307KeyResourceLists[] = { + kAsScene1307KeyResourceList1, + kAsScene1307KeyResourceList2, + kAsScene1307KeyResourceList3 +}; + +static const int kAsScene1307KeySurfacePriorities[] = { + 700, 500, 300, 100 +}; + +const uint kAsScene1307KeyPointsCount = 12; + +static const NPoint kAsScene1307KeyPoints[] = { + {-2, 0}, {-5, 0}, { 5, 0}, + {12, 0}, {17, 0}, {25, 0}, + {16, -2}, {10, -6}, { 0, -7}, + {-7, -3}, {-3, 4}, { 2, 2} +}; + +const uint kAsScene1307KeyFrameIndicesCount = 20; + +static const int16 kAsScene1307KeyFrameIndices[] = { + 1, 4, 8, 11, 15, 16, 17, 17, 17, 16, + 15, 14, 12, 10, 9, 7, 5, 3, 2, 1 +}; + +const int kAsScene1307KeyDivValue = 200; +const int16 kAsScene1307KeyXDelta = 70; +const int16 kAsScene1307KeyYDelta = -12; + +AsScene1307Key::AsScene1307Key(NeverhoodEngine *vm, Scene *parentScene, uint keyIndex, NRect *clipRects) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _keyIndex(keyIndex), _clipRects(clipRects), + _isClickable(true) { + + NPoint pt; + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_keyIndex]; + + _dataResource.load(0x22102142); + _pointList = _dataResource.getPointArray(0xAC849240); + pt = (*_pointList)[getSubVar(VA_CURR_KEY_SLOT_NUMBERS, _keyIndex)]; + _x = pt.x; + _y = pt.y; + createSurface(kAsScene1307KeySurfacePriorities[getSubVar(VA_CURR_KEY_SLOT_NUMBERS, _keyIndex) % 4], 190, 148); + startAnimation(fileHashes[0], 0, -1); + loadSound(0, 0xDC4A1280); + loadSound(1, 0xCC021233); + loadSound(2, 0xC4C23844); + loadSound(3, 0xC4523208); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1307Key::handleMessage); +} + +uint32 AsScene1307Key::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_isClickable) { + sendMessage(_parentScene, 0x4826, 0); + stRemoveKey(); + messageResult = 1; + } + break; + case 0x2000: + _isClickable = param.asInteger() != 0; + break; + case 0x2001: + setSubVar(VA_CURR_KEY_SLOT_NUMBERS, _keyIndex, param.asInteger()); + stMoveKey(); + break; + case 0x2003: + playSound(3); + stUnlock(); + break; + case 0x2004: + playSound(2); + stInsert(); + break; + } + return messageResult; +} + +void AsScene1307Key::suRemoveKey() { + if (_pointIndex < kAsScene1307KeyPointsCount) { + _x += kAsScene1307KeyPoints[_pointIndex].x; + _y += kAsScene1307KeyPoints[_pointIndex].y; + updateBounds(); + _pointIndex++; + } else { + SetSpriteUpdate(NULL); + } +} + +void AsScene1307Key::suInsertKey() { + if (_pointIndex < kAsScene1307KeyPointsCount) { + _x -= kAsScene1307KeyPoints[kAsScene1307KeyPointsCount - _pointIndex - 1].x; + _y -= kAsScene1307KeyPoints[kAsScene1307KeyPointsCount - _pointIndex - 1].y; + updateBounds(); + _pointIndex++; + if (_pointIndex == 7) + playSound(0); + } else { + SetSpriteUpdate(NULL); + sendMessage(_parentScene, 0x2002, 0); + } +} + +void AsScene1307Key::suMoveKey() { + if (_pointIndex < kAsScene1307KeyFrameIndicesCount) { + _frameIndex += kAsScene1307KeyFrameIndices[_pointIndex]; + _x = _prevX + (_deltaX * _frameIndex) / kAsScene1307KeyDivValue; + _y = _prevY + (_deltaY * _frameIndex) / kAsScene1307KeyDivValue; + updateBounds(); + _pointIndex++; + } else { + NPoint pt = (*_pointList)[getSubVar(VA_CURR_KEY_SLOT_NUMBERS, _keyIndex)]; + _x = pt.x + kAsScene1307KeyXDelta; + _y = pt.y + kAsScene1307KeyYDelta; + stInsertKey(); + } +} + +void AsScene1307Key::stRemoveKey() { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_keyIndex]; + _pointIndex = 0; + startAnimation(fileHashes[0], 0, -1); + playSound(1); + SetSpriteUpdate(&AsScene1307Key::suRemoveKey); +} + +void AsScene1307Key::stInsertKey() { + _pointIndex = 0; + sendMessage(_parentScene, 0x1022, kAsScene1307KeySurfacePriorities[getSubVar(VA_CURR_KEY_SLOT_NUMBERS, _keyIndex) % 4]); + setClipRect(_clipRects[getSubVar(VA_CURR_KEY_SLOT_NUMBERS, _keyIndex) % 4]); + _newStickFrameIndex = STICK_LAST_FRAME; + SetSpriteUpdate(&AsScene1307Key::suInsertKey); +} + +void AsScene1307Key::stMoveKey() { + NPoint pt = (*_pointList)[getSubVar(VA_CURR_KEY_SLOT_NUMBERS, _keyIndex)]; + int16 newX = pt.x + kAsScene1307KeyXDelta; + int16 newY = pt.y + kAsScene1307KeyYDelta; + sendMessage(_parentScene, 0x1022, 1000); + setClipRect(0, 0, 640, 480); + _prevX = _x; + _prevY = _y; + if (newX == _x && newY == _y) { + stInsertKey(); + } else { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_keyIndex]; + _pointIndex = 0; + _frameIndex = 0; + _deltaX = newX - _x; + _deltaY = newY - _y; + startAnimation(fileHashes[0], 0, -1); + SetSpriteUpdate(&AsScene1307Key::suMoveKey); + } +} + +void AsScene1307Key::stUnlock() { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_keyIndex]; + startAnimation(fileHashes[1], 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; +} + +void AsScene1307Key::stInsert() { + const uint32 *fileHashes = kAsScene1307KeyResourceLists[_keyIndex]; + startAnimation(fileHashes[2], 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; +} + +Scene1307::Scene1307(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _countdown(0), _asCurrKey(NULL), + _isInsertingKey(false), _doLeaveScene(false), _isPuzzleSolved(false) { + + Sprite *tempSprite; + + _vm->gameModule()->initKeySlotsPuzzle(); + + _dataResource.load(0x22102142); + _keyHolePoints = _dataResource.getPointArray(0xAC849240); + + for (uint i = 0; i < 16; i++) { + NPoint pt = (*_keyHolePoints)[i]; + _keyHoleRects[i].x1 = pt.x - 15; + _keyHoleRects[i].y1 = pt.y - 15; + _keyHoleRects[i].x2 = pt.x + 15; + _keyHoleRects[i].y2 = pt.y + 15; + } + + SetMessageHandler(&Scene1307::handleMessage); + SetUpdateHandler(&Scene1307::update); + + setBackground(0xA8006200); + setPalette(0xA8006200); + addEntity(_palette); + insertPuzzleMouse(0x06204A88, 20, 620); + + tempSprite = insertStaticSprite(0x00A3621C, 800); + _clipRects[0].set(tempSprite->getDrawRect().x, 0, 640, 480); + tempSprite = insertStaticSprite(0x00A3641C, 600); + _clipRects[1].set(tempSprite->getDrawRect().x, 0, 640, 480); + tempSprite = insertStaticSprite(0x00A3681C, 400); + _clipRects[2].set(tempSprite->getDrawRect().x, 0, 640, 480); + tempSprite = insertStaticSprite(0x00A3701C, 200); + _clipRects[3].set(tempSprite->getDrawRect().x, 0, 640, 480); + + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) { + if (getSubVar(VA_IS_KEY_INSERTED, keyIndex)) { + _asKeys[keyIndex] = insertSprite<AsScene1307Key>(this, keyIndex, _clipRects); + addCollisionSprite(_asKeys[keyIndex]); + } else { + _asKeys[keyIndex] = NULL; + } + } + + loadSound(0, 0x68E25540); + +} + +void Scene1307::update() { + Scene::update(); + if (_countdown && (--_countdown == 0)) + _doLeaveScene = true; + else if (_countdown == 20) + _palette->startFadeToWhite(40); + if (_doLeaveScene && !isSoundPlaying(0)) { + leaveScene(1); + setGlobalVar(V_KEYDOOR_UNLOCKED, 1); + } +} + +uint32 Scene1307::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (!_isPuzzleSolved) { + if (param.asPoint().x > 20 && param.asPoint().x < 620) { + if (_asCurrKey && !_isInsertingKey) { + int16 mouseX = param.asPoint().x; + int16 mouseY = param.asPoint().y; + uint clickedKeyHoleIndex; + for (clickedKeyHoleIndex = 0; clickedKeyHoleIndex < 16; clickedKeyHoleIndex++) { + if (mouseX >= _keyHoleRects[clickedKeyHoleIndex].x1 && mouseX <= _keyHoleRects[clickedKeyHoleIndex].x2 && + mouseY >= _keyHoleRects[clickedKeyHoleIndex].y1 && mouseY <= _keyHoleRects[clickedKeyHoleIndex].y2) + break; + } + if (clickedKeyHoleIndex < 16) { + // Check if the clicked keyhole is already occupied with a key + bool occupied = false; + for (uint keyIndex = 0; keyIndex < 3 && !occupied; keyIndex++) { + if (getSubVar(VA_IS_KEY_INSERTED, keyIndex) && _asKeys[keyIndex] != _asCurrKey) { + if (getSubVar(VA_CURR_KEY_SLOT_NUMBERS, keyIndex) == clickedKeyHoleIndex) + occupied = true; + } + } + if (!occupied) { + // If the keyhole is free, insert the current key + sendMessage(_asCurrKey, 0x2001, clickedKeyHoleIndex); + _isInsertingKey = true; + _mouseClicked = false; + } + } + } + } else if (_countdown == 0 && !_asCurrKey && !_isInsertingKey) + leaveScene(0); + } + break; + case 0x2002: + // Check if all keys are in the correct keyholes + if (getSubVar(VA_IS_KEY_INSERTED, 0) && getSubVar(VA_CURR_KEY_SLOT_NUMBERS, 0) == getSubVar(VA_GOOD_KEY_SLOT_NUMBERS, 0) && + getSubVar(VA_IS_KEY_INSERTED, 1) && getSubVar(VA_CURR_KEY_SLOT_NUMBERS, 1) == getSubVar(VA_GOOD_KEY_SLOT_NUMBERS, 1) && + getSubVar(VA_IS_KEY_INSERTED, 2) && getSubVar(VA_CURR_KEY_SLOT_NUMBERS, 2) == getSubVar(VA_GOOD_KEY_SLOT_NUMBERS, 2)) { + // Play unlock animations for all keys + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) { + if (_asKeys[keyIndex]) + sendMessage(_asKeys[keyIndex], 0x2003, 1); + } + playSound(0); + _isPuzzleSolved = true; + _countdown = 47; + } else { + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) + if (getSubVar(VA_IS_KEY_INSERTED, keyIndex) && _asKeys[keyIndex]) + sendMessage(_asKeys[keyIndex], 0x2000, 1); + sendMessage(_asCurrKey, 0x2004, 1); + } + _asCurrKey = NULL; + _isInsertingKey = false; + break; + case 0x4826: + _asCurrKey = (Sprite*)sender; + for (uint keyIndex = 0; keyIndex < 3; keyIndex++) + if (getSubVar(VA_IS_KEY_INSERTED, keyIndex) && _asKeys[keyIndex]) + sendMessage(_asKeys[keyIndex], 0x2000, 0); + break; + } + return messageResult; +} + +static const uint32 kScene1308NumberFileHashes[] = { + 0x08006320, 0x10006320, 0x20006320, + 0x40006320, 0x80006320, 0x00006321, + 0x00006322, 0x00006324, 0x00006328, + 0x08306320, 0x10306320, 0x20306320, + 0x40306320, 0x80306320, 0x00306321, + 0x00306322 +}; + +AsScene1308JaggyDoor::AsScene1308JaggyDoor(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 0xBA0AE050, 1100, 320, 240), _parentScene(parentScene) { + + setVisible(false); + stopAnimation(); + SetMessageHandler(&AsScene1308JaggyDoor::handleMessage); +} + +uint32 AsScene1308JaggyDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + case 0x4808: + stOpenDoor(); + break; + case 0x4809: + stCloseDoor(); + break; + } + return messageResult; +} + +void AsScene1308JaggyDoor::stOpenDoor() { + startAnimation(0xBA0AE050, 0, -1); + setVisible(true); + playSound(0, calcHash("fxDoorOpen38")); + NextState(&AsScene1308JaggyDoor::stOpenDoorDone); +} + +void AsScene1308JaggyDoor::stOpenDoorDone() { + sendMessage(_parentScene, 0x2000, 0); + stopAnimation(); + setVisible(false); +} + +void AsScene1308JaggyDoor::stCloseDoor() { + startAnimation(0xBA0AE050, -1, -1); + _playBackwards = true; + setVisible(true); + playSound(0, calcHash("fxDoorClose38")); + NextState(&AsScene1308JaggyDoor::stCloseDoorDone); +} + +void AsScene1308JaggyDoor::stCloseDoorDone() { + sendMessage(_parentScene, 0x2001, 0); + stopAnimation(); +} + +AsScene1308KeyboardDoor::AsScene1308KeyboardDoor(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 0xA08A0851, 1100, 320, 240), _parentScene(parentScene) { + + playSound(0, 0x51456049); + SetMessageHandler(&AsScene1308KeyboardDoor::handleMessage); + NextState(&AsScene1308KeyboardDoor::stFallingKeys); +} + +uint32 AsScene1308KeyboardDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1308KeyboardDoor::stFallingKeys() { + startAnimation(0x6238B191, 0, -1); + _x = 580; + _y = 383; + NextState(&AsScene1308KeyboardDoor::stFallingKeysDone); +} + +void AsScene1308KeyboardDoor::stFallingKeysDone() { + sendMessage(_parentScene, 0x2004, 0); + stopAnimation(); + setVisible(false); +} + +AsScene1308LightWallSymbols::AsScene1308LightWallSymbols(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 0x80180A10, 100, 320, 240), _parentScene(parentScene) { + + setVisible(false); + stopAnimation(); + Entity::_priority = 1200; + SetMessageHandler(&AsScene1308LightWallSymbols::handleMessage); +} + +uint32 AsScene1308LightWallSymbols::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + stFadeIn(); + break; + case 0x2003: + stFadeOut(); + break; + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1308LightWallSymbols::stFadeIn() { + startAnimation(0x80180A10, 0, -1); + setVisible(true); + _newStickFrameIndex = STICK_LAST_FRAME; +} + +void AsScene1308LightWallSymbols::stFadeOut() { + startAnimation(0x80180A10, -1, -1); + _playBackwards = true; + NextState(&AsScene1308LightWallSymbols::stFadeOutDone); +} + +void AsScene1308LightWallSymbols::stFadeOutDone() { + sendMessage(_parentScene, 0x2003, 0); + stopAnimation(); + setVisible(false); +} + +SsScene1308Number::SsScene1308Number(NeverhoodEngine *vm, uint32 fileHash, int index) + : StaticSprite(vm, fileHash, 100) { + + setVisible(false); + _x = _spriteResource.getPosition().x + index * 20; + updatePosition(); +} + +AsScene1308Mouse::AsScene1308Mouse(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + _x = 286; + _y = 429; + createSurface1(0xA282C472, 100); + startAnimation(0xA282C472, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1308Mouse::handleMessage); +} + +uint32 AsScene1308Mouse::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x66382026) + playSound(0, 0x0CD84468); + else if (param.asInteger() == 0x6E28061C) + playSound(0, 0x78C8402C); + else if (param.asInteger() == 0x462F0410) + playSound(0, 0x60984E28); + break; + } + return messageResult; +} + +Scene1308::Scene1308(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isProjecting(false), _asProjector(NULL) { + + _vm->gameModule()->initKeySlotsPuzzle(); + + SetMessageHandler(&Scene1308::handleMessage); + + setBackground(0x41024202); + setPalette(0x41024202); + insertScreenMouse(0x24206418); + + _asTape = insertSprite<AsScene1201Tape>(this, 17, 1100, 502, 445, 0x9148A011); + addCollisionSprite(_asTape); + + if (getGlobalVar(V_MOUSE_SUCKED_IN)) { + insertSprite<AsScene1308Mouse>(); + insertSprite<AnimatedSprite>(0x461A1490, 200, 235, 429); + } + + _sprite1 = insertStaticSprite(0x0A042060, 1100); + _asJaggyDoor = insertSprite<AsScene1308JaggyDoor>(this); + _asLightWallSymbols = insertSprite<AsScene1308LightWallSymbols>(this); + _ssNumber1 = insertSprite<SsScene1308Number>(kScene1308NumberFileHashes[getSubVar(VA_GOOD_KEY_SLOT_NUMBERS, 1)], 0); + _ssNumber2 = insertSprite<SsScene1308Number>(kScene1308NumberFileHashes[getSubVar(VA_GOOD_KEY_SLOT_NUMBERS, 0)], 1); + _ssNumber3 = insertSprite<SsScene1308Number>(kScene1308NumberFileHashes[getSubVar(VA_GOOD_KEY_SLOT_NUMBERS, 2)], 2); + _sprite2 = insertStaticSprite(0x40043120, 995); + _sprite3 = insertStaticSprite(0x43003100, 995); + _sprite4 = NULL; + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1308>(380, 440); + setMessageList(0x004B57C0); + if (getGlobalVar(V_KEYDOOR_UNLOCKED)) { + _sprite4 = insertStaticSprite(0x0101A624, 1100); + setRectList(0x004B5990); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + setRectList(0x004B5980); + } + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene1308>(640, 440); + setMessageList(0x004B57C8); + if (getGlobalVar(V_KEYDOOR_UNLOCKED)) { + _sprite4 = insertStaticSprite(0x0101A624, 1100); + setRectList(0x004B5990); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + setRectList(0x004B5980); + } + } else if (which == 2) { + // Klaymen returning from keyslots panel + insertKlaymen<KmScene1308>(475, 440); + setMessageList(0x004B58B0); + if (getGlobalVar(V_KEYDOOR_UNLOCKED)) { + _sprite5 = insertSprite<AsScene1308KeyboardDoor>(this); + _sprite4 = insertStaticSprite(0x0101A624, 1100); + _sprite4->setVisible(false); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + setRectList(0x004B5980); + } + } else { + // Klaymen entering from the left + insertKlaymen<KmScene1308>(41, 440); + setMessageList(0x004B57D0); + sendMessage(_asJaggyDoor, 0x4808, 0); + _sprite1->setVisible(false); + if (getGlobalVar(V_KEYDOOR_UNLOCKED)) { + _sprite4 = insertStaticSprite(0x0101A624, 1100); + _klaymen->setVisible(false); + } else { + _sprite5 = insertStaticSprite(0x080811A0, 100); + _klaymen->setVisible(false); + } + } + + if (_sprite4) + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, _sprite4->getDrawRect().x2(), 480); + else + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + + if (getGlobalVar(V_PROJECTOR_LOCATION) == 4) { + _asProjector = insertSprite<AsCommonProjector>(this, _klaymen, (Sprite*)NULL); + addCollisionSprite(_asProjector); + _asProjector->setClipRect(0, 0, 640, _sprite2->getDrawRect().y2()); + _asProjector->setRepl(64, 0); + } + +} + +uint32 Scene1308::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x88C11390) { + setRectList(0x004B59A0); + _isProjecting = true; + } else if (param.asInteger() == 0x08821382) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + if (getGlobalVar(V_KEYDOOR_UNLOCKED)) + setRectList(0x004B5990); + else + setRectList(0x004B5980); + _isProjecting = false; + } else if (param.asInteger() == 0x4AC68808) { + clearRectList(); + sendMessage(_asJaggyDoor, 0x4809, 0); + _sprite1->setVisible(false); + _klaymen->setVisible(false); + } + break; + case 0x1022: + if (sender == _asProjector) { + if (param.asInteger() >= 1000) + setSurfacePriority(_sprite3->getSurface(), 1100); + else + setSurfacePriority(_sprite3->getSurface(), 995); + } + break; + case 0x2000: + if (getGlobalVar(V_KEYDOOR_UNLOCKED)) + setRectList(0x004B5990); + else + setRectList(0x004B5980); + setMessageList(0x004B57E8, false); + _sprite1->setVisible(true); + _klaymen->setVisible(true); + break; + case 0x2001: + leaveScene(0); + break; + case 0x2003: + _ssNumber1->setVisible(false); + _ssNumber2->setVisible(false); + _ssNumber3->setVisible(false); + break; + case 0x2004: + _sprite4->setVisible(true); + setRectList(0x004B5990); + break; + case 0x4807: + sendMessage(_asLightWallSymbols, 0x2003, 0); + break; + case 0x480F: + sendMessage(_asLightWallSymbols, 0x2002, 0); + _ssNumber1->setVisible(true); + _ssNumber2->setVisible(true); + _ssNumber3->setVisible(true); + break; + case 0x4826: + if (sender == _asProjector) { + if (_isProjecting) + setMessageList2(0x004B5868); + else { + if (param.asInteger() == 1) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setMessageList2(0x004B5848); + } else if (sendMessage(_asProjector, 0x480C, _klaymen->getX() <= _asProjector->getX() ? 0 : 1) != 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setMessageList2(0x004B5830); + } else + setMessageList2(0x004B5800); + } + } else if (sender == _asTape) { + if (_isProjecting) + setMessageList2(0x004B5868); + else if (_messageListStatus != 2) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList2(0x004B58E0); + } + } + break; + } + return 0; +} + +Scene1317::Scene1317(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene1317::handleMessage); + _smackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, 0x08982841, true, false)); + _vm->_screen->setSmackerDecoder(_smackerPlayer->getSmackerDecoder()); + insertScreenMouse(0x08284011); + showMouse(false); + _smackerFileHash = 0; + _keepLastSmackerFrame = false; +} + +void Scene1317::update() { + if (_smackerFileHash) { + _smackerPlayer->open(_smackerFileHash, _keepLastSmackerFrame); + _smackerFileHash = 0; + } + Scene::update(); +} + +void Scene1317::upChooseKing() { + if (!_klaymenBlinks && _klaymenBlinkCountdown != 0 && (--_klaymenBlinkCountdown == 0)) + _klaymenBlinks = true; + + if (!_klaymenBlinks && _smackerPlayer->getFrameNumber() + 1 >= 2) { + _smackerPlayer->rewind(); + } else if (_klaymenBlinks && _smackerPlayer->getFrameNumber() + 1 >= 6) { + _smackerPlayer->rewind(); + _klaymenBlinks = false; + _klaymenBlinkCountdown = _vm->_rnd->getRandomNumber(30 - 1) + 15; + } + + if (!_klaymenBlinks && _decisionCountdown != 0 && (--_decisionCountdown == 0)) + stNoDecisionYet(); + + if (_smackerFileHash) { + _smackerPlayer->open(_smackerFileHash, _keepLastSmackerFrame); + _smackerFileHash = 0; + } + + Scene::update(); + +} + +uint32 Scene1317::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + stChooseKing(); + break; + } + return messageResult; +} + +uint32 Scene1317::hmChooseKing(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x >= 21 && param.asPoint().y >= 24 && + param.asPoint().x <= 261 && param.asPoint().y <= 280) { + stHoborgAsKing(); + } else if (param.asPoint().x >= 313 && param.asPoint().y >= 184 && + param.asPoint().x <= 399 && param.asPoint().y <= 379) { + stKlaymenAsKing(); + } else if (param.asPoint().x >= 347 && param.asPoint().y >= 380 && + param.asPoint().x <= 418 && param.asPoint().y <= 474) { + stKlaymenAsKing(); + } + break; + } + return messageResult; +} + +uint32 Scene1317::hmHoborgAsKing(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + stEndMovie(); + break; + } + return messageResult; +} + +uint32 Scene1317::hmEndMovie(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + leaveScene(0); + break; + } + return messageResult; +} + +void Scene1317::stChooseKing() { + showMouse(true); + _smackerFileHash = 0x10982841; + _keepLastSmackerFrame = true; + _decisionCountdown = 450; + _klaymenBlinks = false; + _klaymenBlinkCountdown = _vm->_rnd->getRandomNumber(30 - 1) + 15; + SetMessageHandler(&Scene1317::hmChooseKing); + SetUpdateHandler(&Scene1317::upChooseKing); +} + +void Scene1317::stNoDecisionYet() { + showMouse(false); + _smackerFileHash = 0x20982841; + _keepLastSmackerFrame = false; + SetMessageHandler(&Scene1317::handleMessage); + SetUpdateHandler(&Scene1317::update); +} + +void Scene1317::stHoborgAsKing() { + showMouse(false); + _smackerFileHash = 0x40982841; + _keepLastSmackerFrame = false; + SetMessageHandler(&Scene1317::hmHoborgAsKing); + SetUpdateHandler(&Scene1317::update); +} + +void Scene1317::stKlaymenAsKing() { + showMouse(false); + _smackerFileHash = 0x80982841; + _keepLastSmackerFrame = false; + SetMessageHandler(&Scene1317::hmEndMovie); + SetUpdateHandler(&Scene1317::update); +} + +void Scene1317::stEndMovie() { + showMouse(false); + _smackerFileHash = 0x40800711; + _keepLastSmackerFrame = false; + SetMessageHandler(&Scene1317::hmEndMovie); + SetUpdateHandler(&Scene1317::update); +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1300.h b/engines/neverhood/modules/module1300.h new file mode 100644 index 0000000000..501f76304f --- /dev/null +++ b/engines/neverhood/modules/module1300.h @@ -0,0 +1,295 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1300_H +#define NEVERHOOD_MODULES_MODULE1300_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" + +namespace Neverhood { + +// Module1300 + +class Module1300 : public Module { +public: + Module1300(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1300(); +protected: + int _sceneNum; + uint32 _musicFileHash; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class AsScene1302Bridge : public AnimatedSprite { +public: + AsScene1302Bridge(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stLowerBridge(); + void stRaiseBridge(); + void cbLowerBridgeEvent(); +}; + +class SsScene1302Fence : public StaticSprite { +public: + SsScene1302Fence(NeverhoodEngine *vm); +protected: + int16 _firstY; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoveDown(); + void suMoveUp(); +}; + +class Scene1302 : public Scene { +public: + Scene1302(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_asVenusFlyTrap; + Sprite *_asBridge; + Sprite *_ssFence; + Sprite *_asRing1; + Sprite *_asRing2; + Sprite *_asRing3; + Sprite *_asRing4; + Sprite *_asRing5; + Sprite *_class595; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1303Balloon : public AnimatedSprite { +public: + AsScene1303Balloon(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmBalloonPopped(int messageNum, const MessageParam ¶m, Entity *sender); + void stPopBalloon(); +}; + +class Scene1303 : public Scene { +public: + Scene1303(NeverhoodEngine *vm, Module *parentModule); +protected: + Sprite *_sprite1; + Sprite *_asBalloon; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1304Needle : public AnimatedSprite { +public: + AsScene1304Needle(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, int16 x, int16 y); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1304 : public Scene { +public: + Scene1304(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + Sprite *_asKey; + Sprite *_asNeedle; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1305 : public Scene { +public: + Scene1305(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1306Elevator : public AnimatedSprite { +public: + AsScene1306Elevator(NeverhoodEngine *vm, Scene *parentScene, AnimatedSprite *asElevatorDoor); +protected: + Scene *_parentScene; + AnimatedSprite *_asElevatorDoor; + bool _isUp; + bool _isDown; + int _countdown; + void update(); + void upGoingDown(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stGoingUp(); + void cbGoingUpEvent(); + void stGoingDown(); + void cbGoingDownEvent(); +}; + +class Scene1306 : public Scene { +public: + Scene1306(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene1306(); +protected: + Sprite *_ssButton; + Sprite *_asTape; + AnimatedSprite *_asElevatorDoor; + Sprite *_asElevator; + Sprite *_sprite1; + Sprite *_asKey; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage416EB0(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1307Key : public AnimatedSprite { +public: + AsScene1307Key(NeverhoodEngine *vm, Scene *parentScene, uint keyIndex, NRect *clipRects); +protected: + Scene *_parentScene; + NPointArray *_pointList; + uint _pointIndex; + int _frameIndex; + uint _keyIndex; + NRect *_clipRects; + bool _isClickable; + int16 _prevX, _prevY; + int16 _deltaX, _deltaY; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suRemoveKey(); + void suInsertKey(); + void suMoveKey(); + void stRemoveKey(); + void stInsertKey(); + void stMoveKey(); + void stUnlock(); + void stInsert(); +}; + +class Scene1307 : public Scene { +public: + Scene1307(NeverhoodEngine *vm, Module *parentModule); +protected: + NPointArray *_keyHolePoints; + NRect _keyHoleRects[16]; + NRect _clipRects[4]; + Sprite *_asKeys[3]; + int _countdown; + Sprite *_asCurrKey; + bool _isInsertingKey; + bool _doLeaveScene; + bool _isPuzzleSolved; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene1308JaggyDoor : public AnimatedSprite { +public: + AsScene1308JaggyDoor(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stOpenDoor(); + void stOpenDoorDone(); + void stCloseDoor(); + void stCloseDoorDone(); +}; + +class AsScene1308KeyboardDoor : public AnimatedSprite { +public: + AsScene1308KeyboardDoor(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stFallingKeys(); + void stFallingKeysDone(); +}; + +class AsScene1308LightWallSymbols : public AnimatedSprite { +public: + AsScene1308LightWallSymbols(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stFadeIn(); + void stFadeOut(); + void stFadeOutDone(); +}; + +class SsScene1308Number : public StaticSprite { +public: + SsScene1308Number(NeverhoodEngine *vm, uint32 fileHash, int index); +}; + +class AsScene1308Mouse : public AnimatedSprite { +public: + AsScene1308Mouse(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1308 : public Scene { +public: + Scene1308(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_asTape; + Sprite *_asJaggyDoor; + Sprite *_asLightWallSymbols; + Sprite *_ssNumber1; + Sprite *_ssNumber2; + Sprite *_ssNumber3; + AnimatedSprite *_asProjector; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_sprite4; + Sprite *_sprite5; + bool _isProjecting; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1317 : public Scene { +public: + Scene1317(NeverhoodEngine *vm, Module *parentModule); +protected: + SmackerPlayer *_smackerPlayer; + bool _klaymenBlinks; + int _klaymenBlinkCountdown; + int _decisionCountdown; + uint32 _smackerFileHash; + bool _keepLastSmackerFrame; + void update(); + void upChooseKing(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmChooseKing(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmHoborgAsKing(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmEndMovie(int messageNum, const MessageParam ¶m, Entity *sender); + void stChooseKing(); + void stNoDecisionYet(); + void stHoborgAsKing(); + void stKlaymenAsKing(); + void stEndMovie(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1300_H */ diff --git a/engines/neverhood/modules/module1400.cpp b/engines/neverhood/modules/module1400.cpp new file mode 100644 index 0000000000..4f69637ee0 --- /dev/null +++ b/engines/neverhood/modules/module1400.cpp @@ -0,0 +1,1621 @@ +/* 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/module1400.h" +#include "neverhood/modules/module1000.h" +#include "neverhood/modules/module2100.h" +#include "neverhood/modules/module2200.h" +#include "neverhood/diskplayerscene.h" +#include "neverhood/gamemodule.h" + +namespace Neverhood { + +Module1400::Module1400(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + _vm->_soundMan->addMusic(0x00AD0012, 0x06333232); + _vm->_soundMan->addMusic(0x00AD0012, 0x624A220E); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else + createScene(0, 0); + +} + +Module1400::~Module1400() { + _vm->_soundMan->deleteMusicGroup(0x00AD0012); +} + +void Module1400::createScene(int sceneNum, int which) { + debug("Module1400::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _vm->_soundMan->startMusic(0x06333232, 0, 2); + _childObject = new Scene1401(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _vm->_soundMan->stopMusic(0x06333232, 0, 2); + _vm->_soundMan->stopMusic(0x624A220E, 0, 2); + _childObject = new Scene1402(_vm, this, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->_soundMan->stopMusic(0x06333232, 0, 2); + _vm->_soundMan->startMusic(0x624A220E, 0, 2); + _childObject = new Scene1403(_vm, this, which); + break; + case 3: + _vm->gameState().sceneNum = 3; + _vm->_soundMan->startMusic(0x06333232, 0, 2); + _childObject = new Scene1404(_vm, this, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->startMusic(0x06333232, 0, 2); + _childObject = new Scene1405(_vm, this); + break; + case 5: + _vm->gameState().sceneNum = 5; + _vm->_soundMan->stopMusic(0x06333232, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 2); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->_soundMan->stopMusic(0x06333232, 0, 2); + _childObject = new Scene1407(_vm, this); + break; + } + SetUpdateHandler(&Module1400::updateScene); + _childObject->handleUpdate(); +} + +void Module1400::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(1, 0); + else if (_moduleResult == 2) + createScene(3, 0); + else + leaveModule(0); + break; + case 1: + if (_moduleResult == 1) + createScene(2, 0); + else if (_moduleResult == 2) + createScene(6, -1); + else + createScene(0, 1); + break; + case 2: + createScene(1, 1); + break; + case 3: + if (_moduleResult == 1) + createScene(4, 0); + else if (_moduleResult == 2) + createScene(5, -1); + else + createScene(0, 2); + break; + case 4: + createScene(3, 1); + break; + case 5: + createScene(3, 2); + break; + case 6: + createScene(1, 2); + break; + } + } +} + +// Scene1401 + +AsScene1401Pipe::AsScene1401Pipe(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100), _countdown1(0), _countdown2(0) { + + createSurface(900, 152, 147); + _x = 454; + _y = 217; + startAnimation(0x4C210500, 0, -1); + SetUpdateHandler(&AsScene1401Pipe::update); + SetMessageHandler(&AsScene1401Pipe::handleMessage); +} + +AsScene1401Pipe::~AsScene1401Pipe() { + _vm->_soundMan->deleteSoundGroup(0x01104C08); +} + +void AsScene1401Pipe::update() { + AnimatedSprite::update(); + if (_countdown1 != 0 && (--_countdown1 == 0)) + stDoneSucking(); + if (_countdown2 != 0 && (--_countdown2 == 0)) { + _vm->_soundMan->addSound(0x01104C08, 0x4A116437); + _vm->_soundMan->playSoundLooping(0x4A116437); + } +} + +void AsScene1401Pipe::upSuckInProjector() { + AnimatedSprite::update(); + if (_countdown1 != 0) + _countdown1--; +} + +uint32 AsScene1401Pipe::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0A8A1490) + playSound(1, 0x6AB6666F); + break; + case 0x2000: + _countdown1 = 70; + _countdown2 = 8; + stStartSucking(); + break; + case 0x483A: + stSuckInProjector(); + break; + } + return messageResult; +} + +uint32 AsScene1401Pipe::hmSuckInProjector(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + if (_countdown1 != 0) + stStartSucking(); + else + stDoneSucking(); + SetMessageHandler(&AsScene1401Pipe::handleMessage); + SetUpdateHandler(&AsScene1401Pipe::update); + break; + } + return messageResult; +} + +void AsScene1401Pipe::stStartSucking() { + startAnimation(0x4C240100, 0, -1); + playSound(0, 0x4A30063F); +} + +void AsScene1401Pipe::stDoneSucking() { + _vm->_soundMan->deleteSound(0x4A116437); + playSound(0, 0x4A120435); + startAnimation(0x4C210500, 0, -1); +} + +void AsScene1401Pipe::stSuckInProjector() { + startAnimation(0x6C210810, 0, -1); + SetUpdateHandler(&AsScene1401Pipe::upSuckInProjector); + SetMessageHandler(&AsScene1401Pipe::hmSuckInProjector); +} + +AsScene1401Mouse::AsScene1401Mouse(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + createSurface(100, 71, 41); + _x = 478; + _y = 433; + startAnimation(0xA282C472, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1401Mouse::handleMessage); +} + +uint32 AsScene1401Mouse::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x66382026) + playSound(0, 0x0CD84468); + else if (param.asInteger() == 0x6E28061C) + playSound(0, 0x78C8402C); + else if (param.asInteger() == 0x462F0410) + playSound(0, 0x60984E28); + break; + case 0x4839: + stSuckedIn(); + break; + } + return messageResult; +} + +void AsScene1401Mouse::suSuckedIn() { + AnimatedSprite::updateDeltaXY(); + if (_collisionBounds.y1 <= 150) { + playSound(0, 0x0E32247F); + stopAnimation(); + setVisible(false); + SetMessageHandler(NULL); + SetSpriteUpdate(NULL); + } +} + +void AsScene1401Mouse::stSuckedIn() { + startAnimation(0x34880040, 0, -1); + SetSpriteUpdate(&AsScene1401Mouse::suSuckedIn); +} + +AsScene1401Cheese::AsScene1401Cheese(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + createSurface(200, 152, 147); + _x = 427; + _y = 433; + startAnimation(0x461A1490, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1401Cheese::handleMessage); +} + +uint32 AsScene1401Cheese::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4839: + stSuckedIn(); + break; + } + return messageResult; +} + +void AsScene1401Cheese::suSuckedIn() { + AnimatedSprite::updateDeltaXY(); + if (_collisionBounds.y1 <= 150) { + playSound(0, 0x18020439); + stopAnimation(); + setVisible(false); + SetMessageHandler(NULL); + SetSpriteUpdate(NULL); + } +} + +void AsScene1401Cheese::stSuckedIn() { + startAnimation(0x103B8020, 0, -1); + SetSpriteUpdate(&AsScene1401Cheese::suSuckedIn); +} + +AsScene1401BackDoor::AsScene1401BackDoor(NeverhoodEngine *vm, Sprite *klaymen, bool isOpen) + : AnimatedSprite(vm, 1100), _klaymen(klaymen), _countdown(0), _isOpen(isOpen) { + + _x = 320; + _y = 240; + createSurface1(0x04551900, 100); + if (isOpen) { + startAnimation(0x04551900, -1, -1); + _countdown = 48; + } else { + stopAnimation(); + setVisible(false); + } + _newStickFrameIndex = STICK_LAST_FRAME; + SetUpdateHandler(&AsScene1401BackDoor::update); + SetMessageHandler(&AsScene1401BackDoor::handleMessage); +} + +void AsScene1401BackDoor::update() { + if (_countdown != 0 && (--_countdown == 0)) + stCloseDoor(); + AnimatedSprite::update(); +} + + +uint32 AsScene1401BackDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + if (_isOpen) + _countdown = 168; + messageResult = _isOpen ? 1 : 0; + break; + case 0x3002: + gotoNextState(); + break; + case 0x4808: + _countdown = 168; + if (!_isOpen) + stOpenDoor(); + break; + } + return messageResult; +} + +void AsScene1401BackDoor::stOpenDoor() { + _isOpen = true; + setVisible(true); + startAnimation(0x04551900, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + playSound(0, calcHash("fxDoorOpen24")); +} + +void AsScene1401BackDoor::stCloseDoor() { + _isOpen = false; + setVisible(true); + startAnimation(0x04551900, -1, -1); + playSound(0, calcHash("fxDoorClose24")); + _playBackwards = true; + NextState(&AsScene1401BackDoor::stCloseDoorDone); +} + +void AsScene1401BackDoor::stCloseDoorDone() { + stopAnimation(); + setVisible(false); +} + +static const AsCommonProjectorItem kAsCommonProjectorItems[] = { + {{154, 453}, 4, 2, 0, 0, 1}, + {{104, 391}, 4, -1, -1, 1, 1}, + {{ 22, 447}, 6, -1, -1, 1, 1}, + {{112, 406}, 2, -1, -1, 1, 0}, + {{262, 433}, 1, 1, 0, 0, 0} +}; + +AsCommonProjector::AsCommonProjector(NeverhoodEngine *vm, Scene *parentScene, Sprite *klaymen, Sprite *asPipe) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _klaymen(klaymen), _asPipe(asPipe) { + + _asProjectorItem = &kAsCommonProjectorItems[getGlobalVar(V_PROJECTOR_LOCATION)]; + createSurface(990, 101, 182); + startAnimation(0x10E3042B, 0, -1); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsCommonProjector::handleMessage); + _x = getGlobalVar(V_PROJECTOR_SLOT) * 108 + _asProjectorItem->point.x; + _lockedInSlot = true; + moveProjector(); + setDoDeltaX(1); + if ((int8)getGlobalVar(V_PROJECTOR_SLOT) == _asProjectorItem->lockSlotIndex) + stStayLockedInSlot(); + loadSound(2, 0xC8C2507C); +} + +AsCommonProjector::~AsCommonProjector() { + _vm->_soundMan->deleteSoundGroup(0x05331081); +} + +uint32 AsCommonProjector::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 0x4807: + setGlobalVar(V_PROJECTOR_SLOT, (_x - _asProjectorItem->point.x) / 108); + if ((int8)getGlobalVar(V_PROJECTOR_SLOT) == _asProjectorItem->lockSlotIndex) + stStartLockedInSlot(); + else + stIdle(); + break; + case 0x480B: + if (param.asInteger() != 1) { + if ((int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount) + incGlobalVar(V_PROJECTOR_SLOT, 1); + } else if (getGlobalVar(V_PROJECTOR_SLOT) > 0) + incGlobalVar(V_PROJECTOR_SLOT, -1); + stMoving(); + break; + case 0x480C: + // Check if the projector can be moved + if (param.asInteger() != 1) + messageResult = (int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount ? 1 : 0; + else + messageResult = getGlobalVar(V_PROJECTOR_SLOT) > 0 ? 1 : 0; + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + case 0x4839: + stStartSuckedIn(); + break; + } + return messageResult; +} + +uint32 AsCommonProjector::hmLockedInSlot(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (param.asPoint().x - _x >= 17 && param.asPoint().x - _x <= 56 && + param.asPoint().y - _y >= -120 && param.asPoint().y - _y <= -82) { + sendMessage(_parentScene, 0x4826, 1); + } else + sendMessage(_parentScene, 0x4826, 0); + messageResult = 1; + break; + case 0x4807: + sendMessage(_parentScene, 0x4807, 0); + stStopProjecting(); + break; + case 0x480B: + if (param.asInteger() != 1) { + if ((int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount) + incGlobalVar(V_PROJECTOR_SLOT, 1); + } else if (getGlobalVar(V_PROJECTOR_SLOT) > 0) + incGlobalVar(V_PROJECTOR_SLOT, -1); + stTurnToFront(); + break; + case 0x480C: + // Check if the projector can be moved + if (param.asInteger() != 1) + messageResult = (int8)getGlobalVar(V_PROJECTOR_SLOT) < _asProjectorItem->maxSlotCount ? 1 : 0; + else + messageResult = getGlobalVar(V_PROJECTOR_SLOT) > 0 ? 1 : 0; + break; + case 0x480F: + stStartProjecting(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsCommonProjector::hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsCommonProjector::suMoving() { + if (_x <= _klaymen->getX()) + _x = _klaymen->getX() - 100; + else + _x = _klaymen->getX() + 100; + moveProjector(); + if (_beforeMoveX == _x) { + if (getGlobalVar(V_PROJECTOR_SLOT) == 0 && _asProjectorItem->leftBorderLeaves != 0) { + sendMessage(_parentScene, 0x1019, 0); + incGlobalVar(V_PROJECTOR_LOCATION, -1); + setGlobalVar(V_PROJECTOR_SLOT, kAsCommonProjectorItems[getGlobalVar(V_PROJECTOR_LOCATION)].maxSlotCount); + } else if ((int8)getGlobalVar(V_PROJECTOR_SLOT) == _asProjectorItem->maxSlotCount && _asProjectorItem->rightBorderLeaves != 0) { + sendMessage(_parentScene, 0x1019, 1); + incGlobalVar(V_PROJECTOR_LOCATION, +1); + setGlobalVar(V_PROJECTOR_SLOT, 0); + } + } + Sprite::updateBounds(); +} + +void AsCommonProjector::moveProjector() { + + bool nowLockedInSlot = false; + + _y = _asProjectorItem->point.y; + + if (_asProjectorItem->index1 != -1) { + int16 elX = _asProjectorItem->index1 * 108 + _asProjectorItem->point.x; + if (elX - 20 < _x && elX + 20 > _x) { + nowLockedInSlot = true; + _y = _asProjectorItem->point.y + 10; + } + } + + if (_asProjectorItem->lockSlotIndex != -1) { + int16 elX = _asProjectorItem->lockSlotIndex * 108 + _asProjectorItem->point.x; + if (elX - 20 < _x && elX + 20 > _x) { + nowLockedInSlot = true; + _y = _asProjectorItem->point.y + 10; + } + } + + if (_lockedInSlot && !nowLockedInSlot) + _lockedInSlot = false; + else if (!_lockedInSlot && nowLockedInSlot) { + playSound(1, 0x5440E474); + _lockedInSlot = true; + } + +} + +void AsCommonProjector::stSuckedIn() { + AnimatedSprite::updateDeltaXY(); + if (_collisionBounds.y1 <= 150) { + sendMessage(_asPipe, 0x483A, 0); + stopAnimation(); + setVisible(false); + SetMessageHandler(&Sprite::handleMessage); + SetSpriteUpdate(NULL); + } +} + +void AsCommonProjector::stIdle() { + startAnimation(0x10E3042B, 0, -1); + SetMessageHandler(&AsCommonProjector::handleMessage); + SetSpriteUpdate(NULL); +} + +void AsCommonProjector::stMoving() { + _beforeMoveX = getGlobalVar(V_PROJECTOR_SLOT) * 108 + _asProjectorItem->point.x; + startAnimation(0x14A10137, 0, -1); + playSound(1, 0xEC008474); + SetMessageHandler(&AsCommonProjector::handleMessage); + SetSpriteUpdate(&AsCommonProjector::suMoving); +} + +void AsCommonProjector::stStartLockedInSlot() { + startAnimation(0x80C32213, 0, -1); + SetMessageHandler(&AsCommonProjector::hmAnimation); + SetSpriteUpdate(NULL); + NextState(&AsCommonProjector::stStayLockedInSlot); +} + +void AsCommonProjector::stStayLockedInSlot() { + startAnimation(0xD23B207F, 0, -1); + SetMessageHandler(&AsCommonProjector::hmLockedInSlot); + SetSpriteUpdate(NULL); +} + +void AsCommonProjector::stStartProjecting() { + startAnimation(0x50A80517, 0, -1); + setGlobalVar(V_PROJECTOR_ACTIVE, 1); + playSound(0, 0xCC4A8456); + _vm->_soundMan->addSound(0x05331081, 0xCE428854); + _vm->_soundMan->playSoundLooping(0xCE428854); + SetMessageHandler(&AsCommonProjector::hmAnimation); + SetSpriteUpdate(NULL); + NextState(&AsCommonProjector::stLockedInSlot); +} + +void AsCommonProjector::stLockedInSlot() { + sendMessage(_parentScene, 0x480F, 0); + startAnimation(0xD833207F, 0, -1); + SetMessageHandler(&AsCommonProjector::hmLockedInSlot); + SetSpriteUpdate(NULL); +} + +void AsCommonProjector::stStopProjecting() { + startAnimation(0x50A94417, 0, -1); + setGlobalVar(V_PROJECTOR_ACTIVE, 0); + playSound(0, 0xCC4A8456); + _vm->_soundMan->deleteSound(0xCE428854); + SetMessageHandler(&AsCommonProjector::hmAnimation); + SetSpriteUpdate(NULL); + NextState(&AsCommonProjector::stStayLockedInSlot); +} + +void AsCommonProjector::stTurnToFront() { + _beforeMoveX = getGlobalVar(V_PROJECTOR_SLOT) * 108 + _asProjectorItem->point.x; + startAnimation(0x22CB4A33, 0, -1); + SetMessageHandler(&AsCommonProjector::hmAnimation); + SetSpriteUpdate(&AsCommonProjector::suMoving); + NextState(&AsCommonProjector::stMoving); +} + +void AsCommonProjector::stStartSuckedIn() { + setGlobalVar(V_PROJECTOR_LOCATION, 4); + setGlobalVar(V_PROJECTOR_SLOT, 0); + startAnimation(0x708D4712, 0, -1); + playSound(2); + SetMessageHandler(&Sprite::handleMessage); + SetSpriteUpdate(&AsCommonProjector::stSuckedIn); +} + +Scene1401::Scene1401(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _projectorBorderFlag(false), _ssFloorButton(NULL), _asProjector(NULL), + _asPipe(NULL), _asMouse(NULL), _asCheese(NULL), _asBackDoor(NULL), + _sprite1(NULL), _sprite2(NULL), _sprite3(NULL), _ssButton(NULL) { + + SetMessageHandler(&Scene1401::handleMessage); + SetUpdateHandler(&Scene1401::update); + + setRectList(0x004B6758); + setBackground(0x08221FA5); + setPalette(0x08221FA5); + insertScreenMouse(0x21FA108A); + + _ssFloorButton = insertSprite<SsCommonFloorButton>(this, 0x980F3124, 0x12192892, 100, 0); + _asPipe = insertSprite<AsScene1401Pipe>(); + + if (!getGlobalVar(V_MOUSE_SUCKED_IN)) { + _asMouse = insertSprite<AsScene1401Mouse>(); + _asCheese = insertSprite<AsScene1401Cheese>(); + } + + _sprite3 = insertStaticSprite(0xA82BA811, 1100); + insertStaticSprite(0x0A116C60, 1100); + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0xB84B1100, 100, 0); + _sprite1 = insertStaticSprite(0x38EA100C, 1005); + _sprite2 = insertStaticSprite(0x98D0223C, 1200); + _sprite2->setVisible(false); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1401>(380, 447); + setMessageList(0x004B65C8); + _sprite1->setVisible(false); + } else if (which == 1) { + // Klaymen entering from the left + insertKlaymen<KmScene1401>(0, 447); + setMessageList(0x004B65D0); + _sprite1->setVisible(false); + } else if (which == 2) { + // Klaymen entering from the right + insertKlaymen<KmScene1401>(660, 447); + setMessageList(0x004B65D8); + _sprite1->setVisible(false); + } else { + // Klaymen entering from the back + insertKlaymen<KmScene1401>(290, 413); + setMessageList(0x004B65E8); + _sprite1->setVisible(false); + } + + if (getGlobalVar(V_PROJECTOR_LOCATION) == 2) { + _asProjector = insertSprite<AsCommonProjector>(this, _klaymen, _asPipe); + addCollisionSprite(_asProjector); + if (getGlobalVar(V_PROJECTOR_SLOT) == 6) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + _klaymen->setX(_asProjector->getX() + 100); + _klaymen->updateBounds(); + setMessageList(0x004B6670); + } else if (getGlobalVar(V_PROJECTOR_SLOT) == 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + _klaymen->setX(_asProjector->getX() - 100); + _klaymen->updateBounds(); + setMessageList(0x004B6670); + } + _asProjector->setClipRect(_sprite3->getDrawRect().x, _sprite2->getDrawRect().y, 640, 480); + } + + _klaymen->setClipRect(_sprite3->getDrawRect().x, 0, 640, 480); + + if (which == 0 && _asProjector) + sendMessage(_asProjector, 0x482B, 0); + + _asBackDoor = insertSprite<AsScene1401BackDoor>(_klaymen, which == 0); + +} + +void Scene1401::update() { + Scene::update(); + if (_asProjector && !_projectorBorderFlag && _asProjector->getY() < 360) { + _sprite2->setVisible(true); + _projectorBorderFlag = true; + } else + _sprite2->setVisible(false); +} + +uint32 Scene1401::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02144CB1) + sendEntityMessage(_klaymen, 0x1014, _ssFloorButton); + else if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssButton); + else if (param.asInteger() == 0x01C66840) { + if (sendMessage(_asBackDoor, 0x2001, 0) != 0) + setMessageList(0x004B6690); + else + setMessageList(0x004B66B0); + } + break; + case 0x1019: + if (param.asInteger() != 0) + leaveScene(2); + else + leaveScene(1); + break; + case 0x480B: + if (sender == _ssFloorButton) { + sendMessage(_asPipe, 0x2000, 0); + if (!getGlobalVar(V_MOUSE_SUCKED_IN)) { + sendMessage(_asMouse, 0x4839, 0); + sendMessage(_asCheese, 0x4839, 0); + setGlobalVar(V_MOUSE_SUCKED_IN, 1); + } + if (_asProjector && _asProjector->getX() > 404 && _asProjector->getX() < 504) + sendMessage(_asProjector , 0x4839, 0); + } else if (sender == _ssButton) + sendMessage(_asBackDoor, 0x4808, 0); + break; + case 0x4826: + if (sender == _asProjector) { + if (sendMessage(_asProjector, 0x480C, _klaymen->getX() > _asProjector->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setMessageList2(0x004B6658); + } else + setMessageList2(0x004B65F0); + } + break; + case 0x482A: + _sprite1->setVisible(true); + if (_asProjector) + sendMessage(_asProjector, 0x482B, 0); + break; + case 0x482B: + _sprite1->setVisible(false); + if (_asProjector) + sendMessage(_asProjector, 0x482A, 0); + break; + } + return 0; +} + +// Scene1402 + +SsScene1402BridgePart::SsScene1402BridgePart(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority) + : StaticSprite(vm, fileHash, surfacePriority) { + + SetFilterY(&Sprite::defFilterY); + SetUpdateHandler(&StaticSprite::updatePosition); +} + +AsScene1402PuzzleBox::AsScene1402PuzzleBox(NeverhoodEngine *vm, Scene *parentScene, int status) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + createSurface(900, 347, 230); + + SetFilterY(&Sprite::defFilterY); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1402PuzzleBox::handleMessage); + _x = 279; + _y = 270; + if (status == 2) { + // Puzzle box after the puzzle was solved + startAnimation(0x20060259, 0, -1); + playSound(0, 0x419014AC); + loadSound(1, 0x61901C29); + NextState(&AsScene1402PuzzleBox::stMoveDownSolvedDone); + } else if (status == 1) { + // Puzzle box appears + startAnimation(0x210A0213, 0, -1); + playSound(0, 0x41809C6C); + NextState(&AsScene1402PuzzleBox::stMoveUpDone); + } else { + // Puzzle box is here + startAnimation(0x20060259, -1, -1); + loadSound(1, 0x61901C29); + _newStickFrameIndex = STICK_LAST_FRAME; + } +} + +uint32 AsScene1402PuzzleBox::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + playSound(1); + startAnimation(0x20060259, -1, -1); + _playBackwards = true; + NextState(&AsScene1402PuzzleBox::stMoveDownDone); + break; + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1402PuzzleBox::stMoveUpDone() { + sendMessage(_parentScene, 0x2000, 0); + stopAnimation(); + setVisible(false); +} + +void AsScene1402PuzzleBox::stMoveDownDone() { + sendMessage(_parentScene, 0x2001, 0); + stopAnimation(); + setVisible(false); +} + +void AsScene1402PuzzleBox::stMoveDownSolvedDone() { + sendMessage(_parentScene, 0x2003, 0); + stopAnimation(); +} + +Scene1402::Scene1402(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isShaking(false), _asPuzzleBox(NULL), _asProjector(NULL) { + + SetMessageHandler(&Scene1402::handleMessage); + + _vm->_screen->setYOffset(0); + + setBackground(0x231482F0); + setBackgroundY(-10); + setPalette(0x231482F0); + _palette->addPalette(0x91D3A391, 0, 64, 0); + insertScreenMouse(0x482F4239); + + _ssBridgePart1 = insertSprite<SsScene1402BridgePart>(0x15402D64, 1100); + _ssBridgePart2 = insertSprite<SsScene1402BridgePart>(0x10A02120, 1100); + _ssBridgePart3 = insertSprite<SsScene1402BridgePart>(0x60882BE0, 1100); + + if (getGlobalVar(V_MOUSE_PUZZLE_SOLVED)) + setRectList(0x004B0C48); + else + setRectList(0x004B0C98); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1402>(377, 391); + setMessageList(0x004B0B48); + if (!getGlobalVar(V_MOUSE_PUZZLE_SOLVED)) + _asPuzzleBox = insertSprite<AsScene1402PuzzleBox>(this, 0); + } else if (which == 1) { + // Klaymen entering from the left + insertKlaymen<KmScene1402>(42, 391); + setMessageList(0x004B0B50); + } else if (which == 2) { + // Klaymen returning from the puzzle box + insertKlaymen<KmScene1402>(377, 391); + setMessageList(0x004B0B60); + _klaymen->setDoDeltaX(1); + if (getGlobalVar(V_MOUSE_PUZZLE_SOLVED)) { + _asPuzzleBox = insertSprite<AsScene1402PuzzleBox>(this, 1); + clearRectList(); + showMouse(false); + startShaking(); + } else + _asPuzzleBox = insertSprite<AsScene1402PuzzleBox>(this, 0); + } else { + // Klaymen entering from the right + insertKlaymen<KmScene1402>(513, 391); + setMessageList(0x004B0B58); + if (!getGlobalVar(V_MOUSE_PUZZLE_SOLVED)) { + _asPuzzleBox = insertSprite<AsScene1402PuzzleBox>(this, 2); + startShaking(); + } + } + + if (_asPuzzleBox) + _asPuzzleBox->setClipRect(0, 0, 640, _ssBridgePart3->getDrawRect().y2()); + + if (getGlobalVar(V_PROJECTOR_LOCATION) == 1) { + _asProjector = insertSprite<AsCommonProjector>(this, _klaymen, (Sprite*)NULL); + addCollisionSprite(_asProjector); + if (getGlobalVar(V_PROJECTOR_SLOT) == 4) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + _klaymen->setX(_asProjector->getX() + 100); + _klaymen->updateBounds(); + setMessageList(0x004B0BD0); + } else if (getGlobalVar(V_PROJECTOR_SLOT) == 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + _klaymen->setX(_asProjector->getX() - 100); + _klaymen->updateBounds(); + setMessageList(0x004B0BD0); + } + _asProjector->setClipRect(_ssBridgePart1->getDrawRect().x, 0, _ssBridgePart2->getDrawRect().x, _ssBridgePart3->getDrawRect().y2()); + } + + _klaymen->setClipRect(_ssBridgePart1->getDrawRect().x, 0, _ssBridgePart2->getDrawRect().x2(), _ssBridgePart3->getDrawRect().y2()); + +} + +void Scene1402::upShaking() { + if (_isShaking) { + setBackgroundY(_vm->_rnd->getRandomNumber(10 - 1) - 10); + _vm->_screen->setYOffset(-10 - getBackgroundY()); + } else { + setBackgroundY(-10); + _vm->_screen->setYOffset(0); + SetUpdateHandler(&Scene::update); + } + Scene::update(); + if (_asPuzzleBox) + _asPuzzleBox->setClipRect(0, 0, 640, _ssBridgePart3->getDrawRect().y2()); + _klaymen->setClipRect(_ssBridgePart1->getDrawRect().x, 0, _ssBridgePart2->getDrawRect().x2(), _ssBridgePart3->getDrawRect().y2()); +} + +uint32 Scene1402::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x00F43389) { + if (getGlobalVar(V_MOUSE_PUZZLE_SOLVED)) + leaveScene(0); + else { + clearRectList(); + _klaymen->setVisible(false); + showMouse(false); + sendMessage(_asPuzzleBox, 0x2002, 0); + startShaking(); + } + } + break; + case 0x1019: + if (param.asInteger()) + leaveScene(0); + else + leaveScene(1); + break; + case 0x2000: + stopShaking(); + showMouse(true); + setRectList(0x004B0C48); + break; + case 0x2001: + stopShaking(); + leaveScene(0); + break; + case 0x2003: + stopShaking(); + break; + case 0x4826: + if (sender == _asProjector) { + if (sendMessage(_asProjector, 0x480C, _klaymen->getX() > _asProjector->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setMessageList2(0x004B0BB8); + } else + setMessageList2(0x004B0B68); + } + break; + } + return 0; +} + +void Scene1402::startShaking() { + _isShaking = true; + SetUpdateHandler(&Scene1402::upShaking); +} + +void Scene1402::stopShaking() { + _isShaking = false; +} + +// Scene1407 + +static const int16 kScene1407MouseFloorY[] = { + 106, 150, 191, 230, 267, 308, 350, 395 +}; + +static const struct { + int16 x; + int16 floorIndex; + int16 sectionIndex; + int16 nextHoleIndex; +} kScene1407MouseHoles[] = { + {125, 0, 0, 7}, + {452, 7, 21, 0}, + {337, 4, 11, 4}, + {286, 6, 17, 6}, + {348, 6, 17, 39}, + {536, 6, 18, 42}, + {111, 1, 3, 18}, + {203, 1, 3, 38}, + {270, 1, 3, 9}, + {197, 5, 14, 3}, + {252, 5, 14, 35}, + {297, 5, 14, 7}, + {359, 5, 14, 8}, + {422, 4, 12, 26}, + {467, 4, 12, 2}, + {539, 4, 12, 40}, + {111, 5, 13, 17}, + {211, 0, 1, 20}, + {258, 0, 1, 11}, + {322, 0, 1, 16}, + { 99, 6, 16, 31}, + {142, 6, 16, 27}, + {194, 6, 16, 12}, + {205, 2, 6, 45}, + {264, 2, 6, 10}, + { 98, 4, 10, 2}, + {152, 4, 10, 37}, + {199, 4, 10, 13}, + {258, 4, 10, 16}, + {100, 7, 19, 43}, + {168, 7, 19, 23}, + {123, 3, 8, 14}, + {181, 3, 8, 39}, + {230, 3, 8, 28}, + {292, 3, 8, 22}, + {358, 3, 8, 36}, + {505, 3, 9, 44}, + {400, 2, 7, 34}, + {454, 2, 7, 32}, + {532, 2, 7, 46}, + {484, 5, 15, 25}, + {529, 5, 15, 30}, + {251, 7, 20, 48}, + {303, 7, 20, 21}, + {360, 7, 20, 33}, + {503, 0, 2, 5}, + {459, 1, 4, 19}, + {530, 1, 4, 42}, + {111, 2, 5, 47}, + {442, 6, 18, 1} +}; + +static const struct { + int16 x1, x2; + int16 goodHoleIndex; +} kScene1407MouseSections[] = { + {100, 149, 0}, + {182, 351, 17}, + {430, 524, 45}, + { 89, 293, 7}, + {407, 555, 47}, + { 89, 132, 48}, + {178, 303, 23}, + {367, 551, 38}, + {105, 398, 31}, + {480, 537, 36}, + { 84, 275, 27}, + {318, 359, 2}, + {402, 560, 15}, + { 91, 132, 16}, + {179, 400, 10}, + {461, 552, 41}, + { 86, 218, 21}, + {267, 376, 4}, + {420, 560, 49}, + { 77, 188, 30}, + {237, 394, 44}, + {438, 515, 5} +}; + +AsScene1407Mouse::AsScene1407Mouse(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _currSectionIndex(0) { + + createSurface(100, 117, 45); + _x = 108; + _y = 106; + stIdleLookAtGoodHole(); + SetUpdateHandler(&AnimatedSprite::update); +} + +void AsScene1407Mouse::suWalkTo() { + int16 xdelta = _walkDestX - _x; + if (xdelta > _deltaX) + xdelta = _deltaX; + else if (xdelta < -_deltaX) + xdelta = -_deltaX; + _deltaX = 0; + if (_walkDestX == _x) + sendMessage(this, 0x1019, 0); + else { + _x += xdelta; + updateBounds(); + } +} + +void AsScene1407Mouse::upGoThroughHole() { + if (_countdown != 0 && (--_countdown == 0)) { + SetUpdateHandler(&AnimatedSprite::update); + gotoNextState(); + } + AnimatedSprite::update(); +} + +uint32 AsScene1407Mouse::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + { + int16 mouseX = param.asPoint().x; + int16 mouseY = param.asPoint().y; + int holeIndex; + for (holeIndex = 0; holeIndex < 50; holeIndex++) { + int16 holeX = kScene1407MouseHoles[holeIndex].x; + int16 holeY = kScene1407MouseFloorY[kScene1407MouseHoles[holeIndex].floorIndex]; + if (mouseX >= holeX - 14 && mouseX <= holeX + 14 && mouseY >= holeY - 36 && mouseY <= holeY) + break; + } + if (holeIndex < 50 && kScene1407MouseHoles[holeIndex].sectionIndex == _currSectionIndex) { + _nextHoleIndex = kScene1407MouseHoles[holeIndex].nextHoleIndex; + _walkDestX = kScene1407MouseHoles[holeIndex].x; + stWalkToHole(); + } else { + if (mouseX < kScene1407MouseSections[_currSectionIndex].x1) + _walkDestX = kScene1407MouseSections[_currSectionIndex].x1; + else if (mouseX > kScene1407MouseSections[_currSectionIndex].x2) + _walkDestX = kScene1407MouseSections[_currSectionIndex].x2; + else + _walkDestX = mouseX; + stWalkToDest(); + } + } + break; + case 0x1019: + gotoNextState(); + break; + case 0x2001: + { + // Reset the position + // Find the nearest hole and go through it, and exit at the first hole + int16 distance = 640; + int matchIndex = 50; + for (int index = 0; index < 50; index++) + if (kScene1407MouseHoles[index].sectionIndex == _currSectionIndex && + ABS(kScene1407MouseHoles[index].x - _x) < distance) { + matchIndex = index; + distance = ABS(kScene1407MouseHoles[index].x - _x); + } + if (matchIndex < 50) { + _nextHoleIndex = 0; + _walkDestX = kScene1407MouseHoles[matchIndex].x; + stWalkToHole(); + } + } + break; + } + return messageResult; +} + +void AsScene1407Mouse::stIdleLookAtGoodHole() { + setDoDeltaX(kScene1407MouseHoles[kScene1407MouseSections[_currSectionIndex].goodHoleIndex].x < _x ? 1 : 0); + startAnimation(0x72215194, 0, -1); + SetMessageHandler(&AsScene1407Mouse::handleMessage); + SetSpriteUpdate(NULL); +} + +void AsScene1407Mouse::stWalkToDest() { + if (_walkDestX != _x) { + setDoDeltaX(_walkDestX < _x ? 1 : 0); + startAnimation(0x22291510, 0, -1); + SetMessageHandler(&AsScene1407Mouse::handleMessage); + SetSpriteUpdate(&AsScene1407Mouse::suWalkTo); + NextState(&AsScene1407Mouse::stIdleLookAtGoodHole); + } +} + +void AsScene1407Mouse::stWalkToHole() { + setDoDeltaX(_walkDestX < _x ? 1 : 0); + startAnimation(0x22291510, 0, -1); + SetMessageHandler(&AsScene1407Mouse::handleMessage); + SetSpriteUpdate(&AsScene1407Mouse::suWalkTo); + NextState(&AsScene1407Mouse::stGoThroughHole); +} + +void AsScene1407Mouse::stGoThroughHole() { + startAnimation(0x72215194, 0, -1); + setVisible(false); + _countdown = 12; + SetUpdateHandler(&AsScene1407Mouse::upGoThroughHole); + SetMessageHandler(NULL); + SetSpriteUpdate(NULL); + NextState(&AsScene1407Mouse::stArriveAtHole); +} + +void AsScene1407Mouse::stArriveAtHole() { + _currSectionIndex = kScene1407MouseHoles[_nextHoleIndex].sectionIndex; + _x = kScene1407MouseHoles[_nextHoleIndex].x; + _y = kScene1407MouseFloorY[kScene1407MouseHoles[_nextHoleIndex].floorIndex]; + if (_nextHoleIndex == 1) { + sendMessage(_parentScene, 0x2000, 0); + _walkDestX = 512; + stWalkToDest(); + setVisible(true); + } else { + _walkDestX = _x + 14; + stWalkToDest(); + setVisible(true); + } +} + +Scene1407::Scene1407(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _puzzleSolvedCountdown(0), _resetButtonCountdown(0) { + + SetMessageHandler(&Scene1407::handleMessage); + SetUpdateHandler(&Scene1407::update); + + setBackground(0x00442225); + setPalette(0x00442225); + insertPuzzleMouse(0x4222100C, 20, 620); + + _asMouse = insertSprite<AsScene1407Mouse>(this); + _ssResetButton = insertStaticSprite(0x12006600, 100); + _ssResetButton->setVisible(false); + +} + +void Scene1407::update() { + Scene::update(); + if (_puzzleSolvedCountdown != 0 && (--_puzzleSolvedCountdown == 0)) + leaveScene(1); + else if (_resetButtonCountdown != 0 && (--_resetButtonCountdown == 0)) + _ssResetButton->setVisible(false); +} + +uint32 Scene1407::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (_puzzleSolvedCountdown == 0) { + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + // Exit scene + leaveScene(0); + } else if (param.asPoint().x >= 75 && param.asPoint().x <= 104 && + param.asPoint().y >= 62 && param.asPoint().y <= 90) { + // The reset button was clicked + sendMessage(_asMouse, 0x2001, 0); + _ssResetButton->setVisible(true); + playSound(0, 0x44045000); + _resetButtonCountdown = 12; + } else { + // Handle the mouse + sendMessage(_asMouse, messageNum, param); + } + } + break; + case 0x2000: + // The mouse got the cheese (nomnom) + setGlobalVar(V_MOUSE_PUZZLE_SOLVED, 1); + playSound(0, 0x68E25540); + showMouse(false); + _puzzleSolvedCountdown = 72; + break; + } + return 0; +} + +// Scene1403 + +Scene1403::Scene1403(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _asProjector(NULL), _isProjecting(false) { + + SetMessageHandler(&Scene1403::handleMessage); + + setRectList(0x004B1FF8); + setBackground(0x2110A234); + setPalette(0x2110A234); + insertScreenMouse(0x0A230219); + + _sprite1 = insertStaticSprite(0x01102A33, 100); + _sprite1->setVisible(false); + _sprite2 = insertStaticSprite(0x04442520, 995); + _sprite3 = insertStaticSprite(0x08742271, 995); + _asTape1 = insertSprite<AsScene1201Tape>(this, 12, 1100, 201, 468, 0x9148A011); + addCollisionSprite(_asTape1); + _asTape1->setRepl(64, 0); + _asTape2 = insertSprite<AsScene1201Tape>(this, 16, 1100, 498, 468, 0x9048A093); + addCollisionSprite(_asTape2); + _asTape2->setRepl(64, 0); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1403>(380, 463); + setMessageList(0x004B1F18); + } else { + // Klaymen entering from the right + insertKlaymen<KmScene1403>(640, 463); + setMessageList(0x004B1F20); + } + _klaymen->setRepl(64, 0); + + if (getGlobalVar(V_PROJECTOR_LOCATION) == 0) { + _asProjector = insertSprite<AsCommonProjector>(this, _klaymen, (Sprite*)NULL); + addCollisionSprite(_asProjector); + if (getGlobalVar(V_PROJECTOR_SLOT) == 4) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + _klaymen->setX(_asProjector->getX() + 100); + _klaymen->updateBounds(); + setMessageList(0x004B1F70); + } + _asProjector->setClipRect(0, 0, 640, _sprite2->getDrawRect().y2()); + _asProjector->setRepl(64, 0); + } + +} + +uint32 Scene1403::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x88C11390) { + setRectList(0x004B2008); + _isProjecting = true; + } else if (param.asInteger() == 0x08821382) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setRectList(0x004B1FF8); + _isProjecting = false; + } + break; + case 0x1019: + leaveScene(0); + break; + case 0x1022: + if (sender == _asProjector) { + if (param.asInteger() >= 1000) + setSurfacePriority(_sprite3->getSurface(), 1100); + else + setSurfacePriority(_sprite3->getSurface(), 995); + } + break; + case 0x4807: + _sprite1->setVisible(false); + break; + case 0x480F: + _sprite1->setVisible(true); + break; + case 0x4826: + if (sender == _asProjector) { + if (_isProjecting) + setMessageList2(0x004B1FA8); + else if (param.asInteger() == 1) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setMessageList2(0x004B1F88); + } else if (sendMessage(_asProjector, 0x480C, _klaymen->getX() > _asProjector->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setMessageList2(0x004B1F58); + } else + setMessageList2(0x004B1F28); + } else if (sender == _asTape1 || sender == _asTape2) { + if (_isProjecting) + setMessageList2(0x004B1FA8); + else if (_messageListStatus != 2) { + sendEntityMessage(_klaymen, 0x1014, sender); + setMessageList2(0x004B1FB8); + } + } + break; + } + return 0; +} + +// Scene1404 + +Scene1404::Scene1404(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _asProjector(NULL), _asKey(NULL) { + + if (getGlobalVar(V_HAS_FINAL_KEY) && getGlobalVar(V_KEY3_LOCATION) == 0) + setGlobalVar(V_KEY3_LOCATION, 5); + + SetMessageHandler(&Scene1404::handleMessage); + + setRectList(0x004B8D80); + setBackground(0xAC0B006F); + setPalette(0xAC0B006F); + _palette->addPalette(0x00801510, 0, 65, 0); + insertScreenMouse(0xB006BAC8); + + if (getGlobalVar(V_KEY3_LOCATION) == 5) { + _asKey = insertSprite<AsCommonKey>(this, 2, 1100, 267, 411); + addCollisionSprite(_asKey); + } + + _sprite1 = insertStaticSprite(0x1900A1F8, 1100); + _asTape = insertSprite<AsScene1201Tape>(this, 14, 1100, 281, 411, 0x9148A011); + addCollisionSprite(_asTape); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1404>(376, 406); + setMessageList(0x004B8C28); + } else if (which == 1) { + // Klaymen returning from the tiles puzzle + insertKlaymen<KmScene1404>(376, 406); + setMessageList(0x004B8C30); + } else if (which == 2) { + // Klaymen returning from the diskplayer + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene1404>(347, 406); + _klaymen->setDoDeltaX(1); + } else { + insertKlaymen<KmScene1404>(187, 406); + } + setMessageList(0x004B8D28); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene1404>(30, 406); + setMessageList(0x004B8C38); + } + + if (getGlobalVar(V_PROJECTOR_LOCATION) == 3) { + _asProjector = insertSprite<AsCommonProjector>(this, _klaymen, (Sprite*)NULL); + addCollisionSprite(_asProjector); + if (getGlobalVar(V_PROJECTOR_SLOT) == 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + _klaymen->setX(_asProjector->getX() - 100); + _klaymen->updateBounds(); + setMessageList(0x004B8CB8); + } + _asProjector->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + } + + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + +} + +Scene1404::~Scene1404() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene1404::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x410650C2) { + if (_asProjector && _asProjector->getX() == 220) + setMessageList(0x004B8C40); + else + setMessageList(0x004B8CE8); + } + break; + case 0x1019: + leaveScene(0); + break; + case 0x4826: + if (sender == _asProjector) { + if (sendMessage(_asProjector, 0x480C, _klaymen->getX() > _asProjector->getX() ? 1 : 0) != 0) { + sendEntityMessage(_klaymen, 0x1014, _asProjector); + setMessageList2(0x004B8CA0); + } else + setMessageList2(0x004B8C40); + } else if (sender == _asTape && _messageListStatus != 2) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B8CD0); + } else if (sender == _asKey && _messageListStatus != 2) { + sendEntityMessage(_klaymen, 0x1014, _asKey); + setMessageList(0x004B8D18); + } + break; + } + return 0; +} + +// Scene1405 + +static const NPoint kAsScene1405TileItemPositions[] = { + {100, 80}, {162, 78}, {222, 76}, {292, 76}, + {356, 82}, {422, 84}, {488, 86}, {550, 90}, + {102, 134}, {164, 132}, {224, 136}, {294, 136}, + {360, 136}, {422, 138}, {484, 144}, {548, 146}, + { 98, 196}, {160, 200}, {228, 200}, {294, 202}, + {360, 198}, {424, 200}, {482, 202}, {548, 206}, + { 98, 260}, {160, 264}, {226, 260}, {296, 262}, + {358, 260}, {424, 262}, {486, 264}, {550, 266}, + { 94, 322}, {160, 316}, {226, 316}, {296, 320}, + {358, 322}, {422, 324}, {488, 322}, {550, 322}, + { 98, 380}, {160, 376}, {226, 376}, {294, 378}, + {356, 380}, {420, 380}, {490, 378}, {552, 376} +}; + +AsScene1405Tile::AsScene1405Tile(NeverhoodEngine *vm, Scene1405 *parentScene, uint32 tileIndex) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _tileIndex(tileIndex), _countdown(0), _isShowing(false) { + + loadSound(0, 0x05308101); + setSoundPan(0, (tileIndex % 8 * 4 + 4) * 25 / 8); + _x = kAsScene1405TileItemPositions[_tileIndex].x; + _y = kAsScene1405TileItemPositions[_tileIndex].y; + createSurface1(0x844B805C, 1100); + setVisible(false); + if (getSubVar(VA_IS_TILE_MATCH, _tileIndex)) + _countdown = _vm->_rnd->getRandomNumber(36 - 1) + 1; + startAnimation(0x844B805C, getSubVar(VA_TILE_SYMBOLS, _tileIndex), -1); + _newStickFrameIndex = (int16)getSubVar(VA_TILE_SYMBOLS, _tileIndex); + SetUpdateHandler(&AsScene1405Tile::update); + SetMessageHandler(&AsScene1405Tile::handleMessage); +} + +void AsScene1405Tile::update() { + updateAnim(); + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) + show(); +} + +uint32 AsScene1405Tile::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (getSubVar(VA_IS_TILE_MATCH, _tileIndex) == 0 && _parentScene->getCountdown() == 0) { + show(); + sendMessage(_parentScene, 0x2000, _tileIndex); + } + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene1405Tile::show() { + if (!_isShowing) { + _isShowing = true; + playSound(0); + setVisible(true); + } +} + +void AsScene1405Tile::hide() { + if (_isShowing) { + _isShowing = false; + playSound(0); + setVisible(false); + } +} + +Scene1405::Scene1405(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _selectFirstTile(true), _tilesLeft(48), _countdown(0) { + + _vm->gameModule()->initMemoryPuzzle(); + + SetUpdateHandler(&Scene1405::update); + SetMessageHandler(&Scene1405::handleMessage); + + setBackground(0x0C0C007D); + setPalette(0x0C0C007D); + insertPuzzleMouse(0xC00790C8, 20, 620); + + for (uint32 tileIndex = 0; tileIndex < 48; tileIndex++) { + _tiles[tileIndex] = insertSprite<AsScene1405Tile>(this, tileIndex); + addCollisionSprite(_tiles[tileIndex]); + if (getSubVar(VA_IS_TILE_MATCH, tileIndex)) + _tilesLeft--; + } + + loadSound(0, 0x68E25540); +} + +void Scene1405::update() { + Scene::update(); + if (_countdown != 0 && (--_countdown == 0)) { + _tilesLeft = 48; + _tiles[_firstTileIndex]->hide(); + _tiles[_secondTileIndex]->hide(); + for (uint32 i = 0; i < 48; i++) { + if (getSubVar(VA_IS_TILE_MATCH, i)) { + _tiles[i]->hide(); + setSubVar(VA_IS_TILE_MATCH, i, 0); + } + } + } +} + +uint32 Scene1405::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: + if (_selectFirstTile) { + _firstTileIndex = param.asInteger(); + _selectFirstTile = false; + } else { + _secondTileIndex = param.asInteger(); + if (_firstTileIndex != _secondTileIndex) { + _selectFirstTile = true; + if (getSubVar(VA_TILE_SYMBOLS, _secondTileIndex) == getSubVar(VA_TILE_SYMBOLS, _firstTileIndex)) { + setSubVar(VA_IS_TILE_MATCH, _firstTileIndex, 1); + setSubVar(VA_IS_TILE_MATCH, _secondTileIndex, 1); + _tilesLeft -= 2; + if (_tilesLeft == 0) + playSound(0); + } else + _countdown = 10; + } + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1400.h b/engines/neverhood/modules/module1400.h new file mode 100644 index 0000000000..9a592c2952 --- /dev/null +++ b/engines/neverhood/modules/module1400.h @@ -0,0 +1,281 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1400_H +#define NEVERHOOD_MODULES_MODULE1400_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module1200.h" + +namespace Neverhood { + +class Module1400 : public Module { +public: + Module1400(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1400(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1401 + +class AsScene1401Pipe : public AnimatedSprite { +public: + AsScene1401Pipe(NeverhoodEngine *vm); + virtual ~AsScene1401Pipe(); +protected: + int _countdown1; + int _countdown2; + void update(); + void upSuckInProjector(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmSuckInProjector(int messageNum, const MessageParam ¶m, Entity *sender); + void stStartSucking(); + void stDoneSucking(); + void stSuckInProjector(); +}; + +class AsScene1401Mouse : public AnimatedSprite { +public: + AsScene1401Mouse(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suSuckedIn(); + void stSuckedIn(); +}; + +class AsScene1401Cheese : public AnimatedSprite { +public: + AsScene1401Cheese(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suSuckedIn(); + void stSuckedIn(); +}; + +class AsScene1401BackDoor : public AnimatedSprite { +public: + AsScene1401BackDoor(NeverhoodEngine *vm, Sprite *klaymen, bool isOpen); +protected: + Sprite *_klaymen; + int _countdown; + bool _isOpen; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stOpenDoor(); + void stCloseDoor(); + void stCloseDoorDone(); +}; + +struct AsCommonProjectorItem { + NPoint point; + int8 maxSlotCount; + int8 lockSlotIndex; + int8 index1; + int8 leftBorderLeaves; + int8 rightBorderLeaves; +}; + +class AsCommonProjector : public AnimatedSprite { +public: + AsCommonProjector(NeverhoodEngine *vm, Scene *parentScene, Sprite *klaymen, Sprite *asPipe); + virtual ~AsCommonProjector(); +protected: + Scene *_parentScene; + Sprite *_klaymen; + Sprite *_asPipe; + const AsCommonProjectorItem *_asProjectorItem; + int16 _beforeMoveX; + bool _lockedInSlot; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmLockedInSlot(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoving(); + void moveProjector(); + void stSuckedIn(); + void stIdle(); + void stMoving(); + void stStartLockedInSlot(); + void stStayLockedInSlot(); + void stStartProjecting(); + void stLockedInSlot(); + void stStopProjecting(); + void stTurnToFront(); + void stStartSuckedIn(); +}; + +class Scene1401 : public Scene { +public: + Scene1401(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + bool _projectorBorderFlag; + Sprite *_ssFloorButton; + AsCommonProjector *_asProjector; + Sprite *_asPipe; + Sprite *_asMouse; + Sprite *_asCheese; + Sprite *_asBackDoor; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_ssButton; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1402 + +class SsScene1402BridgePart : public StaticSprite { +public: + SsScene1402BridgePart(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority); +}; + +class AsScene1402PuzzleBox : public AnimatedSprite { +public: + AsScene1402PuzzleBox(NeverhoodEngine *vm, Scene *parentScene, int status); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stMoveUpDone(); + void stMoveDownDone(); + void stMoveDownSolvedDone(); +}; + +class Scene1402 : public Scene { +public: + Scene1402(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_ssBridgePart1; + Sprite *_ssBridgePart2; + Sprite *_ssBridgePart3; + Sprite *_asPuzzleBox; + AsCommonProjector *_asProjector; + bool _isShaking; + void upShaking(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void startShaking(); + void stopShaking(); +}; + +// Scene1407 + +class AsScene1407Mouse : public AnimatedSprite { +public: + AsScene1407Mouse(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + int16 _walkDestX; + int16 _currSectionIndex; + int16 _nextHoleIndex; + int _countdown; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suWalkTo(); + void upGoThroughHole(); + void stIdleLookAtGoodHole(); + void stWalkToDest(); + void stWalkToHole(); + void stGoThroughHole(); + void stArriveAtHole(); +}; + +class Scene1407 : public Scene { +public: + Scene1407(NeverhoodEngine *vm, Module *parentModule); +protected: + Sprite *_asMouse; + Sprite *_ssResetButton; + int _puzzleSolvedCountdown; + int _resetButtonCountdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1403 + +class Scene1403 : public Scene { +public: + Scene1403(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + AsScene1201Tape *_asTape1; + AsScene1201Tape *_asTape2; + AsCommonProjector *_asProjector; + bool _isProjecting; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1404 + +class Scene1404 : public Scene { +public: + Scene1404(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene1404(); +protected: + Sprite *_sprite1; + Sprite *_asTape; + AsCommonProjector *_asProjector; + Sprite *_asKey; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene1405 + +class Scene1405; + +class AsScene1405Tile : public AnimatedSprite { +public: + AsScene1405Tile(NeverhoodEngine *vm, Scene1405 *parentScene, uint32 tileIndex); + void show(); + void hide(); +protected: + Scene1405 *_parentScene; + bool _isShowing; + uint32 _tileIndex; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1405 : public Scene { +public: + Scene1405(NeverhoodEngine *vm, Module *parentModule); + int getCountdown() const { return _countdown; } +protected: + bool _selectFirstTile; + int _firstTileIndex; + int _secondTileIndex; + int _tilesLeft; + int _countdown; + AsScene1405Tile *_tiles[48]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1400_H */ diff --git a/engines/neverhood/modules/module1500.cpp b/engines/neverhood/modules/module1500.cpp new file mode 100644 index 0000000000..2a9597b1fd --- /dev/null +++ b/engines/neverhood/modules/module1500.cpp @@ -0,0 +1,134 @@ +/* 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/module1500.h" + +namespace Neverhood { + +Module1500::Module1500(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else + createScene(3, -1); + +} + +void Module1500::createScene(int sceneNum, int which) { + debug("Module1500::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _childObject = new Scene1501(_vm, this, 0x8420221D, 0xA61024C4, 150, 48); + break; + case 1: + _vm->gameState().sceneNum = 1; + _childObject = new Scene1501(_vm, this, 0x30050A0A, 0x58B45E58, 110, 48); + break; + case 2: + _vm->gameState().sceneNum = 2; + sendMessage(_parentModule, 0x0800, 0); + createSmackerScene(0x001A0005, true, true, true); + break; + case 3: + _vm->gameState().sceneNum = 3; + _childObject = new Scene1501(_vm, this, 0x0CA04202, 0, 110, 48); + break; + } + SetUpdateHandler(&Module1500::updateScene); + _childObject->handleUpdate(); +} + +void Module1500::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + createScene(1, -1); + break; + case 1: + createScene(2, -1); + break; + case 3: + createScene(0, -1); + break; + default: + leaveModule(0); + break; + } + } +} + +// Scene1501 + +Scene1501::Scene1501(NeverhoodEngine *vm, Module *parentModule, uint32 backgroundFileHash, uint32 soundFileHash, int countdown2, int countdown3) + : Scene(vm, parentModule), _countdown3(countdown3), _countdown2(countdown2), _countdown1(0), _skip(false) { + + SetUpdateHandler(&Scene1501::update); + SetMessageHandler(&Scene1501::handleMessage); + + setBackground(backgroundFileHash); + setPalette(); + addEntity(_palette); + _palette->addBasePalette(backgroundFileHash, 0, 256, 0); + _palette->startFadeToPalette(12); + + if (soundFileHash != 0) + playSound(0, soundFileHash); + +} + +void Scene1501::update() { + Scene::update(); + if (_countdown1 != 0) { + _countdown1--; + if (_countdown1 == 0) { + _vm->_screen->clear(); + leaveScene(0); + } + } else if ((_countdown2 != 0 && (--_countdown2 == 0)) || (_countdown2 == 0 && !isSoundPlaying(0))) { + _countdown1 = 12; + _palette->startFadeToBlack(11); + } + + if (_countdown3 != 0) + _countdown3--; + + if (_countdown3 == 0 && _skip && _countdown1 == 0) { + _countdown1 = 12; + _palette->startFadeToBlack(11); + } + +} + +uint32 Scene1501::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0009: + _skip = true; + break; + } + return messageResult; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1500.h b/engines/neverhood/modules/module1500.h new file mode 100644 index 0000000000..f244948918 --- /dev/null +++ b/engines/neverhood/modules/module1500.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +// TODO: I couldn't come up with a better name than 'Module' so far + +#ifndef NEVERHOOD_MODULES_MODULE1500_H +#define NEVERHOOD_MODULES_MODULE1500_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/smackerscene.h" + +namespace Neverhood { + +class Module1500 : public Module { +public: + Module1500(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class Scene1501 : public Scene { +public: + Scene1501(NeverhoodEngine *vm, Module *parentModule, uint32 backgroundFileHash, uint32 soundFileHash, int countdown2, int countdown3); +protected: + int _countdown1; + int _countdown2; + int _countdown3; + bool _skip; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1500_H */ diff --git a/engines/neverhood/modules/module1600.cpp b/engines/neverhood/modules/module1600.cpp new file mode 100644 index 0000000000..f7e3c37d84 --- /dev/null +++ b/engines/neverhood/modules/module1600.cpp @@ -0,0 +1,1412 @@ +/* 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/module1600.h" +#include "neverhood/gamemodule.h" +#include "neverhood/modules/module1200.h" +#include "neverhood/modules/module2200.h" + +namespace Neverhood { + +static const uint32 kModule1600SoundList[] = { + 0x90805C50, 0x90804450, 0xB4005E60, + 0x91835066, 0x90E14440, 0 +}; + +Module1600::Module1600(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 1) + createScene(4, 1); + else if (which == 2) + createScene(5, 0); + else if (which == 3) + createScene(6, 1); + else if (which == 4) + createScene(1, 0); + else + createScene(0, 0); + + _vm->_soundMan->addSoundList(0x1A008D8, kModule1600SoundList); + _vm->_soundMan->setSoundListParams(kModule1600SoundList, true, 50, 600, 5, 150); + _vm->_soundMan->playTwoSounds(0x1A008D8, 0x41861371, 0x43A2507F, 0); + +} + +Module1600::~Module1600() { + _vm->_soundMan->deleteGroup(0x1A008D8); +} + +void Module1600::createScene(int sceneNum, int which) { + debug("Module1600::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + createNavigationScene(0x004B39D0, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + createNavigationScene(0x004B3A30, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + createNavigationScene(0x004B3A60, which); + break; + case 3: + _vm->gameState().sceneNum = 3; + createNavigationScene(0x004B3A90, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + createNavigationScene(0x004B3B20, which); + break; + case 5: + _vm->gameState().sceneNum = 5; + createNavigationScene(0x004B3B50, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + createNavigationScene(0x004B3B80, which); + break; + case 7: + _vm->gameState().sceneNum = 7; + _childObject = new Scene1608(_vm, this, which); + break; + case 8: + _vm->gameState().sceneNum = 8; + _childObject = new Scene1609(_vm, this); + break; + case 1001: + _vm->gameState().sceneNum = 1; + if (getGlobalVar(V_TALK_COUNTING_INDEX) == 1) + createSmackerScene(0x80050200, true, true, false); + else if (getGlobalVar(V_TALK_COUNTING_INDEX) == 2) + createSmackerScene(0x80090200, true, true, false); + else + createSmackerScene(0x80000200, true, true, false); + if (getGlobalVar(V_TALK_COUNTING_INDEX) >= 2) + setGlobalVar(V_TALK_COUNTING_INDEX, 0); + else + incGlobalVar(V_TALK_COUNTING_INDEX, +1); + break; + } + SetUpdateHandler(&Module1600::updateScene); + _childObject->handleUpdate(); +} + +void Module1600::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 0) + createScene(2, 0); + else if (_moduleResult == 1) + createScene(1, 0); + else if (_moduleResult == 2) + leaveModule(4); + break; + case 1: + if (_moduleResult == 0) + createScene(1001, -1); + else if (_moduleResult == 1) + createScene(0, 3); + break; + case 2: + if (_moduleResult == 0) + createScene(3, 0); + else if (_moduleResult == 1) + createScene(0, 2); + break; + case 3: + if (_moduleResult == 0) + createScene(5, 0); + else if (_moduleResult == 2) + createScene(6, 0); + else if (_moduleResult == 3) + createScene(2, 1); + else if (_moduleResult == 4) + createScene(4, 0); + break; + case 4: + if (_moduleResult == 0) + leaveModule(1); + else if (_moduleResult == 1) + createScene(3, 1); + break; + case 5: + if (_moduleResult == 0) + leaveModule(2); + else if (_moduleResult == 1) + createScene(3, 3); + break; + case 6: + if (_moduleResult == 0) + createScene(8, -1); + else if (_moduleResult == 1) + createScene(3, 5); + break; + case 7: + createScene(6, 1); + break; + case 8: + if (_moduleResult == 0) + createScene(6, 0); + else + createScene(7, 0); + break; + case 1001: + createScene(1, 0); + break; + } + } +} + +AsCommonCar::AsCommonCar(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y) + : AnimatedSprite(vm, 1000), _parentScene(parentScene) { + + createSurface(200, 556, 328); + _x = x; + _y = y; + + _inMainArea = false; + _exitDirection = 0; + _currPointIndex = 0; + _hasAgainDestPoint = false; + _stepError = 0; + _hasAgainDestPointIndex = false; + _steps = 0; + _isBraking = false; + _yMoveTotalSteps = 0; + _isBusy = false; + _isIdle = false; + _isMoving = true; + _rectFlag = false; + _newDeltaXType = -1; + _soundCounter = 0; + _pathPoints = NULL; + _currMoveDirection = 0; + + startAnimation(0xD4220027, 0, -1); + setDoDeltaX(getGlobalVar(V_CAR_DELTA_X)); + + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::handleMessage); + SetSpriteUpdate(NULL); +} + +AsCommonCar::~AsCommonCar() { + if (_finalizeStateCb == AnimationCallback(&AsCommonCar::evTurnCarDone)) + setGlobalVar(V_CAR_DELTA_X, !getGlobalVar(V_CAR_DELTA_X)); +} + +void AsCommonCar::setPathPoints(NPointArray *pathPoints) { + _pathPoints = pathPoints; +} + +void AsCommonCar::update() { + if (_newDeltaXType >= 0) { + setDoDeltaX(_newDeltaXType); + _newDeltaXType = -1; + } + AnimatedSprite::update(); + if (_hasAgainDestPoint && _yMoveTotalSteps == 0 && !_isBusy) { + _hasAgainDestPoint = false; + _hasAgainDestPointIndex = false; + sendPointMessage(this, 0x2004, _againDestPoint); + } else if (_hasAgainDestPointIndex && _yMoveTotalSteps == 0 && !_isBusy) { + _hasAgainDestPointIndex = false; + sendMessage(this, 0x2003, _againDestPointIndex); + } + updateMovement(); + updateSound(); +} + +void AsCommonCar::upIdle() { + update(); + if (++_idleCounter >= _idleCounterMax) + stIdleBlink(); + updateSound(); +} + +uint32 AsCommonCar::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1019: + SetSpriteUpdate(NULL); + break; + case 0x2002: + // Set the current position without moving + _currPointIndex = param.asInteger(); + _stepError = 0; + _x = pathPoint(_currPointIndex).x; + _y = pathPoint(_currPointIndex).y; + break; + case 0x2003: + // Move to a point by its index + { + int newPointIndex = param.asInteger(); + if (_yMoveTotalSteps <= 0 && !_isBusy) { + _destX = pathPoint(newPointIndex).x; + _destY = pathPoint(newPointIndex).y; + if (_currPointIndex < newPointIndex) { + moveToNextPoint(); + } else if (_currPointIndex == newPointIndex && _stepError == 0) { + if (_currPointIndex == 0) { + _yMoveTotalSteps = 0; + sendMessage(_parentScene, 0x2005, 0); + } else if (_currPointIndex == (int)_pathPoints->size()) { + _yMoveTotalSteps = 0; + sendMessage(_parentScene, 0x2006, 0); + } + } else { + moveToPrevPoint(); + } + } else { + _hasAgainDestPointIndex = true; + _againDestPointIndex = newPointIndex; + } + } + break; + case 0x2004: + // Move to the point closest to the parameter point + { + int minMatchIndex = -1; + int minMatchDistance, distance; + NPoint pt = param.asPoint(); + if (_yMoveTotalSteps <= 0 && !_isBusy) { + // Check if we're already exiting (or something) + if ((pt.x <= 20 && _exitDirection == 1) || + (pt.x >= 620 && _exitDirection == 3) || + (pt.y <= 20 && _exitDirection == 2) || + (pt.y >= 460 && _exitDirection == 4)) + break; + _destX = pt.x; + _destY = pt.y; + minMatchDistance = calcDistance(_destX, _destY, _x, _y) + 1; + for (int i = _currPointIndex + 1; i < (int)_pathPoints->size(); i++) { + distance = calcDistance(_destX, _destY, pathPoint(i).x, pathPoint(i).y); + if (distance >= minMatchDistance) + break; + minMatchDistance = distance; + minMatchIndex = i; + } + for (int i = _currPointIndex; i >= 0; i--) { + distance = calcDistance(_destX, _destY, pathPoint(i).x, pathPoint(i).y); + if (distance >= minMatchDistance) + break; + minMatchDistance = distance; + minMatchIndex = i; + } + if (minMatchIndex == -1) { + if (_currPointIndex == 0) + moveToPrevPoint(); + else + SetSpriteUpdate(NULL); + } else { + if (minMatchIndex > _currPointIndex) + moveToNextPoint(); + else + moveToPrevPoint(); + } + } else { + _hasAgainDestPoint = true; + _againDestPoint = pt; + } + } + break; + case 0x2007: + _yMoveTotalSteps = param.asInteger(); + _steps = 0; + _isBraking = false; + _lastDistance = 640; + SetSpriteUpdate(&AsCommonCar::suMoveToPrevPoint); + break; + case 0x2008: + _yMoveTotalSteps = param.asInteger(); + _steps = 0; + _isBraking = false; + _lastDistance = 640; + SetSpriteUpdate(&AsCommonCar::suMoveToNextPoint); + break; + case 0x2009: + stEnterCar(); + break; + case 0x200A: + stLeaveCar(); + break; + case 0x200E: + stTurnCar(); + break; + case 0x200F: + stCarAtHome(); + _newDeltaXType = param.asInteger(); + break; + } + return messageResult; +} + +uint32 AsCommonCar::hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = AsCommonCar::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (_isBusy && param.asInteger() == 0x025424A2) + gotoNextState(); + break; + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +uint32 AsCommonCar::hmLeaveCar(int messageNum, const MessageParam ¶m, Entity *sender) { + switch (messageNum) { + case 0x2009: + stEnterCar(); + break; + case 0x3002: + sendMessage(_parentScene, 0x200A, 0); + SetMessageHandler(&AsCommonCar::handleMessage); + break; + } + return 0; +} + +void AsCommonCar::stCarAtHome() { + bool doDeltaX = _doDeltaX; + SetSpriteUpdate(NULL); + _hasAgainDestPoint = false; + _hasAgainDestPointIndex = false; + _isBraking = false; + _isBusy = false; + _isIdle = false; + _isMoving = false; + _rectFlag = false; + NextState(&AsCommonCar::stLeanForwardIdle); + startAnimation(0x35698F78, 0, -1); + setDoDeltaX(doDeltaX ? 1 : 0); + _currMoveDirection = 0; + _newMoveDirection = 0; + _steps = 0; + _idleCounter = 0; + _idleCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24; + SetUpdateHandler(&AsCommonCar::upIdle); + SetMessageHandler(&AsCommonCar::handleMessage); + FinalizeState(&AsCommonCar::evIdleDone); +} + +void AsCommonCar::updateTurnMovement() { + if (_turnMoveStatus == 1) { + _lastDistance = 640; + _isIdle = false; + _isBraking = false; + SetSpriteUpdate(&AsCommonCar::suMoveToNextPoint); + } else if (_turnMoveStatus == 2) { + _lastDistance = 640; + _isIdle = false; + _isBraking = false; + SetSpriteUpdate(&AsCommonCar::suMoveToPrevPoint); + } +} + +void AsCommonCar::updateMovement() { + if (_isBraking && !_isIdle && !_isBusy) { + gotoNextState(); + _isMoving = false; + _isIdle = true; + startAnimation(0x192ADD30, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + NextState(&AsCommonCar::stLeanForwardIdle); + } else if (!_isBraking && _steps && _isIdle) { + gotoNextState(); + _isIdle = false; + startAnimation(0x9966B138, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + NextState(&AsCommonCar::stUpdateMoveDirection); + } else if (_newMoveDirection != _currMoveDirection && _isMoving && !_isBusy) { + gotoNextState(); + _currMoveDirection = _newMoveDirection; + stUpdateMoveDirection(); + } +} + +void AsCommonCar::stEnterCar() { + startAnimation(0xA86A9538, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + NextState(&AsCommonCar::stLeanForwardIdle); +} + +void AsCommonCar::stLeaveCar() { + startAnimation(0xA86A9538, -1, -1); + _playBackwards = true; + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmLeaveCar); +} + +void AsCommonCar::stLeanForwardIdle() { + startAnimation(0x35698F78, 0, -1); + _currMoveDirection = 0; + _newMoveDirection = 0; + _steps = 0; + _idleCounter = 0; + _idleCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24; + SetUpdateHandler(&AsCommonCar::upIdle); + SetMessageHandler(&AsCommonCar::handleMessage); + FinalizeState(&AsCommonCar::evIdleDone); +} + +void AsCommonCar::evIdleDone() { + SetUpdateHandler(&AsCommonCar::update); +} + +void AsCommonCar::stIdleBlink() { + startAnimation(0xB579A77C, 0, -1); + _idleCounter = 0; + _idleCounterMax = _vm->_rnd->getRandomNumber(64 - 1) + 24; + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + NextState(&AsCommonCar::stLeanForwardIdle); +} + +void AsCommonCar::stUpdateMoveDirection() { + _isMoving = true; + if (_currMoveDirection == 1) + startAnimation(0xD4AA03A4, 0, -1); + else if (_currMoveDirection == 3) + startAnimation(0xD00A1364, 0, -1); + else if ((_currMoveDirection == 2 && _doDeltaX) || (_currMoveDirection == 4 && !_doDeltaX)) + stTurnCar(); + else + startAnimation(0xD4220027, 0, -1); + setGlobalVar(V_CAR_DELTA_X, _doDeltaX ? 1 : 0); +} + +void AsCommonCar::moveToNextPoint() { + if (_currPointIndex >= (int)_pathPoints->size() - 1) { + _yMoveTotalSteps = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2006, 0); + } else { + NPoint nextPt = pathPoint(_currPointIndex + 1); + NPoint currPt = pathPoint(_currPointIndex); + if (ABS(nextPt.y - currPt.y) <= ABS(nextPt.x - currPt.x) && + ((_currMoveDirection == 2 && nextPt.x < currPt.x) || + (_currMoveDirection == 4 && nextPt.x >= currPt.x))) { + if (_currMoveDirection == 2) + _currMoveDirection = 4; + else if (_currMoveDirection == 4) + _currMoveDirection = 2; + if (_isIdle) + stTurnCarMoveToNextPoint(); + else + stBrakeMoveToNextPoint(); + } else { + if (_steps == 0) { + gotoNextState(); + _isIdle = false; + startAnimation(0x9966B138, 0, -1); + SetMessageHandler(&AsCommonCar::hmAnimation); + SetUpdateHandler(&AsCommonCar::update); + NextState(&AsCommonCar::stUpdateMoveDirection); + } + _isBraking = false; + SetSpriteUpdate(&AsCommonCar::suMoveToNextPoint); + _lastDistance = 640; + } + } +} + +void AsCommonCar::stBrakeMoveToNextPoint() { + gotoNextState(); + _isBusy = true; + _isBraking = true; + startAnimation(0x192ADD30, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + NextState(&AsCommonCar::stTurnCarMoveToNextPoint); +} + +void AsCommonCar::stTurnCar() { + // Turn to left/right #1 + gotoNextState(); + _isBusy = true; + startAnimation(0xF46A0324, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + FinalizeState(&AsCommonCar::evTurnCarDone); + _turnMoveStatus = 0; + updateTurnMovement(); +} + +void AsCommonCar::stTurnCarMoveToNextPoint() { + // Turn to left/right #2 + gotoNextState(); + _isBusy = true; + startAnimation(0xF46A0324, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + FinalizeState(&AsCommonCar::evTurnCarDone); + _turnMoveStatus = 1; + updateTurnMovement(); +} + +void AsCommonCar::stTurnCarMoveToPrevPoint() { + // Turn to left/right #3 + FinalizeState(NULL); + _isBusy = true; + startAnimation(0xF46A0324, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + FinalizeState(&AsCommonCar::evTurnCarDone); + _turnMoveStatus = 2; + updateTurnMovement(); +} + +void AsCommonCar::moveToPrevPoint() { + if (_currPointIndex == 0 && _stepError == 0) { + _yMoveTotalSteps = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2005, 0); + } else { + NPoint prevPt; + NPoint currPt; + if (_stepError == 0) { + prevPt = pathPoint(_currPointIndex - 1); + currPt = pathPoint(_currPointIndex); + } else { + prevPt = pathPoint(_currPointIndex); + currPt = pathPoint(_currPointIndex + 1); + } + if (ABS(prevPt.y - currPt.y) <= ABS(prevPt.x - currPt.x) && + ((_currMoveDirection == 2 && prevPt.x < currPt.x) || + (_currMoveDirection == 4 && prevPt.x >= currPt.x))) { + if (_currMoveDirection == 2) + _currMoveDirection = 4; + else if (_currMoveDirection == 4) + _currMoveDirection = 2; + if (_isIdle) + stTurnCarMoveToPrevPoint(); + else + stBrakeMoveToPrevPoint(); + } else { + if (_steps == 0) { + gotoNextState(); + _isIdle = false; + startAnimation(0x9966B138, 0, -1); + SetMessageHandler(&AsCommonCar::hmAnimation); + SetUpdateHandler(&AsCommonCar::update); + NextState(&AsCommonCar::stUpdateMoveDirection); + } + _isBraking = false; + SetSpriteUpdate(&AsCommonCar::suMoveToPrevPoint); + _lastDistance = 640; + } + } +} + +void AsCommonCar::stBrakeMoveToPrevPoint() { + FinalizeState(NULL); + _isBusy = true; + _isBraking = true; + startAnimation(0x192ADD30, 0, -1); + SetUpdateHandler(&AsCommonCar::update); + SetMessageHandler(&AsCommonCar::hmAnimation); + NextState(&AsCommonCar::stTurnCarMoveToPrevPoint); +} + +void AsCommonCar::evTurnCarDone() { + _isBusy = false; + setDoDeltaX(2); + _newMoveDirection = 0; + stUpdateMoveDirection(); +} + +void AsCommonCar::suMoveToNextPoint() { + int16 newX = _x, newY = _y; + + if (_currPointIndex >= (int)_pathPoints->size()) { + _yMoveTotalSteps = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2006, 0); + return; + } + + if (_isBraking) { + if (_steps <= 0) { + sendMessage(this, 0x1019, 0); + return; + } else + _steps--; + } else if (_steps < 11) + _steps++; + + bool firstTime = true; + _ySteps = _steps; + int stepsCtr = _steps; + + while (stepsCtr > 0) { + NPoint pt1; + NPoint pt2 = pathPoint(_currPointIndex); + if (_currPointIndex + 1 >= (int)_pathPoints->size()) + pt1 = pathPoint(0); + else + pt1 = pathPoint(_currPointIndex + 1); + int16 deltaX = ABS(pt1.x - pt2.x); + int16 deltaY = ABS(pt1.y - pt2.y); + if (deltaX >= deltaY) { + _newMoveDirection = 2; + if (pt1.x < pt2.x) + _newMoveDirection = 4; + if (stepsCtr + _stepError >= deltaX) { + stepsCtr -= deltaX; + stepsCtr += _stepError; + _stepError = 0; + _currPointIndex++; + if (_currPointIndex == (int)_pathPoints->size() - 1) + stepsCtr = 0; + newX = pathPoint(_currPointIndex).x; + newY = pathPoint(_currPointIndex).y; + } else { + _stepError += stepsCtr; + if (pt1.x >= pt2.x) + newX += stepsCtr; + else + newX -= stepsCtr; + if (pt1.y >= pt2.y) + newY = pt2.y + (deltaY * _stepError) / deltaX; + else + newY = pt2.y - (deltaY * _stepError) / deltaX; + stepsCtr = 0; + } + } else { + _newMoveDirection = 3; + if (pt1.y < pt2.y) + _newMoveDirection = 1; + if (firstTime) { + if (pt1.y >= pt2.y) + stepsCtr += 7; + else { + stepsCtr -= 4; + if (stepsCtr < 0) + stepsCtr = 0; + } + _ySteps = stepsCtr; + } + if (stepsCtr + _stepError >= deltaY) { + stepsCtr -= deltaY; + stepsCtr += _stepError; + _stepError = 0; + _currPointIndex++; + if (_currPointIndex == (int)_pathPoints->size() - 1) + stepsCtr = 0; + newX = pathPoint(_currPointIndex).x; + newY = pathPoint(_currPointIndex).y; + } else { + _stepError += stepsCtr; + if (pt1.x >= pt2.x) + newX = pt2.x + (deltaX * _stepError) / deltaY; + else + newX = pt2.x - (deltaX * _stepError) / deltaY; + if (pt1.y >= pt2.y) + newY += stepsCtr; + else + newY -= stepsCtr; + stepsCtr = 0; + } + } + firstTime = false; + } + + if (_yMoveTotalSteps != 0) { + _x = newX; + _y = newY; + _yMoveTotalSteps -= _ySteps; + if (_yMoveTotalSteps <= 0) { + _isBraking = true; + _yMoveTotalSteps = 0; + } + } else { + int distance = calcDistance(_destX, _destY, _x, _y); + _x = newX; + _y = newY; + if (newX > 20 && newX < 620 && newY > 20 && newY < 460) { + _exitDirection = 0; + _inMainArea = true; + } else if (_inMainArea) { + _destX = pathPoint(_pathPoints->size() - 1).x; + _destY = pathPoint(_pathPoints->size() - 1).y; + _inMainArea = false; + if (_x <= 20) + _exitDirection = 1; + else if (_x >= 620) + _exitDirection = 3; + else if (_y <= 20) + _exitDirection = 2; + else if (_y >= 460) + _exitDirection = 4; + if (_exitDirection != 0 && _isBraking) { + _isBraking = false; + _steps = 11; + } + } + if ((distance < 20 && _exitDirection == 0 && _lastDistance < distance) || + (_exitDirection == 0 && _lastDistance + 20 < distance)) + _isBraking = true; + if (distance < _lastDistance) + _lastDistance = distance; + if (_currPointIndex == (int)_pathPoints->size() - 1) { + _isBraking = true; + _yMoveTotalSteps = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2006, 0); + } + } + +} + +void AsCommonCar::suMoveToPrevPoint() { + int16 newX = _x, newY = _y; + + if (_currPointIndex == 0 && _stepError == 0) { + _yMoveTotalSteps = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2005, 0); + return; + } + + if (_isBraking) { + if (_steps <= 0) { + sendMessage(this, 0x1019, 0); + return; + } else + _steps--; + } else if (_steps < 11) + _steps++; + + bool firstTime = true; + _ySteps = _steps; + int stepsCtr = _steps; + + while (stepsCtr > 0) { + if (_stepError == 0) + _currPointIndex--; + NPoint pt1; + NPoint pt2 = pathPoint(_currPointIndex); + if (_currPointIndex + 1 >= (int)_pathPoints->size()) + pt1 = pathPoint(0); + else + pt1 = pathPoint(_currPointIndex + 1); + int16 deltaX = ABS(pt1.x - pt2.x); + int16 deltaY = ABS(pt1.y - pt2.y); + if (deltaX >= deltaY) { + _newMoveDirection = 4; + if (pt1.x < pt2.x) + _newMoveDirection = 2; + if (_stepError == 0) + _stepError = deltaX; + if (stepsCtr > _stepError) { + stepsCtr -= _stepError; + _stepError = 0; + if (_currPointIndex == 0) + stepsCtr = 0; + newX = pathPoint(_currPointIndex).x; + newY = pathPoint(_currPointIndex).y; + } else { + _stepError -= stepsCtr; + if (pt1.x >= pt2.x) + newX -= stepsCtr; + else + newX += stepsCtr; + if (pt1.y >= pt2.y) + newY = pt2.y + (deltaY * _stepError) / deltaX; + else + newY = pt2.y - (deltaY * _stepError) / deltaX; + stepsCtr = 0; + } + } else { + _newMoveDirection = 1; + if (pt1.y < pt2.y) + _newMoveDirection = 3; + if (firstTime) { + if (pt1.y >= pt2.y) { + stepsCtr -= 4; + if (stepsCtr < 0) + stepsCtr = 0; + } else { + stepsCtr += 7; + } + _ySteps = stepsCtr; + } + if (_stepError == 0) + _stepError = deltaY; + if (stepsCtr > _stepError) { + stepsCtr -= _stepError; + _stepError = 0; + if (_currPointIndex == 0) + stepsCtr = 0; + newX = pathPoint(_currPointIndex).x; + newY = pathPoint(_currPointIndex).y; + } else { + _stepError -= stepsCtr; + if (pt1.x >= pt2.x) + newX = pt2.x + (deltaX * _stepError) / deltaY; + else + newX = pt2.x - (deltaX * _stepError) / deltaY; + if (pt1.y >= pt2.y) + newY -= stepsCtr; + else + newY += stepsCtr; + stepsCtr = 0; + } + } + firstTime = false; + } + + if (_yMoveTotalSteps != 0) { + _x = newX; + _y = newY; + _yMoveTotalSteps -= _ySteps; + if (_yMoveTotalSteps <= 0) { + _isBraking = true; + _yMoveTotalSteps = 0; + } + } else { + int distance = calcDistance(_destX, _destY, _x, _y); + _x = newX; + _y = newY; + if (newX > 20 && newX < 620 && newY > 20 && newY < 460) { + _exitDirection = 0; + _inMainArea = true; + } else if (_inMainArea) { + _destX = pathPoint(0).x; + _destY = pathPoint(0).y; + _inMainArea = false; + if (_x <= 20) + _exitDirection = 1; + else if (_x >= 620) + _exitDirection = 3; + else if (_y <= 20) + _exitDirection = 2; + else if (_y >= 460) + _exitDirection = 4; + if (_exitDirection != 0 && _isBraking) { + _isBraking = false; + _steps = 11; + } + } + if ((distance < 20 && _exitDirection == 0 && _lastDistance < distance) || + (_exitDirection == 0 && _lastDistance + 20 < distance)) + _isBraking = true; + if (distance < _lastDistance) + _lastDistance = distance; + if (_currPointIndex == 0 && _stepError == 0) { + _isBraking = true; + _yMoveTotalSteps = 0; + sendMessage(this, 0x1019, 0); + sendMessage(_parentScene, 0x2005, 0); + } + } + +} + +void AsCommonCar::updateSound() { + int maxSoundCounter = 0; + _soundCounter++; + if (_steps != 0 && !_isIdle) { + if (_currMoveDirection == 1) + maxSoundCounter = 18 - _steps; + else if (_currMoveDirection == 3) { + maxSoundCounter = 5 - _steps; + if (maxSoundCounter < 1) + maxSoundCounter = 1; + } else + maxSoundCounter = 14 - _steps; + } else + maxSoundCounter = 21; + if (_soundCounter >= maxSoundCounter) { + sendMessage(_parentScene, 0x200D, 0); + _soundCounter = 0; + } +} + +AsCommonIdleCarLower::AsCommonIdleCarLower(NeverhoodEngine *vm, int16 x, int16 y) + : AnimatedSprite(vm, 0x1209E09F, 1100, x, y) { + + setDoDeltaX(1); + startAnimation(0x1209E09F, 1, -1); + _newStickFrameIndex = 1; +} + +AsCommonIdleCarFull::AsCommonIdleCarFull(NeverhoodEngine *vm, int16 x, int16 y) + : AnimatedSprite(vm, 0x1209E09F, 100, x, y) { + + setDoDeltaX(1); + _newStickFrameIndex = 0; +} + +AsCommonCarConnector::AsCommonCarConnector(NeverhoodEngine *vm, AsCommonCar *asCar) + : AnimatedSprite(vm, 1100), _asCar(asCar) { + + createSurface1(0x60281C10, 150); + startAnimation(0x60281C10, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + SetUpdateHandler(&AsCommonCarConnector::update); +} + +void AsCommonCarConnector::update() { + _x = _asCar->getX(); + _y = _asCar->getY(); + AnimatedSprite::update(); +} + +void Tracks::findTrackPoint(NPoint pt, int &minMatchTrackIndex, int &minMatchDistance, + DataResource &dataResource) { + const uint trackCount = size(); + minMatchTrackIndex = -1; + minMatchDistance = 640; + for (uint trackIndex = 0; trackIndex < trackCount; trackIndex++) { + NPointArray *pointList = dataResource.getPointArray((*this)[trackIndex]->trackPointsName); + for (uint pointIndex = 0; pointIndex < pointList->size(); pointIndex++) { + NPoint testPt = (*pointList)[pointIndex]; + int distance = calcDistance(testPt.x, testPt.y, pt.x, pt.y); + if (distance < minMatchDistance) { + minMatchTrackIndex = trackIndex; + minMatchDistance = distance; + } + } + } +} + +Scene1608::Scene1608(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _asCar(NULL), _countdown1(0) { + + setGlobalVar(V_CAR_DELTA_X, 1); + + SetMessageHandler(&Scene1608::hmLowerFloor); + + _asKey = insertSprite<AsCommonKey>(this, 1, 1100, 198, 220); + addCollisionSprite(_asKey); + + if (which < 0) { + // Restoring game + if (_vm->gameState().which == 1) + // Klaymen is in the car + which = 1; + else { + // Klaymen is standing around + setRectList(0x004B47D0); + insertKlaymen<KmScene1608>(380, 438); + _kmScene1608 = _klaymen; + _klaymenInCar = false; + _sprite1 = insertStaticSprite(0x7D0404E8, 1100); + setMessageList(0x004B46A8); + setBackground(0x10080E01); + setPalette(0x10080E01); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + addCollisionSprite(_asTape); + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + SetUpdateHandler(&Scene1608::upLowerFloor); + insertScreenMouse(0x80E05108); + insertStaticSprite(0x4B18F868, 1200); + } + } else if (which == 0) { + // Klaymen entering from the left + _vm->gameState().which = 0; + setRectList(0x004B47D0); + insertKlaymen<KmScene1608>(0, 438); + _kmScene1608 = _klaymen; + _klaymenInCar = false; + setMessageList(0x004B46B0); + setBackground(0x10080E01); + setPalette(0x10080E01); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + addCollisionSprite(_asTape); + insertScreenMouse(0x80E05108); + _sprite1 = insertStaticSprite(0x7D0404E8, 1100); + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, 640, 480); + SetUpdateHandler(&Scene1608::upLowerFloor); + insertStaticSprite(0x4B18F868, 1200); + } else if (which == 2) { + // Klaymen returning from looking through the upper window + _vm->gameState().which = 1; + _dataResource.load(0x003C0492); + _roomPathPoints = _dataResource.getPointArray(calcHash("meArchroArchRoomPath")); + setBackground(0x98001604); + setPalette(0x98001604); + _palette->addPalette("paPodRed", 65, 31, 65); + insertScreenMouse(0x01600988); + _sprite2 = insertStaticSprite(0x491F38A8, 1100); + _asCar = createSprite<AsCommonCar>(this, 375, 227); // Create but don't add to the sprite list yet + _asIdleCarLower = insertSprite<AsCommonIdleCarLower>(375, 227); + _asIdleCarFull = insertSprite<AsCommonIdleCarFull>(375, 227); + _asCar->setVisible(false); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene1608>(373, 220); + _klaymen->setDoDeltaX(1); + } else + insertKlaymen<KmScene1608>(283, 220); + _kmScene1608 = _klaymen; + setMessageList(0x004B47A8); + SetMessageHandler(&Scene1608::hmUpperFloor); + SetUpdateHandler(&Scene1608::upUpperFloor); + _asCar->setPathPoints(_roomPathPoints); + sendMessage(_asCar, 0x2002, _roomPathPoints->size() - 1); + _sprite3 = insertStaticSprite(0xB47026B0, 1100); + _clipRect1.set(_sprite3->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _clipRect3.set(_sprite2->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _clipRect2 = _clipRect1; + _clipRect2.y2 = 215; + _klaymen->setClipRect(_clipRect1); + _asCar->setClipRect(_clipRect1); + _asIdleCarLower->setClipRect(_clipRect1); + _asIdleCarFull->setClipRect(_clipRect1); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + addCollisionSprite(_asTape); + insertSprite<AsCommonCarConnector>(_asCar)->setClipRect(_clipRect1); + _klaymenInCar = false; + _carClipFlag = false; + _carStatus = 0; + setRectList(0x004B4810); + } + + // NOTE: Not in the else because 'which' is set to 1 in the true branch + if (which == 1) { + // Klaymen riding the car + _vm->gameState().which = 1; + _dataResource.load(0x003C0492); + _roomPathPoints = _dataResource.getPointArray(calcHash("meArchroArchRoomPath")); + setBackground(0x98001604); + setPalette(0x98001604); + _palette->addPalette("paPodRed", 65, 31, 65); + insertScreenMouse(0x01600988); + _asCar = insertSprite<AsCommonCar>(this, 375, 227); + _asIdleCarLower = insertSprite<AsCommonIdleCarLower>(375, 227); + _asIdleCarFull = insertSprite<AsCommonIdleCarFull>(375, 227); + _sprite2 = insertStaticSprite(0x491F38A8, 1100); + _kmScene1608 = createSprite<KmScene1608>(this, 439, 220); + sendMessage(_kmScene1608, 0x2032, 1); + _kmScene1608->setDoDeltaX(1); + SetMessageHandler(&Scene1608::hmRidingCar); + SetUpdateHandler(&Scene1608::upRidingCar); + _asIdleCarLower->setVisible(false); + _asIdleCarFull->setVisible(false); + _asCar->setPathPoints(_roomPathPoints); + sendMessage(_asCar, 0x2002, 0); + sendMessage(_asCar, 0x2008, 90); + _sprite3 = insertStaticSprite(0xB47026B0, 1100); + _clipRect1.set(_sprite3->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _clipRect3.set(_sprite2->getDrawRect().x, _sprite3->getDrawRect().y, 640, _sprite2->getDrawRect().y2()); + _clipRect2 = _clipRect1; + _clipRect2.y2 = 215; + _kmScene1608->setClipRect(_clipRect1); + _asCar->setClipRect(_clipRect1); + _asIdleCarLower->setClipRect(_clipRect1); + _asIdleCarFull->setClipRect(_clipRect1); + _asTape = insertSprite<AsScene1201Tape>(this, 13, 1100, 412, 443, 0x9148A011); + // ... addCollisionSprite(_asTape); + insertSprite<AsCommonCarConnector>(_asCar)->setClipRect(_clipRect1); + _klaymenInCar = true; + _carClipFlag = true; + _carStatus = 0; + } + + _palette->addPalette("paKlayRed", 0, 64, 0); + +} + +Scene1608::~Scene1608() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _kmScene1608->isDoDeltaX() ? 1 : 0); + if (_klaymenInCar) + delete _kmScene1608; + else + delete _asCar; +} + +void Scene1608::upLowerFloor() { + Scene::update(); + if (_countdown1 != 0 && (--_countdown1 == 0)) + leaveScene(0); +} + +void Scene1608::upUpperFloor() { + Scene::update(); + if (_carStatus == 1) { + removeSurface(_klaymen->getSurface()); + removeEntity(_klaymen); + addSprite(_asCar); + _klaymenInCar = true; + clearRectList(); + SetUpdateHandler(&Scene1608::upCarAtHome); + SetMessageHandler(&Scene1608::hmCarAtHome); + _asIdleCarLower->setVisible(false); + _asIdleCarFull->setVisible(false); + _asCar->setVisible(true); + sendMessage(_asCar, 0x2009, 0); + _asCar->handleUpdate(); + _klaymen = NULL; + _carStatus = 0; + } + updateKlaymenCliprect(); +} + +void Scene1608::upCarAtHome() { + Scene::update(); + if (_mouseClicked) { + if (_mouseClickPos.x <= 329 && _asCar->getX() == 375 && _asCar->getY() == 227) { + sendMessage(_asCar, 0x200A, 0); + SetUpdateHandler(&Scene1608::upGettingOutOfCar); + } else { + sendPointMessage(_asCar, 0x2004, _mouseClickPos); + SetMessageHandler(&Scene1608::hmRidingCar); + SetUpdateHandler(&Scene1608::upRidingCar); + } + _mouseClicked = false; + } + updateKlaymenCliprect(); +} + +void Scene1608::upGettingOutOfCar() { + Scene::update(); + if (_carStatus == 2) { + _klaymen = _kmScene1608; + removeSurface(_asCar->getSurface()); + removeEntity(_asCar); + addSprite(_klaymen); + _klaymenInCar = false; + SetMessageHandler(&Scene1608::hmUpperFloor); + SetUpdateHandler(&Scene1608::upUpperFloor); + setRectList(0x004B4810); + _asIdleCarLower->setVisible(true); + _asIdleCarFull->setVisible(true); + _asCar->setVisible(false); + setMessageList(0x004B4748); + processMessageList(); + _klaymen->handleUpdate(); + _carStatus = 0; + } + updateKlaymenCliprect(); +} + +void Scene1608::upRidingCar() { + Scene::update(); + if (_mouseClicked) { + sendPointMessage(_asCar, 0x2004, _mouseClickPos); + _mouseClicked = false; + } + if (_asCar->getX() < 300) { + if (_carClipFlag) { + _carClipFlag = false; + _asCar->setClipRect(_clipRect1); + if (!_asCar->isDoDeltaX()) + sendMessage(_asCar, 0x200E, 0); + } + } else if (!_carClipFlag) { + _carClipFlag = true; + _asCar->setClipRect(_clipRect3); + } +} + +uint32 Scene1608::hmLowerFloor(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x20250B1A) { + clearRectList(); + _klaymen->setVisible(false); + showMouse(false); + _sprite1->setVisible(false); + //sendMessage(_asDoor, 0x4809, 0); // Play sound? + _countdown1 = 28; + } + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_kmScene1608, 0x1014, _asTape); + setMessageList(0x004B4770); + } else if (sender == _asKey) + setMessageList(0x004B46C8); + break; + } + return 0; +} + +uint32 Scene1608::hmUpperFloor(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x60842040) + _carStatus = 1; + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + case 0x4826: + if (sender == _asKey) { + sendEntityMessage(_kmScene1608, 0x1014, _asKey); + setMessageList(0x004B4760); + } + break; + } + return 0; +} + +uint32 Scene1608::hmRidingCar(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + leaveScene(1); + break; + case 0x2006: + SetMessageHandler(&Scene1608::hmCarAtHome); + SetUpdateHandler(&Scene1608::upCarAtHome); + sendMessage(_asCar, 0x200F, 1); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +uint32 Scene1608::hmCarAtHome(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x200A: + _carStatus = 2; + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +void Scene1608::updateKlaymenCliprect() { + if (_kmScene1608->getX() <= 375) + _kmScene1608->setClipRect(_clipRect1); + else + _kmScene1608->setClipRect(_clipRect2); +} + +Scene1609::Scene1609(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _countdown1(1), _currentSymbolIndex(0), _symbolPosition(0), _changeCurrentSymbol(true), _isSolved(false) { + + _vm->gameModule()->initCodeSymbolsPuzzle(); + _noisySymbolIndex = getGlobalVar(V_NOISY_SYMBOL_INDEX); + + SetMessageHandler(&Scene1609::handleMessage); + SetUpdateHandler(&Scene1609::update); + + setBackground(0x92124A14); + setPalette(0x92124A14); + insertPuzzleMouse(0x24A10929, 20, 620); + + for (int symbolPosition = 0; symbolPosition < 12; symbolPosition++) + _asSymbols[symbolPosition] = insertSprite<AsScene3011Symbol>(symbolPosition, false); + + _ssButton = insertSprite<SsScene3011Button>(this, true); + addCollisionSprite(_ssButton); + loadSound(0, 0x68E25540); + +} + +void Scene1609::update() { + if (!_isSolved && _countdown1 != 0 && (--_countdown1 == 0)) { + if (_changeCurrentSymbol) { + _currentSymbolIndex++; + if (_currentSymbolIndex >= 12) + _currentSymbolIndex = 0; + _asSymbols[_symbolPosition]->change(_currentSymbolIndex + 12, _currentSymbolIndex == (int)getSubVar(VA_CODE_SYMBOLS, _noisySymbolIndex)); + _changeCurrentSymbol = false; + _countdown1 = 36; + } else { + _asSymbols[_symbolPosition]->hide(); + _changeCurrentSymbol = true; + _countdown1 = 12; + } + } + if (_isSolved && !isSoundPlaying(0)) + leaveScene(1); + Scene::update(); +} + +uint32 Scene1609::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: + if (!_isSolved) { + if (_changeCurrentSymbol) + _asSymbols[_symbolPosition]->change(_currentSymbolIndex + 12, false); + _asSymbols[_symbolPosition]->stopSymbolSound(); + _symbolPosition++; + if (_symbolPosition >= 12) { + if (testVars()) { + playSound(0); + setGlobalVar(V_CODE_SYMBOLS_SOLVED, 1); + _isSolved = true; + } else { + _symbolPosition = 0; + for (int i = 0; i < 12; i++) + _asSymbols[i]->hide(); + } + } + _changeCurrentSymbol = true; + _countdown1 = 1; + } + break; + } + return 0; +} + +bool Scene1609::testVars() { + int cmpSymbolIndex = 0; + + // Find the position of the first symbol + while ((int)getSubVar(VA_CODE_SYMBOLS, cmpSymbolIndex) != _asSymbols[0]->getSymbolIndex()) + cmpSymbolIndex++; + + // Check if the entered symbols match + for (int enteredSymbolIndex = 0; enteredSymbolIndex < 12; enteredSymbolIndex++) { + if ((int)getSubVar(VA_CODE_SYMBOLS, cmpSymbolIndex) != _asSymbols[enteredSymbolIndex]->getSymbolIndex()) + return false; + cmpSymbolIndex++; + if (cmpSymbolIndex >= 12) + cmpSymbolIndex = 0; + } + + return true; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1600.h b/engines/neverhood/modules/module1600.h new file mode 100644 index 0000000000..0bf44ff7b8 --- /dev/null +++ b/engines/neverhood/modules/module1600.h @@ -0,0 +1,183 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1600_H +#define NEVERHOOD_MODULES_MODULE1600_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module3000.h" + +namespace Neverhood { + +// Module1600 + +class Module1600 : public Module { +public: + Module1600(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1600(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class AsCommonCar : public AnimatedSprite { +public: + AsCommonCar(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y); + ~AsCommonCar(); + void setPathPoints(NPointArray *pathPoints); +protected: + Scene *_parentScene; + NPointArray *_pathPoints; + int _newMoveDirection; + int _currMoveDirection; + int _exitDirection; + int _currPointIndex; + bool _hasAgainDestPoint; + NPoint _againDestPoint; + bool _hasAgainDestPointIndex; + int _againDestPointIndex; + bool _inMainArea; + bool _isBraking; + bool _isBusy; + bool _isIdle; + bool _isMoving; + bool _rectFlag; + int _idleCounter; + int _idleCounterMax; + int _steps; + int _stepError; + int _lastDistance; + int _yMoveTotalSteps; + int _ySteps; + int _newDeltaXType; + int _soundCounter; + int _turnMoveStatus; + int16 _destX, _destY; + NPoint pathPoint(uint index) { return (*_pathPoints)[index]; } + void update(); + void upIdle(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmLeaveCar(int messageNum, const MessageParam ¶m, Entity *sender); + void stCarAtHome(); + void updateTurnMovement(); + void updateMovement(); + void stEnterCar(); + void stLeaveCar(); + void stLeanForwardIdle(); + void evIdleDone(); + void stIdleBlink(); + void stUpdateMoveDirection(); + void stTurnCar(); + void moveToNextPoint(); + void stBrakeMoveToNextPoint(); + void stTurnCarMoveToNextPoint(); + void moveToPrevPoint(); + void stBrakeMoveToPrevPoint(); + void stTurnCarMoveToPrevPoint(); + void evTurnCarDone(); + void suMoveToNextPoint(); + void suMoveToPrevPoint(); + void updateSound(); +}; + +class AsCommonIdleCarLower : public AnimatedSprite { +public: + AsCommonIdleCarLower(NeverhoodEngine *vm, int16 x, int16 y); +}; + +class AsCommonIdleCarFull : public AnimatedSprite { +public: + AsCommonIdleCarFull(NeverhoodEngine *vm, int16 x, int16 y); +}; + +class AsCommonCarConnector : public AnimatedSprite { +public: + AsCommonCarConnector(NeverhoodEngine *vm, AsCommonCar *asCar); +protected: + AsCommonCar *_asCar; + void update(); +}; + +class Tracks : public Common::Array<TrackInfo*> { +public: + void findTrackPoint(NPoint pt, int &minMatchTrackIndex, int &minMatchDistance, + DataResource &dataResource); +}; + +class Scene1608 : public Scene { +public: + Scene1608(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene1608(); +protected: + AsCommonCar *_asCar; + Sprite *_asKey; + Sprite *_asIdleCarLower; + Sprite *_asIdleCarFull; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_asTape; + Klaymen *_kmScene1608; + NRect _clipRect1; + NRect _clipRect2; + NRect _clipRect3; + int _carStatus; + bool _carClipFlag; + bool _klaymenInCar; + int _countdown1; + NPointArray *_roomPathPoints; + void upLowerFloor(); + void upUpperFloor(); + void upCarAtHome(); + void upGettingOutOfCar(); + void upRidingCar(); + uint32 hmLowerFloor(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmUpperFloor(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmRidingCar(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmCarAtHome(int messageNum, const MessageParam ¶m, Entity *sender); + void updateKlaymenCliprect(); +}; + +class Scene1609 : public Scene { +public: + Scene1609(NeverhoodEngine *vm, Module *parentModule); +protected: + Sprite *_ssButton; + AsScene3011Symbol *_asSymbols[12]; + int _currentSymbolIndex; + int _noisySymbolIndex; + int _symbolPosition; + int _countdown1; + bool _changeCurrentSymbol; + bool _isSolved; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + bool testVars(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1600_H */ diff --git a/engines/neverhood/modules/module1700.cpp b/engines/neverhood/modules/module1700.cpp new file mode 100644 index 0000000000..3a6d1f80cb --- /dev/null +++ b/engines/neverhood/modules/module1700.cpp @@ -0,0 +1,279 @@ +/* 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/module1700.h" +#include "neverhood/gamemodule.h" + +namespace Neverhood { + +static const uint32 kModule1700SoundList[] = { + 0xB288D450, + 0x90804450, + 0x99801500, + 0xB288D455, + 0x93825040, + 0 +}; + +Module1700::Module1700(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + _vm->_soundMan->addMusic(0x04212331, 0x31114225); + _vm->_soundMan->addSoundList(0x04212331, kModule1700SoundList); + _vm->_soundMan->setSoundListParams(kModule1700SoundList, true, 50, 600, 5, 150); + _vm->_soundMan->playTwoSounds(0x04212331, 0x41861371, 0x43A2507F, 0); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 0) + createScene(0, -1); + else if (which == 1) + createScene(4, 1); + else + createScene(4, 3); + +} + +Module1700::~Module1700() { + _vm->_soundMan->deleteGroup(0x04212331); +} + +void Module1700::createScene(int sceneNum, int which) { + debug("Module1700::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _vm->_soundMan->setSoundListParams(kModule1700SoundList, false, 0, 0, 0, 0); + createSmackerScene(0x3028A005, true, true, false); + break; + case 1: + _vm->gameState().sceneNum = 1; + createNavigationScene(0x004AE8B8, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + createNavigationScene(0x004AE8E8, which); + break; + case 3: + _vm->gameState().sceneNum = 3; + _vm->_soundMan->setSoundListParams(kModule1700SoundList, false, 0, 0, 0, 0); + createSmackerScene(0x01190041, true, true, false); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->setSoundListParams(kModule1700SoundList, false, 0, 0, 0, 0); + _vm->_soundMan->startMusic(0x31114225, 0, 2); + _childObject = new Scene1705(_vm, this, which); + break; + } + SetUpdateHandler(&Module1700::updateScene); + _childObject->handleUpdate(); +} + +void Module1700::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + _vm->_soundMan->setSoundListParams(kModule1700SoundList, true, 0, 0, 0, 0); + createScene(1, 0); + break; + case 1: + if (_moduleResult == 0) + createScene(2, 0); + else if (_moduleResult == 1) + createScene(1, 1); + break; + case 2: + if (_moduleResult == 0) + createScene(3, -1); + else if (_moduleResult == 1) + createScene(1, 1); + else if (_moduleResult == 2) { + if (!isSoundPlaying(0)) { + setSoundVolume(0, 60); + playSound(0, 0x58B45E58); + } + createScene(2, 2); + } + break; + case 3: + createScene(4, 0); + break; + case 4: + leaveModule(1); + break; + } + } +} + +// Scene1705 + +static const uint32 kScene1705FileHashes[] = { + 0x910EA801, 0x920EA801, 0x940EA801, + 0x980EA801, 0x800EA801, 0xB00EA801, + 0xD00EA801, 0x100EA801, 0x900EA800, + 0xD10EA801, 0x110EA801, 0x910EA800 +}; + +SsScene1705WallSymbol::SsScene1705WallSymbol(NeverhoodEngine *vm, uint32 fileHash, int symbolIndex) + : StaticSprite(vm, fileHash, 100) { + + _x = _spriteResource.getPosition().x + symbolIndex * 30; + _y = _spriteResource.getPosition().y + 160; + updatePosition(); +} + +SsScene1705Tape::SsScene1705Tape(NeverhoodEngine *vm, Scene *parentScene, uint32 tapeIndex, int surfacePriority, int16 x, int16 y, uint32 fileHash) + : StaticSprite(vm, fileHash, surfacePriority, x - 24, y - 4), _parentScene(parentScene), _tapeIndex(tapeIndex) { + + if (!getSubVar(VA_HAS_TAPE, _tapeIndex) && !getSubVar(VA_IS_TAPE_INSERTED, _tapeIndex)) { + SetMessageHandler(&SsScene1705Tape::handleMessage); + } else { + setVisible(false); + SetMessageHandler(NULL); + } + _collisionBoundsOffset = _drawOffset; + _collisionBoundsOffset.x -= 4; + _collisionBoundsOffset.y -= 8; + _collisionBoundsOffset.width += 8; + _collisionBoundsOffset.height += 16; + Sprite::updateBounds(); +} + +uint32 SsScene1705Tape::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_TAPE, _tapeIndex, 1); + setVisible(false); + SetMessageHandler(NULL); + break; + } + return messageResult; +} + +Scene1705::Scene1705(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _paletteArea(1) { + + Sprite *tempSprite; + + setGlobalVar(V_FELL_DOWN_HOLE, 1); + _vm->gameModule()->initCannonSymbolsPuzzle(); + + SetMessageHandler(&Scene1705::handleMessage); + SetUpdateHandler(&Scene1705::update); + + setHitRects(0x004B69D8); + setBackground(0x03118226); + setPalette(0x03118226); + _palette->addBasePalette(0x91D3A391, 0, 64, 0); + _palette->copyBasePalette(0, 256, 0); + addEntity(_palette); + insertScreenMouse(0x18222039); + + insertSprite<SsScene1705WallSymbol>(kScene1705FileHashes[getSubVar(VA_GOOD_CANNON_SYMBOLS_2, 0)], 0); + insertSprite<SsScene1705WallSymbol>(kScene1705FileHashes[getSubVar(VA_GOOD_CANNON_SYMBOLS_2, 1)], 1); + insertSprite<SsScene1705WallSymbol>(kScene1705FileHashes[getSubVar(VA_GOOD_CANNON_SYMBOLS_2, 2)], 2); + _sprite = insertStaticSprite(0x31313A22, 1100); + _ssTape = insertSprite<SsScene1705Tape>(this, 15, 1100, 238, 439, 0x02363852); + addCollisionSprite(_ssTape); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1705>(231, 434); + setMessageList(0x004B69E8); + sendMessage(this, 0x2000, 0); + _klaymen->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else if (which == 1) { + // Klaymen teleporting in + insertKlaymen<KmScene1705>(431, 434); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B6A08, false); + sendMessage(this, 0x2000, 1); + _klaymen->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else if (which == 2) { + // Klaymen teleporting out + insertKlaymen<KmScene1705>(431, 434); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B6AA0, false); + sendMessage(this, 0x2000, 1); + _klaymen->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else if (which == 3) { + // Klaymen returning from teleporter console + insertKlaymen<KmScene1705>(431, 434); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B6A18, false); + sendMessage(this, 0x2000, 1); + _klaymen->setClipRect(0, 0, _sprite->getDrawRect().x2(), 480); + } else { + // Klaymen falling through the hole + insertKlaymen<KmScene1705>(231, 74); + sendMessage(_klaymen, 0x2000, 0); + setMessageList(0x004B69F0); + sendMessage(this, 0x2000, 0); + tempSprite = insertStaticSprite(0x30303822, 1100); + _klaymen->setClipRect(0, tempSprite->getDrawRect().y, _sprite->getDrawRect().x2(), 480); + } + +} + +void Scene1705::update() { + Scene::update(); + if (_klaymen->getX() < 224 && _paletteArea != 0) { + _palette->addBasePalette(0xF2210C15, 0, 64, 0); + _palette->startFadeToPalette(12); + _paletteArea = 0; + } else if (_klaymen->getX() >= 224 && _paletteArea == 0) { + _palette->addBasePalette(0x91D3A391, 0, 64, 0); + _palette->startFadeToPalette(12); + _paletteArea = 1; + } +} + +uint32 Scene1705::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) { + setRectList(0x004B6B40); + _klaymen->setKlaymenIdleTable3(); + } else { + setRectList(0x004B6B30); + _klaymen->setKlaymenIdleTable1(); + } + break; + case 0x4826: + if (sender == _ssTape && _klaymen->getX() <= 318) { + sendEntityMessage(_klaymen, 0x1014, sender); + setMessageList(0x004B6AC0); + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1700.h b/engines/neverhood/modules/module1700.h new file mode 100644 index 0000000000..f57c411a18 --- /dev/null +++ b/engines/neverhood/modules/module1700.h @@ -0,0 +1,72 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1700_H +#define NEVERHOOD_MODULES_MODULE1700_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/smackerscene.h" + +namespace Neverhood { + +class Module1700 : public Module { +public: + Module1700(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1700(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1705 + +class SsScene1705WallSymbol : public StaticSprite { +public: + SsScene1705WallSymbol(NeverhoodEngine *vm, uint32 fileHash, int symbolIndex); +}; + +class SsScene1705Tape : public StaticSprite { +public: + SsScene1705Tape(NeverhoodEngine *vm, Scene *parentScene, uint32 tapeIndex, int surfacePriority, int16 x, int16 y, uint32 fileHash); +protected: + Scene *_parentScene; + uint32 _tapeIndex; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1705 : public Scene { +public: + Scene1705(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite; + Sprite *_ssTape; + int _paletteArea; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1700_H */ diff --git a/engines/neverhood/modules/module1800.cpp b/engines/neverhood/modules/module1800.cpp new file mode 100644 index 0000000000..2a6057f9c8 --- /dev/null +++ b/engines/neverhood/modules/module1800.cpp @@ -0,0 +1,181 @@ +/* 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/module1800.h" +#include "neverhood/navigationscene.h" +#include "neverhood/menumodule.h" + +namespace Neverhood { + +static const uint32 kModule1800SoundList[] = { + 0x16805548, + 0x16805048, + 0xD0E14441, + 0x90E090C2, + 0x90E1D0C2, + 0x90E2D0C2, + 0 +}; + +Module1800::Module1800(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + _vm->_soundMan->addSoundList(0x04A14718, kModule1800SoundList); + _vm->_soundMan->setSoundListParams(kModule1800SoundList, true, 50, 600, 10, 150); + _vm->_soundMan->playTwoSounds(0x04A14718, 0x8A382B55, 0x0C242F1D, 0); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 2) + createScene(5, 0); + else if (which == 3) + createScene(0, 0); + else + createScene(3, 1); + +} + +Module1800::~Module1800() { + _vm->_soundMan->deleteGroup(0x04A14718); +} + +void Module1800::createScene(int sceneNum, int which) { + static const byte kNavigationTypes00[] = {1, 0, 2, 0}; + static const byte kNavigationTypes01[] = {5}; + debug("Module1800::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + createNavigationScene(0x004AFD38, which, kNavigationTypes00); + break; + case 1: + _vm->gameState().sceneNum = 1; + createNavigationScene(0x004AFD98, which, kNavigationTypes01); + break; + case 2: + _vm->gameState().sceneNum = 2; + createSmackerScene(0x006C0085, true, true, false); + break; + case 3: + _vm->gameState().sceneNum = 3; + createNavigationScene(0x004AFDB0, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + createNavigationScene(0x004AFDE0, which); + break; + case 5: + _vm->gameState().sceneNum = 5; + createNavigationScene(0x004AFE40, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->_soundMan->deleteGroup(0x04A14718); + createSmackerScene(0x08D84010, true, true, false); + break; + case 7: + _vm->gameState().sceneNum = 7; + _vm->_soundMan->setSoundListParams(kModule1800SoundList, false, 0, 0, 0, 0); + createSmackerScene(0x0168B121, true, true, false); + break; + case 8: + _vm->gameState().sceneNum = 8; + _childObject = new CreditsScene(_vm, this, false); + break; + case 1009: + _vm->gameState().sceneNum = 3; + // NOTE: Newly introduced sceneNum 1009 (was duplicate 3 with own update handler) + createSmackerScene(0x0A840C01, true, true, false); + break; + } + SetUpdateHandler(&Module1800::updateScene); + _childObject->handleUpdate(); +} + +void Module1800::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(4, 0); + else if (_moduleResult == 2) + createScene(1, -1); + else if (_moduleResult == 3) + createScene(3, 0); + break; + case 1: + if (_navigationAreaType == 3) + createScene(7, -1); + else + createScene(2, -1); + break; + case 2: + createScene(0, 2); + break; + case 3: + if (_moduleResult == 0) + createScene(1009, -1); + else if (_moduleResult == 1) + createScene(0, 1); + break; + case 4: + if (_moduleResult == 0) + createScene(6, -1); + else if (_moduleResult == 1) + createScene(5, 0); + else if (_moduleResult == 2) + createScene(0, 3); + else if (_moduleResult == 3) + createScene(4, 3); + break; + case 5: + if (_moduleResult == 0) + leaveModule(2); + else if (_moduleResult == 1) + createScene(4, 3); + break; + case 6: + createScene(8, -1); + break; + case 7: + leaveModule(3); + break; + case 8: + // NOTE: After Klaymen jumped into the hole and died... + leaveModule(1); + break; + case 1009: + leaveModule(0); + break; + } + } else { + switch (_sceneNum) { + case 0: + if (navigationScene()->isWalkingForward() && navigationScene()->getNavigationIndex() == 2) + _vm->_soundMan->setTwoSoundsPlayFlag(false); + break; + } + } +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1800.h b/engines/neverhood/modules/module1800.h new file mode 100644 index 0000000000..d3f3a635c3 --- /dev/null +++ b/engines/neverhood/modules/module1800.h @@ -0,0 +1,46 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1800_H +#define NEVERHOOD_MODULES_MODULE1800_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module1800 + +class Module1800 : public Module { +public: + Module1800(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1800(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1800_H */ diff --git a/engines/neverhood/modules/module1900.cpp b/engines/neverhood/modules/module1900.cpp new file mode 100644 index 0000000000..1a9ffa127b --- /dev/null +++ b/engines/neverhood/modules/module1900.cpp @@ -0,0 +1,650 @@ +/* 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/module1900.h" +#include "neverhood/gamemodule.h" + +namespace Neverhood { + +static const uint32 kModule1900SoundList[] = { + 0xB4005E60, + 0x91835066, + 0x90E14440, + 0 +}; + +Module1900::Module1900(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + // NOTE: The original has a Scene1908 here as well but it's not used here but in another module... + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else + createScene(0, 0); + + _vm->_soundMan->addSoundList(0x04E1C09C, kModule1900SoundList); + _vm->_soundMan->setSoundListParams(kModule1900SoundList, true, 50, 600, 5, 150); + +} + +Module1900::~Module1900() { + _vm->_soundMan->deleteGroup(0x04E1C09C); +} + +void Module1900::createScene(int sceneNum, int which) { + debug("Module1900::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _childObject = new Scene1901(_vm, this, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + _childObject = new Scene1907(_vm, this); + break; + } + SetUpdateHandler(&Module1900::updateScene); + _childObject->handleUpdate(); +} + +void Module1900::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(6, 0); + else + leaveModule(0); + break; + case 6: + createScene(0, 1); + break; + } + } +} + +// Scene1901 + +Scene1901::Scene1901(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + setRectList(0x004B34C8); + + setBackground(0x01303227); + setPalette(0x01303227); + insertScreenMouse(0x0322301B); + + insertStaticSprite(0x42213133, 1100); + + if (!getGlobalVar(V_STAIRS_PUZZLE_SOLVED)) + insertStaticSprite(0x40A40168, 100); + else if (getGlobalVar(V_STAIRS_DOWN)) { + insertStaticSprite(0x124404C4, 100); + setGlobalVar(V_STAIRS_DOWN_ONCE, 1); + } else + insertStaticSprite(0x02840064, 100); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene1901>(120, 380); + setMessageList(0x004B3408); + } else if (which == 1) { + // Klaymen returning from the puzzle + insertKlaymen<KmScene1901>(372, 380); + setMessageList(0x004B3410); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene1901>(0, 380); + setMessageList(0x004B3400); + } + + tempSprite = insertStaticSprite(0x4830A402, 1100); + _klaymen->setClipRect(tempSprite->getDrawRect().x, 0, 640, 480); + +} + +static const NPoint kAsScene1907SymbolGroundPositions[] = { + {160, 310}, { 90, 340}, {210, 335}, + {210, 380}, {310, 340}, {290, 400}, + {400, 375}, {370, 435}, {475, 415} +}; + +static const NPoint kAsScene1907SymbolPluggedInPositions[] = { + {275, 125}, {244, 125}, {238, 131}, + {221, 135}, {199, 136}, {168, 149}, + {145, 152}, {123, 154}, {103, 157} +}; + +static const NPoint kAsScene1907SymbolGroundHitPositions[] = { + {275, 299}, {244, 299}, {238, 305}, + {221, 309}, {199, 310}, {168, 323}, + {145, 326}, {123, 328}, {103, 331} +}; + +static const NPoint kAsScene1907SymbolPluggedInDownPositions[] = { + {275, 136}, {244, 156}, {238, 183}, + {221, 207}, {199, 228}, {168, 262}, + {145, 285}, {123, 307}, {103, 331} +}; + +static const uint32 kAsScene1907SymbolFileHashes[] = { + 0x006A1034, 0x006A1010, 0x006A1814, + 0x006A1016, 0x006A0014, 0x002A1014, + 0x00EA1014, 0x206A1014, 0x046A1414 +}; + +bool AsScene1907Symbol::_plugInFailed = false; +int AsScene1907Symbol::_plugInTryCount = 0; + +AsScene1907Symbol::AsScene1907Symbol(NeverhoodEngine *vm, Scene1907 *parentScene, int elementIndex, int positionIndex) + : AnimatedSprite(vm, 1000 - positionIndex), _parentScene(parentScene), _elementIndex(elementIndex), _isMoving(false) { + + _plugInFailed = false; + _plugInTryCount = 0; + + if (getGlobalVar(V_STAIRS_PUZZLE_SOLVED)) { + _isPluggedIn = true; + _currPositionIndex = elementIndex; + if (!getGlobalVar(V_STAIRS_DOWN)) { + _x = kAsScene1907SymbolPluggedInPositions[_currPositionIndex].x; + _y = kAsScene1907SymbolPluggedInPositions[_currPositionIndex].y; + } else { + _x = kAsScene1907SymbolPluggedInDownPositions[_currPositionIndex].x; + _y = kAsScene1907SymbolPluggedInDownPositions[_currPositionIndex].y; + } + createSurface1(kAsScene1907SymbolFileHashes[_elementIndex], 1000 + _currPositionIndex); + startAnimation(kAsScene1907SymbolFileHashes[_elementIndex], -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + } else { + _isPluggedIn = false; + _currPositionIndex = positionIndex; + loadSound(0, 0x74231924); + loadSound(1, 0x36691914); + loadSound(2, 0x5421D806); + _parentScene->setPositionFree(_currPositionIndex, false); + _x = kAsScene1907SymbolGroundPositions[_currPositionIndex].x; + _y = kAsScene1907SymbolGroundPositions[_currPositionIndex].y; + createSurface1(kAsScene1907SymbolFileHashes[_elementIndex], 1000 + _currPositionIndex); + startAnimation(kAsScene1907SymbolFileHashes[_elementIndex], 0, -1); + _newStickFrameIndex = 0; + } + _collisionBoundsOffset.set(0, 0, 80, 80); + Sprite::updateBounds(); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + +} + +void AsScene1907Symbol::update() { + updateAnim(); + handleSpriteUpdate(); + updatePosition(); + if (_plugInFailed && _plugInTryCount == 0) + _plugInFailed = false; +} + +uint32 AsScene1907Symbol::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_isPluggedIn && !_plugInFailed) { + tryToPlugIn(); + messageResult = 1; + } else + messageResult = 0; + break; + } + return messageResult; +} + +uint32 AsScene1907Symbol::hmTryToPlugIn(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1907Symbol::suTryToPlugIn() { + _currStep++; + _x -= _deltaX; + _y -= _deltaY; + if (_currStep == 16) { + _x -= _smallDeltaX; + _y -= _smallDeltaY; + SetSpriteUpdate(NULL); + } +} + +void AsScene1907Symbol::suFallOff() { + if (_fallOffDelay != 0) { + _fallOffDelay--; + } else { + _y += _yAccel; + _yAccel += 8; + if (_y >= kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y) { + _y = kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y; + stFallOffHitGround(); + } + } +} + +void AsScene1907Symbol::suFallOffHitGround() { + + if (_x == _someX - _xBreak) + _x -= _smallDeltaX; + else + _x -= _deltaX; + + if (_y == kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y) { + _y -= _someY; + } + + if (_currStep < 8) { + _y -= _yAccel; + _yAccel -= 4; + if (_yAccel < 0) + _yAccel = 0; + } else if (_currStep < 15) { + _y += _yAccel; + _yAccel += 4; + } else { + _y = kAsScene1907SymbolGroundPositions[_newPositionIndex].y; + cbFallOffHitGroundEvent(); + } + + _currStep++; +} + +void AsScene1907Symbol::suMoveDown() { + _y += _yIncr; + if (_yIncr < 11) + _yIncr++; + if (_y >= kAsScene1907SymbolPluggedInDownPositions[_elementIndex].y) { + _y = kAsScene1907SymbolPluggedInDownPositions[_elementIndex].y; + _isMoving = false; + SetSpriteUpdate(NULL); + } +} + +void AsScene1907Symbol::suMoveUp() { + _y -= _yIncr; + if (getGlobalVar(V_WALL_BROKEN)) { + if (_y - (9 + (_elementIndex > 5 ? 31 : 0)) < kAsScene1907SymbolPluggedInPositions[_elementIndex].y) + _yIncr--; + else + _yIncr++; + } else + _yIncr = 2; + if (_yIncr > 9) + _yIncr = 9; + else if (_yIncr < 1) + _yIncr = 1; + if (_y < kAsScene1907SymbolPluggedInPositions[_elementIndex].y) { + _y = kAsScene1907SymbolPluggedInPositions[_elementIndex].y; + _isMoving = false; + SetSpriteUpdate(NULL); + } +} + +void AsScene1907Symbol::tryToPlugIn() { + _isPluggedIn = true; + _plugInTryCount++; + _newPositionIndex = _parentScene->getNextPosition(); + _parentScene->setPositionFree(_currPositionIndex, true); + sendMessage(_parentScene, 0x1022, 1100 + _newPositionIndex); + startAnimation(kAsScene1907SymbolFileHashes[_elementIndex], 0, -1); + SetUpdateHandler(&AsScene1907Symbol::update); + SetMessageHandler(&AsScene1907Symbol::hmTryToPlugIn); + SetSpriteUpdate(&AsScene1907Symbol::suTryToPlugIn); + _currStep = 0; + _deltaX = (_x - kAsScene1907SymbolPluggedInPositions[_newPositionIndex].x) / 16; + _smallDeltaX = _x - _deltaX * 16 - kAsScene1907SymbolPluggedInPositions[_newPositionIndex].x; + _deltaY = (_y - kAsScene1907SymbolPluggedInPositions[_newPositionIndex].y) / 16; + _smallDeltaY = _y - _deltaY * 16 - kAsScene1907SymbolPluggedInPositions[_newPositionIndex].y; + if (_elementIndex == _newPositionIndex) { + NextState(&AsScene1907Symbol::stPlugIn); + } else { + _plugInFailed = true; + NextState(&AsScene1907Symbol::stPlugInFail); + } +} + +void AsScene1907Symbol::fallOff(int newPositionIndex, int fallOffDelay) { + _isPluggedIn = false; + _newPositionIndex = newPositionIndex; + _fallOffDelay = fallOffDelay; + _parentScene->setPositionFree(_newPositionIndex, false); + _x = kAsScene1907SymbolPluggedInPositions[_currPositionIndex].x; + _y = kAsScene1907SymbolPluggedInPositions[_currPositionIndex].y; + _someX = _x; + _someY = _y; + startAnimation(kAsScene1907SymbolFileHashes[_elementIndex], -1, 0); + _playBackwards = true; + _newStickFrameIndex = STICK_LAST_FRAME; + _currStep = 0; + _yAccel = 1; + SetUpdateHandler(&AsScene1907Symbol::update); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteUpdate(&AsScene1907Symbol::suFallOff); +} + +void AsScene1907Symbol::stFallOffHitGround() { + playSound(1); + sendMessage(_parentScene, 0x1022, 1000 + _newPositionIndex); + Entity::_priority = 1000 - _newPositionIndex; + _parentScene->removeCollisionSprite(this); + _parentScene->addCollisionSprite(this); + SetSpriteUpdate(&AsScene1907Symbol::suFallOffHitGround); + NextState(&AsScene1907Symbol::cbFallOffHitGroundEvent); + _newStickFrameIndex = 0; + _currStep = 0; + _yAccel = 30; + _deltaX = (_x - kAsScene1907SymbolGroundPositions[_newPositionIndex].x) / 15; + _xBreak = _deltaX * 15; + _smallDeltaX = _x - kAsScene1907SymbolGroundPositions[_newPositionIndex].x - _xBreak; + _someY = 0; + if (kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y > kAsScene1907SymbolGroundPositions[_newPositionIndex].y) + _someY = kAsScene1907SymbolGroundHitPositions[_currPositionIndex].y - kAsScene1907SymbolGroundPositions[_newPositionIndex].y; +} + +void AsScene1907Symbol::cbFallOffHitGroundEvent() { + _currPositionIndex = _newPositionIndex; + if (_plugInTryCount > 0) + _plugInTryCount--; + startAnimation(kAsScene1907SymbolFileHashes[_elementIndex], 0, -1); + _newStickFrameIndex = 0; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteUpdate(NULL); + updateBounds(); + playSound(2); +} + +void AsScene1907Symbol::stPlugIn() { + playSound(0); + _currPositionIndex = _newPositionIndex; + stopAnimation(); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteUpdate(NULL); + if (_elementIndex == 8) + sendMessage(_parentScene, 0x2001, 0); +} + +void AsScene1907Symbol::stPlugInFail() { + _currPositionIndex = _newPositionIndex; + stopAnimation(); + _parentScene->plugInFailed(); +} + +void AsScene1907Symbol::moveUp() { + startAnimation(kAsScene1907SymbolFileHashes[_elementIndex], -1, -1); + stopAnimation(); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteUpdate(&AsScene1907Symbol::suMoveUp); + _yIncr = 1; + _isMoving = true; +} + +void AsScene1907Symbol::moveDown() { + startAnimation(kAsScene1907SymbolFileHashes[_elementIndex], -1, -1); + stopAnimation(); + SetMessageHandler(&AsScene1907Symbol::handleMessage); + SetSpriteUpdate(&AsScene1907Symbol::suMoveDown); + _yIncr = 4; + _isMoving = true; +} + +SsScene1907UpDownButton::SsScene1907UpDownButton(NeverhoodEngine *vm, Scene1907 *parentScene, AsScene1907Symbol *asScene1907Symbol) + : StaticSprite(vm, 1400), _parentScene(parentScene), _asScene1907Symbol(asScene1907Symbol), + _countdown1(0) { + + loadSprite(0x64516424, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 1400); + setVisible(false); + loadSound(0, 0x44061000); + SetUpdateHandler(&SsScene1907UpDownButton::update); + SetMessageHandler(&SsScene1907UpDownButton::handleMessage); + if (getGlobalVar(V_STAIRS_PUZZLE_SOLVED)) { + if (getGlobalVar(V_STAIRS_DOWN)) + setToDownPosition(); + else + setToUpPosition(); + } +} + +void SsScene1907UpDownButton::update() { + updatePosition(); + if (_countdown1 != 0 && (--_countdown1 == 0)) { + setVisible(false); + sendMessage(_parentScene, 0x2000, 0); + } +} + +uint32 SsScene1907UpDownButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown1 == 0 && !_asScene1907Symbol->isMoving() && getGlobalVar(V_STAIRS_PUZZLE_SOLVED)) { + setVisible(true); + _countdown1 = 4; + updatePosition(); + playSound(0); + } + messageResult = 1; + } + return messageResult; +} + +void SsScene1907UpDownButton::setToUpPosition() { + _y = _spriteResource.getPosition().y; + updateBounds(); + updatePosition(); +} + +void SsScene1907UpDownButton::setToDownPosition() { + _y = _spriteResource.getPosition().y + 174; + updateBounds(); + updatePosition(); +} + +AsScene1907WaterHint::AsScene1907WaterHint(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1400) { + + createSurface1(0x110A1061, 1500); + _x = 320; + _y = 240; + startAnimation(0x110A1061, 0, -1); + _newStickFrameIndex = 0; + setVisible(false); + _needRefresh = true; + AnimatedSprite::updatePosition(); + SetUpdateHandler(&AsScene1907WaterHint::update); + SetMessageHandler(&Sprite::handleMessage); +} + +void AsScene1907WaterHint::update() { + updateAnim(); + updatePosition(); +} + +uint32 AsScene1907WaterHint::hmShowing(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene1907WaterHint::show() { + setVisible(true); + startAnimation(0x110A1061, 0, -1); + SetMessageHandler(&AsScene1907WaterHint::hmShowing); + NextState(&AsScene1907WaterHint::hide); +} + +void AsScene1907WaterHint::hide() { + stopAnimation(); + setVisible(false); + SetMessageHandler(&Sprite::handleMessage); +} + +Scene1907::Scene1907(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule), _currMovingSymbolIndex(0), _pluggedInCount(0), + _moveDownCountdown(0), _moveUpCountdown(0), _countdown3(0), _hasPlugInFailed(false) { + + setBackground(0x20628E05); + setPalette(0x20628E05); + + for (int i = 0; i < 9; i++) + _positionFree[i] = true; + + for (int i = 0; i < 9; i++) { + _asSymbols[i] = insertSprite<AsScene1907Symbol>(this, i, getRandomPositionIndex()); + addCollisionSprite(_asSymbols[i]); + } + + _ssUpDownButton = insertSprite<SsScene1907UpDownButton>(this, _asSymbols[8]); + addCollisionSprite(_ssUpDownButton); + + _asWaterHint = insertSprite<AsScene1907WaterHint>(); + + insertPuzzleMouse(0x28E0120E, 20, 620); + + SetMessageHandler(&Scene1907::handleMessage); + SetUpdateHandler(&Scene1907::update); + + if (getGlobalVar(V_STAIRS_PUZZLE_SOLVED)) + _pluggedInCount = 9; + + loadSound(0, 0x72004A10); + loadSound(1, 0x22082A12); + loadSound(2, 0x21100A10); + loadSound(3, 0x68E25540); + +} + +void Scene1907::update() { + Scene::update(); + + if (_hasPlugInFailed) { + int fallOffDelay = 0; + _hasPlugInFailed = false; + for (int i = 0; i < 9; i++) { + AsScene1907Symbol *asSymbol = _asSymbols[8 - i]; + if (asSymbol->isPluggedIn()) { + asSymbol->fallOff(getRandomPositionIndex(), fallOffDelay); + fallOffDelay += _vm->_rnd->getRandomNumber(10 - 1) + 4; + } + } + } + + if (_moveDownCountdown != 0 && (--_moveDownCountdown == 0)) { + _asSymbols[_currMovingSymbolIndex]->moveDown(); + if (_currMovingSymbolIndex > 0) { + _moveDownCountdown = 2; + _currMovingSymbolIndex--; + } + } + + if (_moveUpCountdown != 0 && (--_moveUpCountdown == 0)) { + _moveDownCountdown = 0; + for (int i = 0; i < 9; i++) + _asSymbols[i]->moveUp(); + } + + if (_countdown3 != 0 && (--_countdown3 == 0)) { + _asWaterHint->show(); + _moveUpCountdown = 4; + } + +} + +uint32 Scene1907::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) && + !_hasPlugInFailed && _moveDownCountdown == 0 && _moveUpCountdown == 0 && _countdown3 == 0) { + leaveScene(0); + } + break; + case 0x2000: + if (getGlobalVar(V_STAIRS_DOWN)) { + playSound(0); + for (int i = 0; i < 9; i++) + _asSymbols[i]->moveUp(); + _ssUpDownButton->setToUpPosition(); + setGlobalVar(V_STAIRS_DOWN, 0); + } else { + if (!getGlobalVar(V_WALL_BROKEN)) { + playSound(2); + _countdown3 = 5; + } else { + playSound(1); + _ssUpDownButton->setToDownPosition(); + setGlobalVar(V_STAIRS_DOWN, 1); + } + _moveDownCountdown = 1; + _currMovingSymbolIndex = 8; + } + break; + case 0x2001: + playSound(3); + setGlobalVar(V_STAIRS_PUZZLE_SOLVED, 1); + break; + } + return 0; +} + +void Scene1907::plugInFailed() { + _pluggedInCount = 0; + _hasPlugInFailed = true; +} + +int Scene1907::getRandomPositionIndex() { + bool found = false; + int index = 0; + // Check if any position is free + for (int i = 0; i < 9; i++) + if (_positionFree[i]) + found = true; + if (found) { + // Get a random free position + found = false; + while (!found) { + index = _vm->_rnd->getRandomNumber(9 - 1); + if (_positionFree[index]) + found = true; + } + } + return index; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module1900.h b/engines/neverhood/modules/module1900.h new file mode 100644 index 0000000000..abb5eb1d87 --- /dev/null +++ b/engines/neverhood/modules/module1900.h @@ -0,0 +1,143 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE1900_H +#define NEVERHOOD_MODULES_MODULE1900_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module1200.h" + +namespace Neverhood { + +class Module1900 : public Module { +public: + Module1900(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module1900(); +protected: + int _sceneNum; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1901 + +class Scene1901 : public Scene { +public: + Scene1901(NeverhoodEngine *vm, Module *parentModule, int which); +}; + +// Scene1907 + +class Scene1907; + +class AsScene1907Symbol : public AnimatedSprite { +public: + AsScene1907Symbol(NeverhoodEngine *vm, Scene1907 *parentScene, int elementIndex, int positionIndex); + void moveUp(); + void moveDown(); + void fallOff(int newPositionIndex, int fallOffDelay); + bool isPluggedIn() { return _isPluggedIn; } + bool isMoving() { return _isMoving; } +protected: + Scene1907 *_parentScene; + int _elementIndex; + int _currPositionIndex; + int _newPositionIndex; + bool _isPluggedIn; + bool _isMoving; + int _someX, _someY; + int _xBreak; + int _currStep; + int _yAccel; + int _yIncr; + int _fallOffDelay; + int _deltaX, _smallDeltaX; + int _deltaY, _smallDeltaY; + // Dumb, change if possible + static bool _plugInFailed; + static int _plugInTryCount; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmTryToPlugIn(int messageNum, const MessageParam ¶m, Entity *sender); + void suTryToPlugIn(); + void suFallOff(); + void suFallOffHitGround(); + void suMoveDown(); + void suMoveUp(); + void tryToPlugIn(); + void stFallOffHitGround(); + void cbFallOffHitGroundEvent(); + void stPlugIn(); + void stPlugInFail(); +}; + +class AsScene1907WaterHint : public AnimatedSprite { +public: + AsScene1907WaterHint(NeverhoodEngine *vm); + void show(); +protected: + void update(); + uint32 hmShowing(int messageNum, const MessageParam ¶m, Entity *sender); + void hide(); +}; + +class SsScene1907UpDownButton : public StaticSprite { +public: + SsScene1907UpDownButton(NeverhoodEngine *vm, Scene1907 *parentScene, AsScene1907Symbol *asScene1907Symbol); + void setToUpPosition(); + void setToDownPosition(); +protected: + Scene1907 *_parentScene; + AsScene1907Symbol *_asScene1907Symbol; + int _countdown1; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene1907 : public Scene { +public: + Scene1907(NeverhoodEngine *vm, Module *parentModule); + void plugInFailed(); + void setPositionFree(int index, bool value) { _positionFree[index] = value; } + int getNextPosition() { return _pluggedInCount++; } +protected: + AsScene1907Symbol *_asSymbols[9]; + SsScene1907UpDownButton *_ssUpDownButton; + AsScene1907WaterHint *_asWaterHint; + int _currMovingSymbolIndex; + int _pluggedInCount; + int _moveDownCountdown; + int _moveUpCountdown; + int _countdown3; + bool _hasPlugInFailed; + bool _positionFree[9]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + int getRandomPositionIndex(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE1900_H */ diff --git a/engines/neverhood/modules/module2000.cpp b/engines/neverhood/modules/module2000.cpp new file mode 100644 index 0000000000..5039da1b01 --- /dev/null +++ b/engines/neverhood/modules/module2000.cpp @@ -0,0 +1,160 @@ +/* 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/module2000.h" +#include "neverhood/gamemodule.h" +#include "neverhood/navigationscene.h" + +namespace Neverhood { + +Module2000::Module2000(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 0) + createScene(0, 1); + else if (which == 1) + createScene(0, 3); + +} + +Module2000::~Module2000() { + _vm->_soundMan->deleteGroup(0x81293110); +} + +void Module2000::createScene(int sceneNum, int which) { + debug("Module2000::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _childObject = new Scene2001(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + createNavigationScene(getGlobalVar(V_WORLDS_JOINED) ? 0x004B7B48 : 0x004B7B00, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + setGlobalVar(V_WORLDS_JOINED, 1); + setSubVar(V_TELEPORTER_DEST_AVAILABLE, 1, 1); + createSmackerScene(0x204B2031, true, true, false); + break; + } + SetUpdateHandler(&Module2000::updateScene); + _childObject->handleUpdate(); +} + +void Module2000::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + leaveModule(0); + else + createScene(1, 0); + break; + case 1: + if (_moduleResult == 0) { + if (getGlobalVar(V_WORLDS_JOINED)) + createScene(1, 0); + else + createScene(2, -1); + } else if (_moduleResult == 1) + createScene(1, 1); + else if (_moduleResult == 2) + createScene(0, 0); + break; + case 2: + createScene(1, 0); + break; + } + } +} + +// Scene2001 + +Scene2001::Scene2001(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + SetMessageHandler(&Scene2001::handleMessage); + + setBackground(0xA6417244); + setPalette(0xA6417244); + insertScreenMouse(0x17240A6C); + + tempSprite = insertStaticSprite(0x0D641724, 1100); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2001>(300, 345); + setMessageList(0x004B3538); + sendMessage(this, 0x2000, 0); + } else if (which == 1) { + // Klaymen teleporting in + insertKlaymen<KmScene2001>(116, 345); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B3540, false); + sendMessage(this, 0x2000, 1); + } else if (which == 2) { + // Klaymen teleporting out + insertKlaymen<KmScene2001>(116, 345); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B35F0, false); + sendMessage(this, 0x2000, 1); + } else if (which == 3) { + // Klaymen returning from teleporter console + insertKlaymen<KmScene2001>(116, 345); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B3550, false); + sendMessage(this, 0x2000, 1); + } else { + // Klaymen standing around + insertKlaymen<KmScene2001>(390, 345); + setMessageList(0x004B3530); + sendMessage(this, 0x2000, 0); + _klaymen->setDoDeltaX(1); + } + + _klaymen->setClipRect(tempSprite->getDrawRect().x, 0, 640, 480); + +} + +uint32 Scene2001::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) { + setRectList(0x004B3680); + _klaymen->setKlaymenIdleTable3(); + } else { + setRectList(0x004B3670); + _klaymen->setKlaymenIdleTable1(); + } + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2000.h b/engines/neverhood/modules/module2000.h new file mode 100644 index 0000000000..fa62f9a70e --- /dev/null +++ b/engines/neverhood/modules/module2000.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2000_H +#define NEVERHOOD_MODULES_MODULE2000_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module1200.h" + +namespace Neverhood { + +class Module2000 : public Module { +public: + Module2000(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2000(); +protected: + int _sceneNum; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene2001 + +class Scene2001 : public Scene { +public: + Scene2001(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2000_H */ diff --git a/engines/neverhood/modules/module2100.cpp b/engines/neverhood/modules/module2100.cpp new file mode 100644 index 0000000000..0d7f3dd22a --- /dev/null +++ b/engines/neverhood/modules/module2100.cpp @@ -0,0 +1,336 @@ +/* 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/module2100.h" +#include "neverhood/gamemodule.h" +#include "neverhood/modules/module1200.h" + +namespace Neverhood { + +Module2100::Module2100(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + _vm->_soundMan->addMusic(0x10A10C14, 0x11482B95); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 1) + createScene(0, 0); + else if (which == 2) + createScene(0, 3); + else + createScene(0, 1); + +} + +Module2100::~Module2100() { + _vm->_soundMan->deleteMusicGroup(0x10A10C14); +} + +void Module2100::createScene(int sceneNum, int which) { + debug("Module2100::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _vm->_soundMan->startMusic(0x11482B95, 0, 1); + _childObject = new Scene2101(_vm, this, which); + break; + } + SetUpdateHandler(&Module2100::updateScene); + _childObject->handleUpdate(); +} + +void Module2100::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) { + setGlobalVar(V_DOOR_PASSED, 1); + leaveModule(0); + } else + leaveModule(1); + break; + } + } +} + +// Scene2101 + +AsScene2101Door::AsScene2101Door(NeverhoodEngine *vm, bool isOpen) + : AnimatedSprite(vm, 1100) { + + createSurface(100, 328, 347); + _x = 320; + _y = 240; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2101Door::handleMessage); + if (isOpen) { + startAnimation(0x0C202B9C, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + } else + setVisible(false); +} + +uint32 AsScene2101Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + case 0x4808: + stOpenDoor(); + break; + case 0x4809: + stCloseDoor(); + break; + } + return messageResult; +} + +void AsScene2101Door::stOpenDoor() { + startAnimation(0x0C202B9C, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + setVisible(true); + playSound(0, calcHash("fxDoorOpen32")); +} + +void AsScene2101Door::stCloseDoor() { + startAnimation(0xC222A8D4, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + setVisible(true); + playSound(0, calcHash("fxDoorClose32")); + NextState(&AsScene2101Door::stCloseDoorDone); +} + +void AsScene2101Door::stCloseDoorDone() { + stopAnimation(); + setVisible(false); +} + +AsScene2101HitByDoorEffect::AsScene2101HitByDoorEffect(NeverhoodEngine *vm, Sprite *klaymen) + : AnimatedSprite(vm, 1400), _klaymen(klaymen) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2101HitByDoorEffect::handleMessage); + createSurface(1200, 88, 165); + setVisible(false); +} + +uint32 AsScene2101HitByDoorEffect::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + _x = _klaymen->getX(); + _y = _klaymen->getY() - 132; + startAnimation(0x0422255A, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +SsCommonFloorButton::SsCommonFloorButton(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash) + : StaticSprite(vm, 1100), _parentScene(parentScene), _countdown(0), + _fileHash1(fileHash1), _fileHash2(fileHash2), _soundFileHash(soundFileHash) { + + SetUpdateHandler(&SsCommonFloorButton::update); + SetMessageHandler(&SsCommonFloorButton::handleMessage); + if (_soundFileHash == 0) + _soundFileHash = 0x44141000; + createSurface(1010, 61, 30); + if (_fileHash1) + loadSprite(_fileHash1, kSLFDefDrawOffset | kSLFDefPosition); + else + setVisible(false); +} + +void SsCommonFloorButton::update() { + if (_countdown != 0 && (--_countdown == 0)) { + sendMessage(_parentScene, 0x1022, 1010); + if (_fileHash1) + loadSprite(_fileHash1, kSLFDefDrawOffset | kSLFDefPosition); + else + setVisible(false); + } +} + +uint32 SsCommonFloorButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x480B: + sendMessage(_parentScene, 0x480B, 0); + setVisible(true); + sendMessage(_parentScene, 0x1022, 990); + loadSprite(_fileHash2, kSLFDefDrawOffset | kSLFDefPosition); + _countdown = 16; + playSound(0, _soundFileHash); + break; + } + return messageResult; +} + +Scene2101::Scene2101(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + SetMessageHandler(&Scene2101::handleMessage); + SetUpdateHandler(&Scene2101::update); + + setBackground(0x44242305); + setPalette(0x44242305); + insertScreenMouse(0x4230144A); + + insertStaticSprite(0x00502330, 1100); + tempSprite = insertStaticSprite(0x78492010, 1100); + _ssFloorButton = insertSprite<SsCommonFloorButton>(this, 0x72427010, 0x32423010, 200, 0); + _asTape1 = insertSprite<AsScene1201Tape>(this, 18, 1100, 412, 443, 0x9148A011); + addCollisionSprite(_asTape1); + _asTape2 = insertSprite<AsScene1201Tape>(this, 11, 1100, 441, 443, 0x9148A011); + addCollisionSprite(_asTape2); + + if (which < 0) { + insertKlaymen<KmScene2101>(380, 438); + setMessageList(0x004B8E48); + sendMessage(this, 0x2000, 0); + _asDoor = insertSprite<AsScene2101Door>(false); + _doorStatus = 1; + _countdown1 = 0; + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene2101>(640, 438); + setMessageList(0x004B8E50); + sendMessage(this, 0x2000, 0); + _asDoor = insertSprite<AsScene2101Door>(true); + _doorStatus = 2; + _countdown1 = 48; + } else if (which == 2) { + // Klaymen teleporting out + insertKlaymen<KmScene2101>(115, 438); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B8F58); + sendMessage(this, 0x2000, 1); + _asDoor = insertSprite<AsScene2101Door>(false); + _doorStatus = 1; + _countdown1 = 0; + } else if (which == 3) { + // Klaymen returning from the teleporter console + insertKlaymen<KmScene2101>(115, 438); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B8EB0); + sendMessage(this, 0x2000, 1); + _asDoor = insertSprite<AsScene2101Door>(false); + _doorStatus = 1; + _countdown1 = 0; + } else { + // Klaymen teleporting in + insertKlaymen<KmScene2101>(115, 438); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004B8EA0); + sendMessage(this, 0x2000, 1); + _asDoor = insertSprite<AsScene2101Door>(false); + _doorStatus = 1; + _countdown1 = 0; + } + + _asHitByDoorEffect = insertSprite<AsScene2101HitByDoorEffect>(_klaymen); + _klaymen->setClipRect(0, 0, tempSprite->getDrawRect().x2(), 480); + +} + +void Scene2101::update() { + if (_countdown1 != 0) { + if (_doorStatus == 2) { + if (--_countdown1 == 0) { + sendMessage(_asDoor, 0x4809, 0); + _doorStatus = 1; + } + } else { + if (_klaymen->getX() > 575) + _canAcceptInput = false; + if (--_countdown1 == 0) { + if (_klaymen->getX() < 480) { + sendMessage(_asDoor, 0x4809, 0); + _doorStatus = 1; + } else if (_klaymen->getX() >= 480 && _klaymen->getX() <= 575) { + _klaymen->setDoDeltaX(0); + setMessageList2(0x004B8F48); + sendMessage(_asDoor, 0x4809, 0); + sendMessage(_asHitByDoorEffect, 0x2001, 0); + _doorStatus = 1; + } + } + } + } else if (_doorStatus == 1 && _messageValue >= 0 && _klaymen->getX() > 470 && !isMessageList2(0x004B8F48)) + setMessageList2(0x004B8F50); + Scene::update(); +} + +uint32 Scene2101::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x02144CB1) + sendEntityMessage(_klaymen, 0x1014, _ssFloorButton); + else if (param.asInteger() == 0x21E64A00) { + if (_doorStatus == 0) + setMessageList(0x004B8E80); + else + setMessageList(0x004B8EC8); + } else if (param.asInteger() == 0x41442820) + cancelMessageList(); + break; + case 0x2000: + if (param.asInteger() != 0) { + setRectList(0x004B9008); + _klaymen->setKlaymenIdleTable3(); + } else { + setRectList(0x004B8FF8); + _klaymen->setKlaymenIdleTable1(); + } + break; + case 0x480B: + if (sender == _ssFloorButton && _doorStatus == 1) { + sendMessage(_asDoor, 0x4808, 0); + _doorStatus = 0; + _countdown1 = 90; + } + break; + case 0x4826: + if (sender == _asTape1 || sender == _asTape2) { + if (_klaymen->getX() >= 228 && _klaymen->getX() <= 500) { + sendEntityMessage(_klaymen, 0x1014, sender); + setMessageList(0x004B8F78); + } else if (_klaymen->getX() < 228) + setMessageList2(0x004B8F00); + } + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2100.h b/engines/neverhood/modules/module2100.h new file mode 100644 index 0000000000..369f5ac0cc --- /dev/null +++ b/engines/neverhood/modules/module2100.h @@ -0,0 +1,92 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2100_H +#define NEVERHOOD_MODULES_MODULE2100_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +class Module2100 : public Module { +public: + Module2100(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2100(); +protected: + int _sceneNum; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene1901 + +class AsScene2101Door : public AnimatedSprite { +public: + AsScene2101Door(NeverhoodEngine *vm, bool isOpen); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stOpenDoor(); + void stCloseDoor(); + void stCloseDoorDone(); +}; + +class AsScene2101HitByDoorEffect : public AnimatedSprite { +public: + AsScene2101HitByDoorEffect(NeverhoodEngine *vm, Sprite *klaymen); +protected: + Sprite *_klaymen; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsCommonFloorButton : public StaticSprite { +public: + SsCommonFloorButton(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int surfacePriority, uint32 soundFileHash); +protected: + Scene *_parentScene; + uint32 _soundFileHash; + uint32 _fileHash1, _fileHash2; + int16 _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2101 : public Scene { +public: + Scene2101(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_ssFloorButton; + Sprite *_asTape1; + Sprite *_asTape2; + Sprite *_asDoor; + Sprite *_asHitByDoorEffect; + int _countdown1; + int _doorStatus; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2100_H */ 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 diff --git a/engines/neverhood/modules/module2200.h b/engines/neverhood/modules/module2200.h new file mode 100644 index 0000000000..af7171dd53 --- /dev/null +++ b/engines/neverhood/modules/module2200.h @@ -0,0 +1,375 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2200_H +#define NEVERHOOD_MODULES_MODULE2200_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module1000.h" +#include "neverhood/graphics.h" + +namespace Neverhood { + +// Module2200 + +class Module2200 : public Module { +public: + Module2200(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2200(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); + void createHallOfRecordsScene(int which, uint32 hallOfRecordsInfoId); +}; + +// Scene2201 + +static const NPoint kSsScene2201PuzzleCubePoints[] = { + {305, 305}, {321, 305}, {336, 305}, {305, 319}, + {321, 319}, {336, 319}, {305, 332}, {321, 332}, + {336, 333} +}; + +static const uint32 kSsScene2201PuzzleCubeFileHashes[] = { + 0x88134A44, 0xAA124340, 0xB8124602, 0xA902464C, + 0x890A4244, 0xA8124642, 0xB812C204, 0x381A4A4C +}; + +class AsScene2201CeilingFan : public AnimatedSprite { +public: + AsScene2201CeilingFan(NeverhoodEngine *vm); +}; + +class AsScene2201Door : public AnimatedSprite { +public: + AsScene2201Door(NeverhoodEngine *vm, Klaymen *klaymen, Sprite *ssDoorLight, bool isOpen); +protected: + Klaymen *_klaymen; + Sprite *_ssDoorLight; + bool _isOpen; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stOpenDoor(); + void stCloseDoor(); +}; + +class SsScene2201PuzzleCube : public StaticSprite { +public: + SsScene2201PuzzleCube(NeverhoodEngine *vm, uint32 positionIndex, uint32 cubeIndex); +}; + +class Scene2201 : public Scene { +public: + Scene2201(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2201(); +protected: + NRect _clipRects[2]; + Sprite *_ssDoorLight; + Sprite *_asDoor; + Sprite *_ssDoorButton; + Sprite *_asTape; + bool _isSoundPlaying; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2202PuzzleCube : public StaticSprite { +public: + SsScene2202PuzzleCube(NeverhoodEngine *vm, Scene *parentScene, int16 cubePosition, int16 cubeSymbol); +protected: + Scene *_parentScene; + int16 _cubeSymbol; + int16 _cubePosition; + int16 _newX, _newY; + int16 _xDelta, _yDelta; + int16 _xIncr; + int16 _yIncr; + int16 _errValue; + int16 _counter; + int16 _xFlagPos; + bool _counterDirection; + bool _isMoving; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoveCubeX(); + void suMoveCubeY(); + void moveCube(int16 newCubePosition); + void stopMoving(); +}; + +class Scene2202 : public Scene { +public: + Scene2202(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2202(); +protected: + Sprite *_ssMovingCube; + Sprite *_ssDoneMovingCube; + bool _isCubeMoving; + int16 _movingCubePosition; + int _surfacePriority; + bool _leaveScene; + bool _isSolved; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + int16 getFreeCubePosition(int16 index); + bool testIsSolved(); +}; + +class AsCommonKey : public AnimatedSprite { +public: + AsCommonKey(NeverhoodEngine *vm, Scene *parentScene, int keyIndex, int surfacePriority, int16 x, int16 y); +protected: + Scene *_parentScene; + int _keyIndex; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2203Door : public AnimatedSprite { +public: + AsScene2203Door(NeverhoodEngine *vm, Scene *parentScene, uint doorIndex); +protected: + Scene *_parentScene; + Sprite *_otherDoor; + uint _doorIndex; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void openDoor(); + void closeDoor(); +}; + +class Scene2203 : public Scene { +public: + Scene2203(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2203(); +protected: + Sprite *_asLeftDoor; + Sprite *_asRightDoor; + Sprite *_ssSmallLeftDoor; + Sprite *_ssSmallRightDoor; + Sprite *_asTape; + Sprite *_asKey; + NRect _leftDoorClipRect; + NRect _rightDoorClipRect; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2205DoorFrame : public StaticSprite { +public: + SsScene2205DoorFrame(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2205 : public Scene { +public: + Scene2205(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + SsCommonPressButton *_ssLightSwitch; + Sprite *_ssDoorFrame; + bool _isKlaymenInLight; + bool _isLightOn; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2206DoorSpikes : public StaticSprite { +public: + AsScene2206DoorSpikes(NeverhoodEngine *vm, uint32 fileHash); +protected: + int _deltaIndex; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suOpen(); + void suClose(); +}; + +class AsScene2206Platform : public StaticSprite { +public: + AsScene2206Platform(NeverhoodEngine *vm, uint32 fileHash); +protected: + int16 _yDelta; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoveDown(); +}; + +class SsScene2206TestTube : public StaticSprite { +public: + SsScene2206TestTube(NeverhoodEngine *vm, Scene *parentScene, int surfacePriority, uint32 fileHash); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2206 : public Scene { +public: + Scene2206(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2206(); +protected: + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_asDoorSpikes; + Sprite *_ssButton; + Sprite *_asPlatform; + Sprite *_ssTestTube; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void klaymenInFrontSpikes(); + void klaymenBehindSpikes(); + void readClickedColumn(); +}; + +class AsScene2207Elevator : public AnimatedSprite { +public: + AsScene2207Elevator(NeverhoodEngine *vm, Scene *parentScene); + ~AsScene2207Elevator(); +protected: + Scene *_parentScene; + NPointArray *_pointArray; + int16 _pointIndex; + int16 _destPointIndex, _destPointIndexDelta; + bool _isMoving; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suSetPosition(); + void moveToY(int16 y); +}; + +class AsScene2207Lever : public AnimatedSprite { +public: + AsScene2207Lever(NeverhoodEngine *vm, Scene *parentScene, int16 x, int16 y, int doDeltaX); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stLeverDown(); + void stLeverDownEvent(); + void stLeverUp(); + void stLeverUpEvent(); +}; + +class AsScene2207WallRobotAnimation : public AnimatedSprite { +public: + AsScene2207WallRobotAnimation(NeverhoodEngine *vm, Scene *parentScene); + ~AsScene2207WallRobotAnimation(); +protected: + bool _idle; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stStartAnimation(); + void stStopAnimation(); + void cbStopAnimation(); +}; + +class AsScene2207WallCannonAnimation : public AnimatedSprite { +public: + AsScene2207WallCannonAnimation(NeverhoodEngine *vm); +protected: + bool _idle; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stStartAnimation(); + void stStopAnimation(); + void cbStopAnimation(); +}; + +class SsScene2207Symbol : public StaticSprite { +public: + SsScene2207Symbol(NeverhoodEngine *vm, uint32 fileHash, int index); +}; + +class Scene2207 : public Scene { +public: + Scene2207(NeverhoodEngine *vm, Module *parentModule); +protected: + Sprite *_asElevator; + Sprite *_ssMaskPart1; + Sprite *_ssMaskPart2; + Sprite *_ssMaskPart3; + Sprite *_asTape; + Sprite *_asLever; + Sprite *_asWallRobotAnimation; + Sprite *_asWallCannonAnimation; + Sprite *_ssButton; + int _elevatorSurfacePriority; + bool _klaymenAtElevator; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 handleMessage2(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2208 : public Scene { +public: + Scene2208(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2208(); +protected: + FontSurface *_fontSurface; + BaseSurface *_backgroundSurface; + BaseSurface *_topBackgroundSurface; + BaseSurface *_bottomBackgroundSurface; + TextResource _textResource; + int16 _backgroundScrollY; + int16 _newRowIndex; + int16 _currRowIndex; + int16 _rowScrollY; + int16 _maxRowIndex; + int16 _visibleRowsCount; + Common::Array<const char*> _strings; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void drawRow(int16 rowIndex); +}; + +class Scene2242 : public Scene { +public: + Scene2242(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2242(); +protected: + Sprite *_asTape; + bool _isKlaymenInLight; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void readClickedColumn(); +}; + +class HallOfRecordsScene : public Scene { +public: + HallOfRecordsScene(NeverhoodEngine *vm, Module *parentModule, int which, uint32 hallOfRecordsInfoId); + ~HallOfRecordsScene(); +protected: + HallOfRecordsInfo *_hallOfRecordsInfo; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void readClickedColumn(); +}; + +class Scene2247 : public Scene { +public: + Scene2247(NeverhoodEngine *vm, Module *parentModule, int which); + ~Scene2247(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void readClickedColumn(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2200_H */ diff --git a/engines/neverhood/modules/module2300.cpp b/engines/neverhood/modules/module2300.cpp new file mode 100644 index 0000000000..34eca14bea --- /dev/null +++ b/engines/neverhood/modules/module2300.cpp @@ -0,0 +1,186 @@ +/* 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/module2300.h" +#include "neverhood/navigationscene.h" + +namespace Neverhood { + +static const uint32 kModule2300SoundList[] = { + 0x90805C50, 0x90804450, 0xB4005E60, 0x91835066, + 0x90E14440, 0x90F0D1C3, 0 +}; + +Module2300::Module2300(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _soundVolume(0) { + + _vm->_soundMan->addSoundList(0x1A214010, kModule2300SoundList); + _vm->_soundMan->setSoundListParams(kModule2300SoundList, true, 50, 600, 10, 150); + + _isWallBroken = getGlobalVar(V_WALL_BROKEN) != 0; + + if (_isWallBroken) { + _vm->_soundMan->setSoundVolume(0x90F0D1C3, 0); + _vm->_soundMan->playSoundLooping(0x90F0D1C3); + } else { + _vm->_soundMan->setSoundParams(0x90F0D1C3, false, 0, 0, 0, 0); + } + + _vm->_soundMan->playTwoSounds(0x1A214010, 0x48498E46, 0x50399F64, 0); + _vm->_soundMan->playTwoSounds(0x1A214010, 0x41861371, 0x43A2507F, 0); + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 1) + createScene(2, 0); + else if (which == 2) + createScene(3, 0); + else if (which == 3) + createScene(4, -1); + else if (which == 4) + createScene(1, 3); + else + createScene(0, 1); + +} + +Module2300::~Module2300() { + _vm->_soundMan->deleteGroup(0x1A214010); +} + +void Module2300::createScene(int sceneNum, int which) { + debug("Module2300::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + createNavigationScene(0x004B67B8, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + createNavigationScene(0x004B67E8, which); + if (_isWallBroken) { + _soundVolume = 15; + _vm->_soundMan->setSoundVolume(0x90F0D1C3, 15); + } + break; + case 2: + _vm->gameState().sceneNum = 2; + createNavigationScene(0x004B6878, which); + break; + case 3: + _vm->gameState().sceneNum = 3; + if (getGlobalVar(V_WALL_BROKEN)) + createNavigationScene(0x004B68F0, which); + else { + _vm->_soundMan->setSoundVolume(0x90F0D1C3, _soundVolume); + createNavigationScene(0x004B68A8, which); + if (_isWallBroken) { + _soundVolume = 87; + _vm->_soundMan->setSoundVolume(0x90F0D1C3, 87); + } + } + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->setTwoSoundsPlayFlag(true); + createSmackerScene(0x20080A0B, true, true, false); + break; + case 9999: + createDemoScene(); + break; + } + SetUpdateHandler(&Module2300::updateScene); + _childObject->handleUpdate(); +} + +void Module2300::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(1, 4); + else + leaveModule(0); + break; + case 1: + if (_moduleResult == 1) + createScene(0, 0); + else if (_vm->isDemo()) + createScene(9999, 0); + else if (_moduleResult == 2) + createScene(2, 1); + else if (_moduleResult == 3) + createScene(1, 3); + else if (_moduleResult == 4) + createScene(3, 1); + else if (_moduleResult == 5) + leaveModule(3); + else + leaveModule(4); + break; + case 2: + if (_moduleResult == 1) + leaveModule(1); + else + createScene(1, 5); + break; + case 3: + if (_moduleResult == 1) + leaveModule(2); + else + createScene(1, 1); + break; + case 4: + _vm->_soundMan->setTwoSoundsPlayFlag(false); + createScene(1, 2); + break; + case 9999: + createScene(1, -1); + break; + } + } else { + switch (_sceneNum) { + case 1: + if (_isWallBroken && navigationScene()->isWalkingForward() && navigationScene()->getNavigationIndex() == 4 && + navigationScene()->getFrameNumber() % 2) { + _soundVolume++; + _vm->_soundMan->setSoundVolume(0x90F0D1C3, _soundVolume); + } + if (navigationScene()->isWalkingForward() && navigationScene()->getNavigationIndex() == 0 && + navigationScene()->getFrameNumber() == 50) { + _vm->_soundMan->playTwoSounds(0x1A214010, 0x48498E46, 0x50399F64, 0); + _vm->_soundMan->setSoundVolume(0x48498E46, 70); + _vm->_soundMan->setSoundVolume(0x50399F64, 70); + } + break; + case 3: + if (_isWallBroken && navigationScene()->isWalkingForward() && navigationScene()->getFrameNumber() % 2) { + _soundVolume--; + _vm->_soundMan->setSoundVolume(0x90F0D1C3, _soundVolume); + } + break; + } + } +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2300.h b/engines/neverhood/modules/module2300.h new file mode 100644 index 0000000000..0a1e1d57a4 --- /dev/null +++ b/engines/neverhood/modules/module2300.h @@ -0,0 +1,48 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2300_H +#define NEVERHOOD_MODULES_MODULE2300_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module2300 + +class Module2300 : public Module { +public: + Module2300(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2300(); +protected: + int _sceneNum; + bool _isWallBroken; + int _soundVolume; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2300_H */ diff --git a/engines/neverhood/modules/module2400.cpp b/engines/neverhood/modules/module2400.cpp new file mode 100644 index 0000000000..450812a5f3 --- /dev/null +++ b/engines/neverhood/modules/module2400.cpp @@ -0,0 +1,992 @@ +/* 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/module2400.h" + +namespace Neverhood { + +Module2400::Module2400(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + _vm->_soundMan->addMusic(0x202D1010, 0xB110382D); + + if (which < 0) + createScene(_vm->gameState().sceneNum, _vm->gameState().which); + else + createScene(0, 0); + +} + +Module2400::~Module2400() { + _vm->_soundMan->deleteMusicGroup(0x202D1010); +} + +void Module2400::createScene(int sceneNum, int which) { + debug("Module2400::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _vm->_soundMan->stopMusic(0xB110382D, 0, 0); + _childObject = new Scene2401(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _vm->_soundMan->startMusic(0xB110382D, 0, 2); + _childObject = new Scene2402(_vm, this, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->_soundMan->startMusic(0xB110382D, 0, 0); + _childObject = new Scene2403(_vm, this, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->stopMusic(0xB110382D, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 0); + break; + case 5: + _vm->gameState().sceneNum = 5; + _vm->_soundMan->startMusic(0xB110382D, 0, 2); + _childObject = new Scene2406(_vm, this, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->_soundMan->stopMusic(0xB110382D, 0, 2); + createSmackerScene(0x20D80001, true, true, false); + break; + case 7: + _vm->gameState().sceneNum = 7; + createStaticScene(0x81523218, 0x2321C81D); + break; + case 8: + _vm->gameState().sceneNum = 8; + createStaticScene(0x08100210, 0x00214089); + break; + case 9: + _vm->gameState().sceneNum = 9; + createStaticScene(0x8C020505, 0x205018C8); + break; + } + SetUpdateHandler(&Module2400::updateScene); + _childObject->handleUpdate(); +} + +void Module2400::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(1, 0); + else + leaveModule(0); + break; + case 1: + if (_moduleResult == 1) + createScene(5, 0); + else if (_moduleResult == 2) + createScene(7, -1); + else + createScene(0, 1); + break; + case 2: + if (_moduleResult == 1) + createScene(9, -1); + else if (_moduleResult == 2) + createScene(6, -1); + else + createScene(5, 1); + break; + case 4: + createScene(5, 2); + break; + case 5: + if (_moduleResult == 1) + createScene(2, 0); + else if (_moduleResult == 2) + createScene(4, 0); + else if (_moduleResult == 3) + createScene(8, -1); + else + createScene(1, 1); + break; + case 6: + createScene(2, 2); + break; + case 7: + createScene(1, 2); + break; + case 8: + createScene(5, 3); + break; + case 9: + createScene(2, 1); + break; + } + } +} + +static const NPoint kScene2401Points[] = { + {384, 389}, {406, 389}, {429, 389}, + {453, 389}, {477, 389} +}; + +static const uint32 kScene2401FileHashes1[] = { + 0x02842920, 0x02882920, 0x02902920, + 0x02A02920, 0x02C02920, 0x02002920, + 0x03802920, 0x00802920, 0x06802920, + 0x03842920 +}; + +static const uint32 kScene2401FileHashes2[] = { + 0xD0910020, 0xD0910038, 0xD0910008, + 0xD0910068, 0xD09100A8, 0 +}; + +static const uint32 kScene2401FileHashes3[] = { + 0xD0910020, 0xD0910038, 0xD0910008, + 0xD0910068, 0xD09100A8, 0 +}; + +static const NRect kScene2401Rects[] = { + NRect(369, 331, 394, 389), + NRect(395, 331, 419, 389), + NRect(420, 331, 441, 389), + NRect(442, 331, 464, 389), + NRect(465, 331, 491, 389) +}; + +static const uint32 kAsScene2401WaterSpitFileHashes2[] = { + 0x5C044690, 0x5C644690, 0x5CA44690, + 0x5D244690, 0x5E244690 +}; + +static const uint32 kAsScene2401WaterSpitFileHashes1[] = { + 0xF4418408, 0xF4418808, 0xF4419008, + 0xF441A008, 0xCD4F8411 +}; + +AsScene2401WaterSpit::AsScene2401WaterSpit(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200) { + + _x = 240; + _y = 447; + createSurface(100, 146, 74); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2401WaterSpit::handleMessage); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); +} + +uint32 AsScene2401WaterSpit::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x120A0013) + playSound(0, kAsScene2401WaterSpitFileHashes1[_soundIndex]); + break; + case 0x2000: + _x = 240; + _y = 447; + _soundIndex = getSubVar(VA_CURR_WATER_PIPES_LEVEL, param.asInteger()); + startAnimation(kAsScene2401WaterSpitFileHashes2[param.asInteger()], 0, -1); + setVisible(true); + playSound(0, 0x48640244); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +AsScene2401FlowingWater::AsScene2401FlowingWater(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200), _isWaterFlowing(false) { + + _x = 88; + _y = 421; + createSurface1(0x10203116, 100); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2401FlowingWater::handleMessage); +} + +AsScene2401FlowingWater::~AsScene2401FlowingWater() { + _vm->_soundMan->deleteSoundGroup(0x40F11C09); +} + +uint32 AsScene2401FlowingWater::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (_isWaterFlowing && param.asInteger() == 0x02421405) + startAnimationByHash(0x10203116, 0x01084280, 0); + break; + case 0x2002: + if (!_isWaterFlowing) { + _vm->_soundMan->addSound(0x40F11C09, 0x980C1420); + _vm->_soundMan->playSoundLooping(0x980C1420); + startAnimation(0x10203116, 0, -1); + setVisible(true); + _isWaterFlowing = true; + } + break; + case 0x2003: + _vm->_soundMan->deleteSound(0x980C1420); + _isWaterFlowing = false; + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +AsScene2401WaterFlushing::AsScene2401WaterFlushing(NeverhoodEngine *vm, int16 x, int16 y) + : AnimatedSprite(vm, 1200), _countdown(0), _flushLoopCount(0) { + + _x = x; + _y = y; + createSurface1(0xB8596884, 100); + setVisible(false); + SetUpdateHandler(&AsScene2401WaterFlushing::update); + SetMessageHandler(&AsScene2401WaterFlushing::handleMessage); +} + +void AsScene2401WaterFlushing::update() { + if (_countdown != 0 && (--_countdown) == 0) { + setDoDeltaX(_vm->_rnd->getRandomNumber(1)); + startAnimation(0xB8596884, 0, -1); + setVisible(true); + } + AnimatedSprite::update(); +} + +uint32 AsScene2401WaterFlushing::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (_flushLoopCount > 0 && param.asInteger() == 0x02421405) { + startAnimationByHash(0xB8596884, 0x01084280, 0); + _flushLoopCount--; + } + break; + case 0x2002: + if (param.asInteger() > 0) { + _flushLoopCount = param.asInteger() - 1; + _countdown = _vm->_rnd->getRandomNumber(3) + 1; + } + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +AsScene2401Door::AsScene2401Door(NeverhoodEngine *vm, bool isOpen) + : AnimatedSprite(vm, 1100), _countdown(0), _isOpen(isOpen) { + + _x = 320; + _y = 240; + createSurface1(0x44687810, 100); + _newStickFrameIndex = STICK_LAST_FRAME; + if (_isOpen) { + stopAnimation(); + setVisible(false); + _countdown = 48; + } else { + startAnimation(0x44687810, 0, -1); + _newStickFrameIndex = 0; + } + SetUpdateHandler(&AsScene2401Door::update); + SetMessageHandler(&AsScene2401Door::handleMessage); +} + +void AsScene2401Door::update() { + if (_isOpen && _countdown != 0 && (--_countdown) == 0) { + _isOpen = false; + setVisible(true); + startAnimation(0x44687810, -1, -1); + _newStickFrameIndex = 0; + _playBackwards = true; + playSound(0, calcHash("fxDoorClose38")); + } + AnimatedSprite::update(); +} + +uint32 AsScene2401Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2004: + if (_isOpen) + _countdown = 168; + messageResult = _isOpen ? 1 : 0; + break; + case 0x3002: + gotoNextState(); + break; + case 0x4808: + if (!_isOpen) { + _countdown = 168; + _isOpen = true; + setVisible(true); + startAnimation(0x44687810, 0, -1); + playSound(0, calcHash("fxDoorOpen38")); + NextState(&AsScene2401Door::stDoorOpenFinished); + } + break; + } + return messageResult; +} + +void AsScene2401Door::stDoorOpenFinished() { + stopAnimation(); + setVisible(false); +} + +Scene2401::Scene2401(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _countdown1(0), _countdown2(0), _unkFlag(false), + _soundToggle(false), _asWaterSpitIndex(0) { + + _vm->gameModule()->initWaterPipesPuzzle(); + + SetMessageHandler(&Scene2401::handleMessage); + SetUpdateHandler(&Scene2401::update); + + setRectList(0x004B3140); + setBackground(0x8C030206); + setPalette(0x8C030206); + addEntity(_palette); + _palette->addBasePalette(0x8C030206, 0, 256, 0); + _palette->addPalette(0x91D3A391, 0, 65, 0); + insertScreenMouse(0x302028C8); + + _sprite1 = insertStaticSprite(0x2E068A23, 200); + insertStaticSprite(0x401410A6, 200); + _asFlowingWater = insertSprite<AsScene2401FlowingWater>(); + insertStaticSprite(0x90C0A4B4, 200); + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x0092916A, 100, 0); + _ssFloorButton = insertSprite<SsCommonFloorButton>(this, 0x28001120, 0x00911068, 100, 0); + + for (uint i = 0; i < 5; i++) + _asWaterFlushing[i] = insertSprite<AsScene2401WaterFlushing>(kScene2401Points[i].x, kScene2401Points[i].y); + + for (uint i = 0; i < 10; i++) { + _ssWaterPipes[i] = insertStaticSprite(kScene2401FileHashes1[i], 300); + _ssWaterPipes[i]->setVisible(false); + } + + _asWaterSpit[0] = insertSprite<AsScene2401WaterSpit>(); + _asWaterSpit[1] = insertSprite<AsScene2401WaterSpit>(); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2401>(200, 447); + setMessageList(0x004B2F70); + _asDoor = insertSprite<AsScene2401Door>(false); + } else if (which == 1) { + // Klaymen entering from the back + insertKlaymen<KmScene2401>(280, 413); + setMessageList(0x004B2F80); + _palette->addBasePalette(0xB103B604, 0, 65, 0); + _palette->addPalette(0xB103B604, 0, 65, 0); + _asDoor = insertSprite<AsScene2401Door>(true); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene2401>(-20, 447); + setMessageList(0x004B2F78); + _asDoor = insertSprite<AsScene2401Door>(false); + } + +} + +void Scene2401::update() { + + if (_countdown1 != 0 && (--_countdown1) == 0) { + if (_pipeStatus > 0 && _pipeStatus <= 10) + _ssWaterPipes[_pipeStatus - 1]->setVisible(false); + if (_pipeStatus >= 10) { + bool puzzleSolved = true, waterInside = false; + for (uint pipeIndex = 0; pipeIndex < 5; pipeIndex++) { + if (getSubVar(VA_CURR_WATER_PIPES_LEVEL, pipeIndex) != getSubVar(VA_GOOD_WATER_PIPES_LEVEL, pipeIndex)) + puzzleSolved = false; + if (getSubVar(VA_CURR_WATER_PIPES_LEVEL, pipeIndex) != 0) + waterInside = true; + } + if (puzzleSolved) { + setGlobalVar(V_NOTES_DOOR_UNLOCKED, 1); + setGlobalVar(V_NOTES_PUZZLE_SOLVED, 1); + sendMessage(_asDoor, 0x4808, 0); + } else if (waterInside) { + playPipeSound(0xD0431020); + for (uint i = 0; i < 5; i++) { + sendMessage(_asWaterFlushing[i], 0x2002, getSubVar(VA_CURR_WATER_PIPES_LEVEL, i)); + setSubVar(VA_CURR_WATER_PIPES_LEVEL, i, 0); + } + } + } else if (_pipeStatus >= 5) { + _ssWaterPipes[_pipeStatus]->setVisible(true); + _countdown1 = 8; + playPipeSound(kScene2401FileHashes3[getSubVar(VA_CURR_WATER_PIPES_LEVEL, _pipeStatus - 5)]); + } else { + _ssWaterPipes[_pipeStatus]->setVisible(true); + _countdown1 = _pipeStatus == 4 ? 16 : 8; + playPipeSound(kScene2401FileHashes3[getSubVar(VA_GOOD_WATER_PIPES_LEVEL, _pipeStatus)]); + } + _pipeStatus++; + } + + if (_countdown2 != 0 && (--_countdown2) == 0) + sendMessage(_asFlowingWater, 0x2003, 0); + + Scene::update(); + +} + +uint32 Scene2401::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssButton); + else if (param.asInteger() == 0x02144CB1) + sendEntityMessage(_klaymen, 0x1014, _ssFloorButton); + else if (param.asInteger() == 0x11C40840) { + if (getGlobalVar(V_NOTES_DOOR_UNLOCKED) && sendMessage(_asDoor, 0x2004, 0)) + setMessageList(0x004B3090); + else + setMessageList(0x004B30B0); + } else if (param.asInteger() == 0x412722C0) { + if (_countdown2 > 0 && getGlobalVar(V_HAS_TEST_TUBE)) { + _countdown2 = 144; + setMessageList(0x004B3020); + } else + setMessageList(0x004B3050); + } else if (param.asInteger() == 0x21142050) { + if (_unkFlag && _countdown1 == 0 && !getGlobalVar(V_NOTES_PUZZLE_SOLVED)) + setMessageList(0x004B2FA8); + else + setMessageList(0x004B2FC8); + } else if (param.asInteger() == 0x87441031) + setSurfacePriority(_sprite1->getSurface(), 1100); + else if (param.asInteger() == 0x80C40322) { + setSurfacePriority(_sprite1->getSurface(), 200); + cancelMessageList(); + _unkFlag = true; + } else if (param.asInteger() == 0x09C4B40A && _countdown2 > 12) + _countdown2 = 12; + break; + case 0x2000: + messageResult = 0; + for (uint32 i = 0; i < 5; i++) + if (kScene2401Rects[i].contains(_mouseClickPos.x, _mouseClickPos.y)) { + messageResult = i; + break; + } + break; + case 0x2001: + sendMessage(_asWaterSpit[_asWaterSpitIndex], 0x2000, param.asInteger()); + _asWaterSpitIndex = (_asWaterSpitIndex + 1) & 1; + incSubVar(VA_CURR_WATER_PIPES_LEVEL, param.asInteger(), 1); + if (getSubVar(VA_CURR_WATER_PIPES_LEVEL, param.asInteger()) >= 5) + setSubVar(VA_CURR_WATER_PIPES_LEVEL, param.asInteger(), 4); + break; + case 0x480B: + if (sender == _ssButton) { + _pipeStatus = 0; + _countdown1 = 8; + } else if (sender == _ssFloorButton && getGlobalVar(V_WATER_RUNNING)) { + _countdown2 = 144; + sendMessage(_asFlowingWater, 0x2002, 0); + playSound(0, 0xE1130324); + } + break; + case 0x482A: + _palette->addBasePalette(0xB103B604, 0, 65, 0); + _palette->startFadeToPalette(12); + break; + case 0x482B: + _palette->addBasePalette(0x91D3A391, 0, 65, 0); + _palette->startFadeToPalette(12); + break; + } + return messageResult; +} + +void Scene2401::playPipeSound(uint32 fileHash) { + playSound(_soundToggle ? 0 : 1, fileHash); + _soundToggle = !_soundToggle; +} + +static const uint32 kScene2402FileHashes[] = { + 0xD0910020, 0xD0910038, 0xD0910008, + 0xD0910068, 0xD09100A8 +}; + +AsScene2402Door::AsScene2402Door(NeverhoodEngine *vm, Scene *parentScene, bool isOpen) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _isOpen(isOpen), _countdown(0) { + + _x = 320; + _y = 240; + createSurface1(0x80495831, 100); + if (_isOpen) { + startAnimation(0x80495831, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + _countdown = 48; + } else { + stopAnimation(); + setVisible(false); + } + SetUpdateHandler(&AsScene2402Door::update); + SetMessageHandler(&AsScene2402Door::handleMessage); +} + +void AsScene2402Door::update() { + if (_isOpen && _countdown != 0 && (--_countdown) == 0) { + _isOpen = false; + setVisible(true); + startAnimation(0x80495831, -1, -1); + _playBackwards = true; + playSound(0, calcHash("fxDoorClose38")); + NextState(&AsScene2402Door::stDoorClosingFinished); + } + AnimatedSprite::update(); +} + +uint32 AsScene2402Door::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (_isOpen) + _countdown = 144; + messageResult = _isOpen ? 1 : 0; + break; + case 0x3002: + gotoNextState(); + break; + case 0x4808: + _countdown = 144; + _isOpen = true; + setVisible(true); + startAnimation(0x80495831, 0, -1); + _newStickFrameIndex = STICK_LAST_FRAME; + playSound(0, calcHash("fxDoorOpen38")); + break; + } + return messageResult; +} + +void AsScene2402Door::stDoorClosingFinished() { + sendMessage(_parentScene, 0x2001, 0); + setVisible(false); +} + +AsScene2402TV::AsScene2402TV(NeverhoodEngine *vm, Klaymen *klaymen) + : AnimatedSprite(vm, 1100), _klaymen(klaymen), _countdown1(0), _countdown2(0) { + + _x = 260; + _y = 210; + createSurface(100, 127, 90); + setDoDeltaX(1); + SetMessageHandler(&Sprite::handleMessage); + if (!getGlobalVar(V_TV_JOKE_TOLD)) { + loadSound(0, 0x58208810); + _countdown1 = 48; + startAnimation(0x4919397A, 0, -1); + _newStickFrameIndex = 0; + SetUpdateHandler(&AsScene2402TV::upWait); + } else { + int16 frameIndex; + if (_klaymen->getX() > 320) + _currFrameIndex = 29; + frameIndex = CLIP<int16>((_klaymen->getX() - _x + 150) / 10, 0, 29); + startAnimation(0x050A0103, frameIndex, -1); + _newStickFrameIndex = frameIndex; + _countdown1 = 0; + SetUpdateHandler(&AsScene2402TV::upFocusKlaymen); + } +} + +AsScene2402TV::~AsScene2402TV() { + _vm->_soundMan->deleteSoundGroup(0x01520123); +} + +void AsScene2402TV::upWait() { + if (_countdown1 != 0 && (--_countdown1) == 0) { + startAnimation(0x4919397A, 0, -1); + SetMessageHandler(&AsScene2402TV::hmJoke); + NextState(&AsScene2402TV::stJokeFinished); + } + AnimatedSprite::update(); +} + +void AsScene2402TV::upFocusKlaymen() { + int16 frameIndex = CLIP<int16>((_klaymen->getX() - _x + 150) / 10, 0, 29); + if (frameIndex != _currFrameIndex) { + if (frameIndex > _currFrameIndex) + _currFrameIndex++; + else if (frameIndex < _currFrameIndex) + _currFrameIndex--; + startAnimation(0x050A0103, _currFrameIndex, -1); + _newStickFrameIndex = _currFrameIndex; + if (_countdown2 == 0) { + _vm->_soundMan->addSound(0x01520123, 0xC42D4528); + _vm->_soundMan->playSoundLooping(0xC42D4528); + } + _countdown2 = 5; + } else if (_countdown2 != 0 && (--_countdown2 == 0)) + _vm->_soundMan->deleteSound(0xC42D4528); + AnimatedSprite::update(); +} + +void AsScene2402TV::stJokeFinished() { + setGlobalVar(V_TV_JOKE_TOLD, 1); + startAnimation(0x050A0103, 0, -1); + _newStickFrameIndex = 0; + SetUpdateHandler(&AsScene2402TV::upFocusKlaymen); +} + +uint32 AsScene2402TV::hmJoke(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x431EA0B0) + playSound(0); + break; + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +Scene2402::Scene2402(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _countdown(0), _soundToggle(false) { + + Sprite *tempSprite; + + SetMessageHandler(&Scene2402::handleMessage); + SetUpdateHandler(&Scene2402::update); + + setRectList(0x004AF900); + setBackground(0x81660220); + setPalette(0x81660220); + insertScreenMouse(0x6022481E); + _asTape = insertSprite<AsScene1201Tape>(this, 9, 1100, 286, 409, 0x9148A011); + addCollisionSprite(_asTape); + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x15288120, 100, 0); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2402>(198, 404); + setMessageList(0x004AF7C8); + } else if (which == 1) { + // Klaymen entering from the right + insertKlaymen<KmScene2402>(660, 404); + setMessageList(0x004AF7D8); + } else if (which == 2) { + // Klaymen returning from looking through the window + insertKlaymen<KmScene2402>(409, 404); + _klaymen->setDoDeltaX(getGlobalVar(V_KLAYMEN_IS_DELTA_X) ? 1 : 0); + setMessageList(0x004AF888); + } else { + // Klaymen entering from the left + insertKlaymen<KmScene2402>(0, 404); + setMessageList(0x004AF7D0); + } + + tempSprite = insertStaticSprite(0x081A60A8, 1100); + _ssDoorFrame = (StaticSprite*)insertStaticSprite(0x406C0AE0, 1100); + _klaymen->setClipRect(_ssDoorFrame->getDrawRect().x, 0, 639, tempSprite->getDrawRect().y2()); + _asDoor = insertSprite<AsScene2402Door>(this, which == 0); + insertSprite<AsScene2402TV>(_klaymen); + insertStaticSprite(0x3A01A020, 200); + +} + +Scene2402::~Scene2402() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +void Scene2402::update() { + if (_countdown != 0 && (--_countdown) == 0) { + if (_pipeStatus >= 10) { + sendMessage(_asDoor, 0x4808, 0); + _ssDoorFrame->loadSprite(0x00B415E0, kSLFDefDrawOffset | kSLFDefPosition); + } else if (_pipeStatus >= 5) { + _countdown = 8; + playPipeSound(kScene2402FileHashes[getSubVar(VA_CURR_WATER_PIPES_LEVEL, _pipeStatus - 5)]); + } else { + _countdown = _pipeStatus == 4 ? 16 : 8; + playPipeSound(kScene2402FileHashes[getSubVar(VA_GOOD_WATER_PIPES_LEVEL, _pipeStatus)]); + } + _pipeStatus++; + } + Scene::update(); +} + +uint32 Scene2402::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x402064D8) + sendEntityMessage(_klaymen, 0x1014, _ssButton); + else if (param.asInteger() == 0x01C66840) { + if (sendMessage(_asDoor, 0x2000, 0)) + setMessageList(0x004AF800); + else + setMessageList(0x004AF818); + } + break; + case 0x2001: + _ssDoorFrame->loadSprite(0x406C0AE0, kSLFDefDrawOffset | kSLFDefPosition); + break; + case 0x480B: + if (sender == _ssButton) { + _pipeStatus = 0; + _countdown = 8; + } + break; + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004AF890); + } + break; + } + return messageResult; +} + +void Scene2402::playPipeSound(uint32 fileHash) { + playSound(_soundToggle ? 0 : 1, fileHash); + _soundToggle = !_soundToggle; +} + +Scene2403::Scene2403(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite1, *tempSprite2, *tempSprite3; + + SetMessageHandler(&Scene2403::handleMessage); + setBackground(0x0C05060C); + setPalette(0x0C05060C); + _palette->addPalette(0x414364B0, 0, 65, 0); + insertScreenMouse(0x506080C8); + _asTape = insertSprite<AsScene1201Tape>(this, 2, 1100, 480, 454, 0x9148A011); + addCollisionSprite(_asTape); + _asLightCord = insertSprite<AsScene2803LightCord>(this, 0xA1095A10, 0x836D3813, 368, 200); + _asLightCord->setClipRect(0, 25, 640, 480); + + if (which < 0) { + // Restoring game + _isClimbingLadder = false; + insertKlaymen<KmScene2403>(220, 449); + setMessageList(0x004B5C98); + setRectList(0x004B5E18); + } else if (which == 1) { + // Klaymen returning from looking through the window + _isClimbingLadder = false; + insertKlaymen<KmScene2403>(433, 449); + setMessageList(0x004B5D70); + setRectList(0x004B5E18); + } else if (which == 2) { + // Klaymen standing around after the critter video + _isClimbingLadder = false; + insertKlaymen<KmScene2403>(440, 449); + _klaymen->setDoDeltaX(1); + setMessageList(0x004B5C98); + setRectList(0x004B5E18); + } else { + // Klaymen coming up from ladder + _isClimbingLadder = true; + insertKlaymen<KmScene2403>(122, 599); + setMessageList(0x004B5CA0); + setRectList(0x004B5E28); + } + + _ssButton = insertSprite<SsCommonButtonSprite>(this, 0x3130B0EB, 100, 0); + tempSprite1 = insertStaticSprite(0x20C24220, 1100); + tempSprite2 = insertStaticSprite(0x03080900, 1300); + tempSprite3 = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + tempSprite3->setClipRect(tempSprite1->getDrawRect().x, 0, 640, tempSprite2->getDrawRect().y2()); + _klaymen->setClipRect(tempSprite1->getDrawRect().x, 0, 640, tempSprite2->getDrawRect().y2()); + loadSound(1, calcHash("fxFogHornSoft")); +} + +uint32 Scene2403::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x040424D0) + sendEntityMessage(_klaymen, 0x1014, _ssButton); + else if (param.asInteger() == 0x180CE614) + sendEntityMessage(_klaymen, 0x1014, _asLightCord); + break; + case 0x2000: + _isClimbingLadder = true; + setRectList(0x004B5E28); + break; + case 0x2001: + _isClimbingLadder = false; + setRectList(0x004B5E18); + break; + case 0x480B: + if (sender == _ssButton) { + if (getSubVar(VA_LOCKS_DISABLED, 0x304008D2)) { + setSubVar(VA_LOCKS_DISABLED, 0x304008D2, 0); + playSound(0, calcHash("fx3LocksDisable")); + } else { + setSubVar(VA_LOCKS_DISABLED, 0x304008D2, 1); + playSound(1); + } + } + break; + case 0x480F: + if (sender == _asLightCord) + leaveScene(2); + break; + case 0x4826: + if (sender == _asTape && !_isClimbingLadder) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B5D98); + } + break; + } + return messageResult; +} + +Scene2406::Scene2406(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite1, *tempSprite2; + + if (getGlobalVar(V_HAS_FINAL_KEY) && getGlobalVar(V_KEY3_LOCATION) == 0) + setGlobalVar(V_KEY3_LOCATION, 2); + + SetMessageHandler(&Scene2406::handleMessage); + + setRectList(0x004B78C8); + insertScreenMouse(0xB03001A8); + + if (getGlobalVar(V_KEY3_LOCATION) == 2) { + _asKey = insertSprite<AsCommonKey>(this, 2, 1100, 560, 409); + addCollisionSprite(_asKey); + } + + _asTape = insertSprite<AsScene1201Tape>(this, 5, 1100, 456, 409, 0x9148A011); + addCollisionSprite(_asTape); + tempSprite2 = insertStaticSprite(0x19625293, 1100); + _clipRects[0].x1 = 0; + _clipRects[0].y1 = 0; + _clipRects[0].x2 = tempSprite2->getDrawRect().x2(); + _clipRects[0].y2 = 480; + + if (getGlobalVar(V_SPIKES_RETRACTED)) { + setBackground(0x1A0B0304); + setPalette(0x1A0B0304); + tempSprite1 = insertStaticSprite(0x32923922, 1100); + } else { + setBackground(0x0A038595); + setPalette(0x0A038595); + tempSprite1 = insertStaticSprite(0x1712112A, 1100); + } + + tempSprite2 = insertStaticSprite(0x22300924, 1300); + _clipRects[1].x1 = tempSprite1->getDrawRect().x; + _clipRects[1].y1 = tempSprite2->getDrawRect().y; + _clipRects[1].x2 = 640; + _clipRects[1].y2 = 480; + + if (which < 0) { + // Restoring game + _isClimbingLadder = false; + insertKlaymen<KmScene2406>(307, 404, _clipRects, 2); + setMessageList(0x004B76C8); + setRectList(0x004B78C8); + } else if (which == 1) { + // Klaymen coming down the ladder + _isClimbingLadder = true; + insertKlaymen<KmScene2406>(253, -16, _clipRects, 2); + setMessageList(0x004B76D8); + setRectList(0x004B78D8); + } else if (which == 2) { + // Klaymen returning from the diskplayer + _isClimbingLadder = false; + insertKlaymen<KmScene2406>(480, 404, _clipRects, 2); + setMessageList(0x004B77C0); + setRectList(0x004B78C8); + } else if (which == 3) { + // Klaymen returning from looking through the window + _isClimbingLadder = false; + insertKlaymen<KmScene2406>(387, 404, _clipRects, 2); + setMessageList(0x004B7810); + setRectList(0x004B78C8); + } else { + // Klaymen entering from the left + _isClimbingLadder = false; + insertKlaymen<KmScene2406>(0, 404, _clipRects, 2); + setMessageList(0x004B76D0); + setRectList(0x004B78C8); + } + + tempSprite2 = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + tempSprite2->setClipRect(_clipRects[1]); + +} + +uint32 Scene2406::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x41062804) { + if (getGlobalVar(V_SPIKES_RETRACTED)) + setMessageList(0x004B7758); + else + setMessageList(0x004B7738); + } + break; + case 0x2000: + _isClimbingLadder = true; + setRectList(0x004B78D8); + break; + case 0x2001: + _isClimbingLadder = false; + setRectList(0x004B78C8); + break; + case 0x4826: + if (sender == _asTape && !_isClimbingLadder) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B77C8); + } else if (sender == _asKey && !_isClimbingLadder) { + sendEntityMessage(_klaymen, 0x1014, _asKey); + setMessageList(0x004B77D8); + } + break; + } + return messageResult; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2400.h b/engines/neverhood/modules/module2400.h new file mode 100644 index 0000000000..b50fff91c4 --- /dev/null +++ b/engines/neverhood/modules/module2400.h @@ -0,0 +1,182 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2400_H +#define NEVERHOOD_MODULES_MODULE2400_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/gamemodule.h" +#include "neverhood/modules/module1000.h" +#include "neverhood/modules/module1100.h" +#include "neverhood/modules/module1200.h" +#include "neverhood/modules/module2100.h" +#include "neverhood/modules/module2200.h" +#include "neverhood/modules/module2800.h" +#include "neverhood/diskplayerscene.h" + +namespace Neverhood { + +// Module2400 + +class Module2400 : public Module { +public: + Module2400(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2400(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2401WaterSpit : public AnimatedSprite { +public: + AsScene2401WaterSpit(NeverhoodEngine *vm); +protected: + int _soundIndex; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2401FlowingWater : public AnimatedSprite { +public: + AsScene2401FlowingWater(NeverhoodEngine *vm); + virtual ~AsScene2401FlowingWater(); +protected: + bool _isWaterFlowing; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2401WaterFlushing : public AnimatedSprite { +public: + AsScene2401WaterFlushing(NeverhoodEngine *vm, int16 x, int16 y); +protected: + int _countdown; + int _flushLoopCount; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2401Door : public AnimatedSprite { +public: + AsScene2401Door(NeverhoodEngine *vm, bool isOpen); +protected: + int _countdown; + bool _isOpen; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stDoorOpenFinished(); +}; + +class Scene2401 : public Scene { +public: + Scene2401(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + Sprite *_asFlowingWater; + Sprite *_ssButton; + Sprite *_ssFloorButton; + Sprite *_asWaterSpit[2]; + Sprite *_ssWaterPipes[10]; + Sprite *_asWaterFlushing[5]; + Sprite *_asDoor; + bool _soundToggle; + bool _unkFlag; + int _countdown1; + int _countdown2; + int _pipeStatus; + int _asWaterSpitIndex; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void playPipeSound(uint32 fileHash); +}; + +class AsScene2402Door : public AnimatedSprite { +public: + AsScene2402Door(NeverhoodEngine *vm, Scene *parentScene, bool isOpen); +protected: + Scene *_parentScene; + int _countdown; + bool _isOpen; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void stDoorClosingFinished(); +}; + +class AsScene2402TV : public AnimatedSprite { +public: + AsScene2402TV(NeverhoodEngine *vm, Klaymen *klaymen); + virtual ~AsScene2402TV(); +protected: + Klaymen *_klaymen; + int _countdown1; + int _countdown2; + void upWait(); + void upFocusKlaymen(); + void stJokeFinished(); + uint32 hmJoke(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2402 : public Scene { +public: + Scene2402(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene2402(); +protected: + Sprite *_asDoor; + Sprite *_ssButton; + Sprite *_asTape; + StaticSprite *_ssDoorFrame; + int _pipeStatus; + int _countdown; + bool _soundToggle; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void playPipeSound(uint32 fileHash); +}; + +class Scene2403 : public Scene { +public: + Scene2403(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_ssButton; + Sprite *_asTape; + Sprite *_asKey; + Sprite *_asLightCord; + bool _isClimbingLadder; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2406 : public Scene { +public: + Scene2406(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_asTape; + Sprite *_asKey; + NRect _clipRects[2]; + bool _isClimbingLadder; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2400_H */ diff --git a/engines/neverhood/modules/module2500.cpp b/engines/neverhood/modules/module2500.cpp new file mode 100644 index 0000000000..a997b5aab1 --- /dev/null +++ b/engines/neverhood/modules/module2500.cpp @@ -0,0 +1,546 @@ +/* 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/module2500.h" +#include "neverhood/modules/module1600.h" + +namespace Neverhood { + +static const uint32 kScene2505StaticSprites[] = { + 0x4000A226, 0 +}; + +static const NRect kScene2505ClipRect = NRect(0, 0, 564, 480); + +static const uint32 kScene2506StaticSprites[] = { + 0x4027AF02, 0 +}; + +static const NRect kScene2506ClipRect = NRect(0, 0, 640, 441); + +static const uint32 kScene2508StaticSprites1[] = { + 0x2F08E610, 0xD844E6A0, 0 +}; + +static const NRect kScene2508ClipRect1 = NRect(0, 0, 594, 448); + +static const uint32 kScene2508StaticSprites2[] = { + 0x2F08E610, 0 +}; + +static const NRect kScene2508ClipRect2 = NRect(0, 0, 594, 448); + +Module2500::Module2500(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _soundIndex(0) { + + _vm->_soundMan->addMusic(0x29220120, 0x05343184); + _vm->_soundMan->startMusic(0x05343184, 0, 0); + SetMessageHandler(&Module2500::handleMessage); + + if (which < 0) + createScene(_vm->gameState().sceneNum, _vm->gameState().which); + else + createScene(0, 0); + + loadSound(0, 0x00880CCC); + loadSound(1, 0x00880CC0); + loadSound(2, 0x00880CCC); + loadSound(3, 0x00880CC0); + +} + +Module2500::~Module2500() { + _vm->_soundMan->deleteMusicGroup(0x29220120); +} + +void Module2500::createScene(int sceneNum, int which) { + debug("Module2500::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _childObject = new Scene2501(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _vm->gameState().which = which; + createScene2704(which, 0x004B01B8, 220); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->gameState().which = which; + if (getGlobalVar(V_WORLDS_JOINED)) + createScene2704(which, 0x004B01E0, 150); + else + createScene2704(which, 0x004B0208, 150); + break; + case 3: + _vm->gameState().sceneNum = 3; + _childObject = new Scene2504(_vm, this, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->gameState().which = which; + createScene2704(which, 0x004B0230, 150, kScene2505StaticSprites, &kScene2505ClipRect); + break; + case 5: + setGlobalVar(V_CAR_DELTA_X, 1); + _vm->gameState().sceneNum = 5; + _vm->gameState().which = which; + createScene2704(which, 0x004B0268, 150, kScene2506StaticSprites, &kScene2506ClipRect); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->gameState().which = which; + createScene2704(which, 0x004B02A0, 150); + break; + case 7: + _vm->gameState().sceneNum = 7; + _vm->gameState().which = which; + if (getGlobalVar(V_ENTRANCE_OPEN)) + createScene2704(which, 0x004B02C8, 150, kScene2508StaticSprites1, &kScene2508ClipRect1); + else + createScene2704(which, 0x004B02C8, 150, kScene2508StaticSprites2, &kScene2508ClipRect2); + break; + case 8: + _vm->gameState().sceneNum = 8; + _childObject = new Scene1608(_vm, this, which); + break; + case 9: + _vm->gameState().sceneNum = 9; + if (getGlobalVar(V_ENTRANCE_OPEN)) + createStaticScene(0xC62A0645, 0xA0641C6A); + else + createStaticScene(0x7A343546, 0x435427AB); + break; + } + SetUpdateHandler(&Module2500::updateScene); + _childObject->handleUpdate(); +} + +void Module2500::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: + if (_moduleResult == 1) + createScene(3, -1); + else + createScene(0, 2); + break; + case 2: + if (_moduleResult == 1) + createScene(4, 0); + else + createScene(0, 1); + break; + case 3: + createScene(1, 1); + break; + case 4: + if (_moduleResult == 1) + createScene(5, 0); + else + createScene(2, 1); + break; + case 5: + if (_moduleResult == 1) + createScene(6, 0); + else + createScene(4, 1); + break; + case 6: + if (_moduleResult == 1) + createScene(7, 0); + else + createScene(5, 1); + break; + case 7: + if (_moduleResult == 1) + createScene(8, 1); + else + createScene(6, 1); + break; + case 8: + if (_moduleResult == 2) + createScene(9, -1); + else + createScene(7, 1); + break; + case 9: + createScene(8, 2); + break; + } + } +} + +uint32 Module2500::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Module::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x200D: + playSound(_soundIndex); + _soundIndex++; + if (_soundIndex >= 4) + _soundIndex = 0; + break; + } + return messageResult; +} + +void Module2500::createScene2704(int which, uint32 sceneInfoId, int16 value, const uint32 *staticSprites, const NRect *clipRect) { + _childObject = new Scene2704(_vm, this, which, sceneInfoId, value, staticSprites, clipRect); +} + +Scene2501::Scene2501(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B2628)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B264C)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B2670)); + + setGlobalVar(V_CAR_DELTA_X, 1); + SetUpdateHandler(&Scene2501::update); + setBackground(0x1B8E8115); + setPalette(0x1B8E8115); + _palette->addPalette(0x00128842, 65, 31, 65); + _palette->addPalette("paKlayRed", 0, 64, 0); + insertScreenMouse(0xE81111B0); + + _ssTrackShadowBackground = createSprite<SsCommonTrackShadowBackground>(0x99BE9015); // Don't add this to the sprite list + addEntity(_ssTrackShadowBackground); + _asCar = createSprite<AsCommonCar>(this, 211, 400); // Create but don't add to the sprite list yet + _asIdleCarLower = insertSprite<AsCommonIdleCarLower>(211, 400); + _asIdleCarFull = insertSprite<AsCommonIdleCarFull>(211, 400); + insertStaticSprite(0xC42AC521, 1500); + + if (which < 0) { + // Restoring game + insertKlaymen<KmScene2501>(162, 393); + _kmScene2501 = _klaymen; + _klaymenInCar = false; + setMessageList(0x004B2538); + setRectList(0x004B2608); + SetMessageHandler(&Scene2501::handleMessage); + SetUpdateHandler(&Scene2501::update); + sendMessage(_asCar, 0x2009, 0); + _asCar->setVisible(false); + _currTrackIndex = 0; + } else if (which == 1 || which == 2) { + // 1: Klaymen entering riding the car on the left track + // 2: Klaymen entering riding the car on the bottom track + addSprite(_asCar); + _kmScene2501 = (Klaymen*)new KmScene2501(_vm, this, 275, 393); + _klaymenInCar = true; + sendMessage(_kmScene2501, 0x2000, 1); + _kmScene2501->setDoDeltaX(1); + SetMessageHandler(&Scene2501::hmRidingCar); + SetUpdateHandler(&Scene2501::upRidingCar); + _asIdleCarLower->setVisible(false); + _asIdleCarFull->setVisible(false); + _currTrackIndex = which; + } else { + // Klaymen entering the car + insertKlaymen<KmScene2501>(162, 393); + _kmScene2501 = _klaymen; + _klaymenInCar = false; + setMessageList(0x004B2538); + setRectList(0x004B2608); + SetMessageHandler(&Scene2501::handleMessage); + SetUpdateHandler(&Scene2501::update); + sendMessage(_asCar, 0x2009, 0); + _asCar->setVisible(false); + _currTrackIndex = 0; + } + + _asCarShadow = insertSprite<AsCommonCarShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarTrackShadow = insertSprite<AsCommonCarTrackShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarConnectorShadow = insertSprite<AsCommonCarConnectorShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + insertSprite<AsCommonCarConnector>(_asCar); + + _newTrackIndex = -1; + _dataResource.load(calcHash("Ashooded")); + + _trackPoints = _dataResource.getPointArray(_tracks[_currTrackIndex]->trackPointsName); + _asCar->setPathPoints(_trackPoints); + + if (which >= 0 && _tracks[_currTrackIndex]->which2 == which) { + NPoint testPoint = (*_trackPoints)[_trackPoints->size() - 1]; + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480) + sendMessage(_asCar, 0x2007, 150); + } else { + NPoint testPoint = (*_trackPoints)[0]; + sendMessage(_asCar, 0x2002, 0); + if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480) + sendMessage(_asCar, 0x2008, 150); + } + + _carStatus = 0; + +} + +Scene2501::~Scene2501() { + // Free sprites not currently in the sprite list + if (_klaymenInCar) + delete _kmScene2501; + else + delete _asCar; +} + +void Scene2501::update() { + Scene::update(); + if (_carStatus == 1) { + removeSprite(_klaymen); + addSprite(_asCar); + clearRectList(); + _klaymenInCar = true; + SetMessageHandler(&Scene2501::hmCarAtHome); + SetUpdateHandler(&Scene2501::upCarAtHome); + _asIdleCarLower->setVisible(false); + _asIdleCarFull->setVisible(false); + _asCar->setVisible(true); + sendMessage(_asCar, 0x2009, 0); + _asCar->handleUpdate(); + _klaymen = NULL; + _carStatus = 0; + } + updateKlaymenClipRect(); +} + +void Scene2501::upCarAtHome() { + Scene::update(); + if (_mouseClicked) { + if (_mouseClickPos.x <= 210 && _asCar->getX() == 211 && _asCar->getY() == 400) { + sendMessage(_asCar, 0x200A, 0); + SetUpdateHandler(&Scene2501::upGettingOutOfCar); + } else { + moveCarToPoint(_mouseClickPos); + SetMessageHandler(&Scene2501::hmRidingCar); + SetUpdateHandler(&Scene2501::upRidingCar); + } + _mouseClicked = false; + } + updateKlaymenClipRect(); +} + +void Scene2501::upGettingOutOfCar() { + Scene::update(); + if (_carStatus == 2) { + _klaymen = _kmScene2501; + removeSprite(_asCar); + addSprite(_klaymen); + _klaymenInCar = false; + SetMessageHandler(&Scene2501::handleMessage); + SetUpdateHandler(&Scene2501::update); + setRectList(0x004B2608); + _asIdleCarLower->setVisible(true); + _asIdleCarFull->setVisible(true); + _asCar->setVisible(false); + setMessageList(0x004B2570); + processMessageList(); + _klaymen->handleUpdate(); + _carStatus = 0; + } + updateKlaymenClipRect(); +} + +void Scene2501::upRidingCar() { + Scene::update(); + if (_mouseClicked) { + moveCarToPoint(_mouseClickPos); + _mouseClicked = false; + } +} + +uint32 Scene2501::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x60842040) + _carStatus = 1; + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return messageResult; +} + +uint32 Scene2501::hmRidingCar(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + if (_tracks[_currTrackIndex]->which1 < 0 && _newTrackIndex >= 0) + changeTrack(); + else if (_tracks[_currTrackIndex]->which1 == 0) { + SetMessageHandler(&Scene2501::hmCarAtHome); + SetUpdateHandler(&Scene2501::upCarAtHome); + sendMessage(_asCar, 0x200F, 1); + } else if (_tracks[_currTrackIndex]->which1 > 0) + leaveScene(_tracks[_currTrackIndex]->which1); + break; + case 0x2006: + if (_tracks[_currTrackIndex]->which2 < 0 && _newTrackIndex >= 0) + changeTrack(); + else if (_tracks[_currTrackIndex]->which2 == 0) { + SetMessageHandler(&Scene2501::hmCarAtHome); + SetUpdateHandler(&Scene2501::upCarAtHome); + sendMessage(_asCar, 0x200F, 1); + } else if (_tracks[_currTrackIndex]->which2 > 0) + leaveScene(_tracks[_currTrackIndex]->which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return messageResult; +} + +uint32 Scene2501::hmCarAtHome(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x200A: + _carStatus = 2; + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return messageResult; +} + +void Scene2501::moveCarToPoint(NPoint &pt) { + int minMatchTrackIndex, minMatchDistance; + _tracks.findTrackPoint(pt, minMatchTrackIndex, minMatchDistance, _dataResource); + if (minMatchTrackIndex >= 0 && minMatchTrackIndex != _currTrackIndex) { + _newTrackIndex = minMatchTrackIndex; + _clickPoint = pt; + if (_currTrackIndex == 0) + sendMessage(_asCar, 0x2003, _trackPoints->size() - 1); + else + sendMessage(_asCar, 0x2003, 0); + } else { + _newTrackIndex = -1; + sendMessage(_asCar, 0x2004, pt); + } +} + +void Scene2501::changeTrack() { + _currTrackIndex = _newTrackIndex; + _trackPoints = _dataResource.getPointArray(_tracks[_currTrackIndex]->trackPointsName); + _asCar->setPathPoints(_trackPoints); + if (_currTrackIndex == 0) + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + else + sendMessage(_asCar, 0x2002, 0); + sendPointMessage(_asCar, 0x2004, _clickPoint); + _newTrackIndex = -1; +} + +void Scene2501::updateKlaymenClipRect() { + if (_kmScene2501->getX() <= 211) + _kmScene2501->setClipRect(0, 0, 640, 480); + else + _kmScene2501->setClipRect(0, 0, 640, 388); +} + +SsScene2504Button::SsScene2504Button(NeverhoodEngine *vm) + : StaticSprite(vm, 1400), _countdown(0), _isSoundPlaying(false) { + + loadSprite(0x070220D9, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400); + setVisible(false); + loadSound(0, 0x4600204C); + loadSound(1, 0x408C0034); + loadSound(2, 0x44043000); + loadSound(3, 0x44045000); + SetMessageHandler(&SsScene2504Button::handleMessage); + SetUpdateHandler(&SsScene2504Button::update); +} + +void SsScene2504Button::update() { + updatePosition(); + if (_isSoundPlaying && !isSoundPlaying(0) && !isSoundPlaying(1)) { + playSound(3); + setVisible(false); + _isSoundPlaying = false; + } + if (_countdown != 0 && (--_countdown) == 0) { + if (getSubVar(VA_LOCKS_DISABLED, 0x01180951)) + playSound(0); + else + playSound(1); + _isSoundPlaying = true; + } +} + +uint32 SsScene2504Button::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0 && !_isSoundPlaying) { + setVisible(true); + _countdown = 2; + if (getSubVar(VA_LOCKS_DISABLED, 0x01180951)) + setSubVar(VA_LOCKS_DISABLED, 0x01180951, 0); + else + setSubVar(VA_LOCKS_DISABLED, 0x01180951, 1); + playSound(2); + } + messageResult = 1; + break; + } + return messageResult; +} + +Scene2504::Scene2504(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *ssButton; + + setBackground(0x90791B80); + setPalette(0x90791B80); + ssButton = insertSprite<SsScene2504Button>(); + addCollisionSprite(ssButton); + insertPuzzleMouse(0x91B8490F, 20, 620); + SetMessageHandler(&Scene2504::handleMessage); + SetUpdateHandler(&Scene::update); +} + +uint32 Scene2504::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) + leaveScene(0); + break; + } + return messageResult; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2500.h b/engines/neverhood/modules/module2500.h new file mode 100644 index 0000000000..07db7907d5 --- /dev/null +++ b/engines/neverhood/modules/module2500.h @@ -0,0 +1,101 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2500_H +#define NEVERHOOD_MODULES_MODULE2500_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module1000.h" +#include "neverhood/modules/module1600.h" +#include "neverhood/modules/module2700.h" + +namespace Neverhood { + +// Module2500 + +class Module2500 : public Module { +public: + Module2500(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2500(); +protected: + int _sceneNum; + int _soundIndex; + void createScene(int sceneNum, int which); + void updateScene(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene2704(int which, uint32 sceneInfoId, int16 value, const uint32 *staticSprites = NULL, const NRect *clipRect = NULL); +}; + +class Scene2501 : public Scene { +public: + Scene2501(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene2501(); +protected: + AsCommonCar *_asCar; + Sprite *_ssTrackShadowBackground; + Sprite *_asCarShadow; + Sprite *_asCarConnectorShadow; + Sprite *_asCarTrackShadow; + Sprite *_asIdleCarLower; + Sprite *_asIdleCarFull; + Klaymen *_kmScene2501; + Tracks _tracks; + NPointArray *_trackPoints; + int _currTrackIndex; + NPoint _clickPoint; + int _newTrackIndex; + int _carStatus; + bool _klaymenInCar; + void update(); + void upCarAtHome(); + void upGettingOutOfCar(); + void upRidingCar(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmRidingCar(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmCarAtHome(int messageNum, const MessageParam ¶m, Entity *sender); + void moveCarToPoint(NPoint &pt); + void changeTrack(); + void updateKlaymenClipRect(); +}; + +class SsScene2504Button : public StaticSprite { +public: + SsScene2504Button(NeverhoodEngine *vm); +protected: + int _countdown; + bool _isSoundPlaying; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2504 : public Scene { +public: + Scene2504(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2500_H */ diff --git a/engines/neverhood/modules/module2600.cpp b/engines/neverhood/modules/module2600.cpp new file mode 100644 index 0000000000..b8dbf7bff1 --- /dev/null +++ b/engines/neverhood/modules/module2600.cpp @@ -0,0 +1,348 @@ +/* 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/module2600.h" + +namespace Neverhood { + +static const uint32 kModule2600SoundList[] = { + 0xB288D450, + 0x90804450, + 0x99801500, + 0xB288D455, + 0x93825040, + 0 +}; + +Module2600::Module2600(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which < 0) + createScene(_vm->gameState().sceneNum, -1); + else if (which == 1) + createScene(4, 1); + else + createScene(0, 1); + + _vm->_soundMan->addSoundList(0x40271018, kModule2600SoundList); + _vm->_soundMan->setSoundListParams(kModule2600SoundList, true, 50, 600, 5, 150); + _vm->_soundMan->playTwoSounds(0x40271018, 0x41861371, 0x43A2507F, 0); + +} + +Module2600::~Module2600() { + _vm->_soundMan->deleteGroup(0x40271018); +} + +void Module2600::createScene(int sceneNum, int which) { + debug("Module2600::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + createNavigationScene(0x004B8608, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + createNavigationScene(0x004B8638, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + createNavigationScene(0x004B86C8, which); + break; + case 3: + _vm->gameState().sceneNum = 3; + if (getGlobalVar(V_CREATURE_ANGRY)) + createNavigationScene(0x004B8758, which); + else + createNavigationScene(0x004B86F8, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + createNavigationScene(0x004B87B8, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + createNavigationScene(0x004B8698, which); + break; + case 7: + _vm->gameState().sceneNum = 7; + _vm->_soundMan->deleteGroup(0x40271018); + createSmackerScene(0x30090001, true, true, false); + break; + case 8: + _vm->gameState().sceneNum = 8; + _childObject = new Scene2609(_vm, this, which); + break; + case 1002: + _vm->gameState().sceneNum = 2; + if (getGlobalVar(V_FRUIT_COUNTING_INDEX) == 1) + createSmackerScene(0x018C0404, true, true, false); + else if (getGlobalVar(V_FRUIT_COUNTING_INDEX) == 2) + createSmackerScene(0x018C0407, true, true, false); + else + createSmackerScene(0x818C0405, true, true, false); + if (getGlobalVar(V_FRUIT_COUNTING_INDEX) >= 2) + setGlobalVar(V_FRUIT_COUNTING_INDEX, 0); + else + incGlobalVar(V_FRUIT_COUNTING_INDEX, +1); + break; + case 1003: + _vm->gameState().sceneNum = 3; + createSmackerScene(0x001C0007, true, true, false); + break; + case 1006: + _vm->gameState().sceneNum = 6; + if (getGlobalVar(V_WATER_RUNNING)) + createSmackerScene(0x049A1181, true, true, false); + else + createSmackerScene(0x04981181, true, true, false); + break; + case 1008: + _vm->gameState().sceneNum = 8; + if (getGlobalVar(V_WATER_RUNNING)) + createSmackerScene(0x42B80941, true, true, false); + else + createSmackerScene(0x42980941, true, true, false); + break; + case 9999: + createDemoScene(); + break; + } + SetUpdateHandler(&Module2600::updateScene); + _childObject->handleUpdate(); +} + +void Module2600::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == 1) + createScene(1, 3); + else + leaveModule(0); + break; + case 1: + if (_moduleResult == 0) + createScene(6, 0); + else if (_moduleResult == 1) + createScene(0, 0); + else if (_moduleResult == 2) + createScene(2, 1); + else if (_moduleResult == 3) + createScene(3, 0); + break; + case 2: + if (_moduleResult == 0) + createScene(1, 0); + else if (_moduleResult == 1) { + if (_vm->isDemo()) + createScene(9999, -1); + else + createScene(1002, -1); + } + break; + case 3: + if (_moduleResult == 0) { + if (getGlobalVar(V_CREATURE_ANGRY)) + createScene(4, 0); + else + createScene(1003, -1); + } else if (_moduleResult == 2) + createScene(1, 1); + else if (_moduleResult == 3) { + if (getGlobalVar(V_CREATURE_ANGRY)) + createScene(4, 0); + else { + setGlobalVar(V_CREATURE_ANGRY, 1); + createScene(7, -1); + } + } + break; + case 4: + if (_moduleResult == 0) + leaveModule(1); + else + createScene(3, 1); + break; + case 6: + if (_moduleResult == 0) { + if (_vm->isDemo()) + createScene(9999, -1); + else + createScene(1006, -1); + } + else if (_moduleResult == 1) + createScene(1, 2); + break; + case 7: + leaveModule(0); + break; + case 8: + createScene(1008, -1); + break; + case 1002: + createScene(2, 1); + break; + case 1003: + createScene(3, 0); + break; + case 1006: + createScene(8, -1); + break; + case 1008: + createScene(6, 0); + break; + case 9999: + createScene(_vm->gameState().sceneNum, -1); + break; + } + } +} + +SsScene2609Button::SsScene2609Button(NeverhoodEngine *vm, Scene *parentScene) + : StaticSprite(vm, 1400), _parentScene(parentScene), _countdown(0) { + + SetUpdateHandler(&SsScene2609Button::update); + SetMessageHandler(&SsScene2609Button::handleMessage); + + loadSprite(0x825A6923, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400); + if (!getGlobalVar(V_WATER_RUNNING)) + setVisible(false); + loadSound(0, 0x10267160); + loadSound(1, 0x7027FD64); + loadSound(2, 0x44043000); + loadSound(3, 0x44045000); +} + +void SsScene2609Button::update() { + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) { + if (getGlobalVar(V_WATER_RUNNING)) { + setGlobalVar(V_WATER_RUNNING, 0); + sendMessage(_parentScene, 0x2001, 0); + } else { + setGlobalVar(V_WATER_RUNNING, 1); + sendMessage(_parentScene, 0x2002, 0); + } + } +} + +uint32 SsScene2609Button::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0) { + sendMessage(_parentScene, 0x2000, 0); + if (getGlobalVar(V_WATER_RUNNING)) { + setVisible(false); + playSound(3); + playSound(1); + _countdown = 12; + } else { + setVisible(true); + playSound(2); + playSound(0); + _countdown = 96; + } + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene2609Water::AsScene2609Water(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1000) { + + _x = 240; + _y = 420; + setDoDeltaX(1); + createSurface1(0x9C210C90, 1200); + setClipRect(260, 260, 400, 368); + _vm->_soundMan->addSound(0x08526C36, 0xDC2769B0); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2609Water::handleMessage); + if (getGlobalVar(V_WATER_RUNNING)) + sendMessage(this, 0x2002, 0); +} + +AsScene2609Water::~AsScene2609Water() { + _vm->_soundMan->deleteSoundGroup(0x08526C36); +} + +uint32 AsScene2609Water::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2001: + stopAnimation(); + setVisible(false); + _vm->_soundMan->stopSound(0xDC2769B0); + break; + case 0x2002: + startAnimation(0x9C210C90, 0, -1); + setVisible(true); + _vm->_soundMan->playSoundLooping(0xDC2769B0); + break; + } + return messageResult; +} + +Scene2609::Scene2609(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isBusy(false) { + + SetUpdateHandler(&Scene::update); + SetMessageHandler(&Scene2609::handleMessage); + + setBackground(0x51409A16); + setPalette(0x51409A16); + _asWater = insertSprite<AsScene2609Water>(); + _ssButton = insertSprite<SsScene2609Button>(this); + addCollisionSprite(_ssButton); + insertPuzzleMouse(0x09A1251C, 20, 620); + insertStaticSprite(0x02138002, 1200); + insertStaticSprite(0x825E2827, 1200); +} + +uint32 Scene2609::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) && !_isBusy) + leaveScene(0); + break; + case 0x2000: + _isBusy = true; + break; + case 0x2001: + _isBusy = false; + sendMessage(_asWater, 0x2001, 0); + break; + case 0x2002: + _isBusy = false; + sendMessage(_asWater, 0x2002, 0); + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2600.h b/engines/neverhood/modules/module2600.h new file mode 100644 index 0000000000..d972e0fb0d --- /dev/null +++ b/engines/neverhood/modules/module2600.h @@ -0,0 +1,74 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2600_H +#define NEVERHOOD_MODULES_MODULE2600_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module2600 + +class Module2600 : public Module { +public: + Module2600(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2600(); +protected: + int _sceneNum; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +class SsScene2609Button : public StaticSprite { +public: + SsScene2609Button(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2609Water : public AnimatedSprite { +public: + AsScene2609Water(NeverhoodEngine *vm); + virtual ~AsScene2609Water(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2609 : public Scene { +public: + Scene2609(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + bool _isBusy; + Sprite *_asWater; + Sprite *_ssButton; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2600_H */ diff --git a/engines/neverhood/modules/module2700.cpp b/engines/neverhood/modules/module2700.cpp new file mode 100644 index 0000000000..8b69bc050e --- /dev/null +++ b/engines/neverhood/modules/module2700.cpp @@ -0,0 +1,1211 @@ +/* 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/module2700.h" +#include "neverhood/gamemodule.h" +#include "neverhood/modules/module1000.h" + +namespace Neverhood { + +static const NRect kScene2710ClipRect = NRect(0, 0, 626, 480); + +static const uint32 kScene2710StaticSprites[] = { + 0x0D2016C0, + 0 +}; + +static const NRect kScene2711ClipRect = NRect(0, 0, 521, 480); + +static const uint32 kScene2711FileHashes1[] = { + 0, + 0x100801A1, + 0x201081A0, + 0x006800A4, + 0x40390120, + 0x000001B1, + 0x001000A1, + 0 +}; + +static const uint32 kScene2711FileHashes2[] = { + 0, + 0x40403308, + 0x71403168, + 0x80423928, + 0x224131A8, + 0x50401328, + 0x70423328, + 0 +}; + +static const uint32 kScene2711FileHashes3[] = { + 0, + 0x1088A021, + 0x108120E5, + 0x18A02321, + 0x148221A9, + 0x10082061, + 0x188820E1, + 0 +}; + +static const NRect kScene2724ClipRect = NRect(0, 141, 640, 480); + +static const uint32 kScene2724StaticSprites[] = { + 0xC20D00A5, + 0 +}; + +static const NRect kScene2725ClipRect = NRect(0, 0, 640, 413); + +static const uint32 kScene2725StaticSprites[] = { + 0xC20E00A5, + 0 +}; + +Module2700::Module2700(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _soundIndex(0), _raidoMusicInitialized(false) { + + _vm->_soundMan->addMusic(0x42212411, 0x04020210); + _vm->_soundMan->startMusic(0x04020210, 24, 2); + SetMessageHandler(&Module2700::handleMessage); + + if (which < 0) { + which = _vm->gameState().which; + // Scenes 0, 30 and 31 are "normal" scenes, whereas the other scenes are tracks. + // "gameState().which" indicates which track the car is at. + if (_vm->gameState().sceneNum == 0 || _vm->gameState().sceneNum == 30 || _vm->gameState().sceneNum == 31) + which = -1; + createScene(_vm->gameState().sceneNum, which); + } else + createScene(0, 0); + + loadSound(0, 0x00880CCC); + loadSound(1, 0x00880CC0); + loadSound(2, 0x00880CCC); + loadSound(3, 0x00880CC0); + +} + +Module2700::~Module2700() { + _vm->_soundMan->deleteGroup(0x42212411); +} + +void Module2700::createScene(int sceneNum, int which) { + debug("Module2700::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _vm->gameState().which = which; + _childObject = new Scene2701(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _vm->gameState().which = which; + _childObject = new Scene2702(_vm, this, which); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->gameState().which = which; + if (which == 6 || which == 7) + createScene2703(which, 0x004B1710); + else if (which == 4 || which == 5) + createScene2703(which, 0x004B1738); + else if (which == 2 || which == 3) + createScene2703(which, 0x004B1760); + else + createScene2703(which, 0x004B1788); + break; + case 3: + _vm->gameState().sceneNum = 3; + _vm->gameState().which = which; + createScene2704(which, 0x004B17B0, 150); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->gameState().which = which; + createScene2704(which, 0x004B17D8, 150); + break; + case 5: + _vm->gameState().sceneNum = 5; + _vm->gameState().which = which; + if (which >= 4) + _childObject = new Scene2706(_vm, this, which); + else if (which == 2 || which == 3) + createScene2704(which, 0x004B1828, 150); + else + createScene2704(which, 0x004B1800, 150); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->gameState().which = which; + createScene2704(which, 0x004B1850, 150); + break; + case 7: + _vm->gameState().sceneNum = 7; + _vm->gameState().which = which; + if (which == 2 || which == 3) + createScene2704(which, 0x004B1878, 150); + else + createScene2704(which, 0x004B18A0, 150); + break; + case 8: + _vm->gameState().sceneNum = 8; + _vm->gameState().which = which; + if (which == 2 || which == 3) + createScene2704(which, 0x004B18C8, 150); + else + createScene2704(which, 0x004B18F0, 150); + break; + case 9: + _vm->gameState().sceneNum = 9; + _vm->gameState().which = which; + createScene2704(which, 0x004B1918, 150, kScene2710StaticSprites, &kScene2710ClipRect); + break; + case 10: + _vm->gameState().sceneNum = 10; + _vm->gameState().which = which; + _vm->gameModule()->initTestTubes2Puzzle(); + _scene2711StaticSprites[0] = kScene2711FileHashes1[getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 2)]; + _scene2711StaticSprites[1] = kScene2711FileHashes2[getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 1)]; + _scene2711StaticSprites[2] = kScene2711FileHashes3[getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 0)]; + _scene2711StaticSprites[3] = 0x0261282E; + _scene2711StaticSprites[4] = 0x9608E5A0; + _scene2711StaticSprites[5] = 0; + createScene2704(which, 0x004B1950, 150, _scene2711StaticSprites, &kScene2711ClipRect); + break; + case 11: + _vm->gameState().sceneNum = 11; + _vm->gameState().which = which; + createScene2704(which, 0x004B19E0, 150); + break; + case 12: + _vm->gameState().sceneNum = 12; + _vm->gameState().which = which; + createScene2704(which, 0x004B1A08, 150); + break; + case 13: + _vm->gameState().sceneNum = 13; + _vm->gameState().which = which; + createScene2704(which, 0x004B1A30, 150); + break; + case 14: + _vm->gameState().sceneNum = 14; + _vm->gameState().which = which; + if (which == 4 || which == 5) + createScene2704(which, 0x004B1A58, 150); + else if (which == 2 || which == 3) + createScene2704(which, 0x004B1A80, 150); + else + createScene2704(which, 0x004B1AA8, 150); + break; + case 15: + _vm->gameState().sceneNum = 15; + _vm->gameState().which = which; + if (which == 4 || which == 5) + createScene2704(which, 0x004B1AD0, 150); + else if (which == 2 || which == 3) + createScene2704(which, 0x004B1AF8, 150); + else + createScene2704(which, 0x004B1B20, 150); + break; + case 16: + _vm->gameState().sceneNum = 16; + _vm->gameState().which = which; + if (which == 4 || which == 5) + createScene2704(which, 0x004B1B48, 150); + else if (which == 2 || which == 3) + createScene2704(which, 0x004B1B70, 150); + else + createScene2704(which, 0x004B1B98, 150); + break; + case 17: + _vm->gameState().sceneNum = 17; + _vm->gameState().which = which; + if (which == 4 || which == 5) + createScene2704(which, 0x004B1BC0, 150); + else if (which == 2 || which == 3) + createScene2704(which, 0x004B1BE8, 150); + else + createScene2704(which, 0x004B1C10, 150); + break; + case 18: + _vm->gameState().sceneNum = 18; + _vm->gameState().which = which; + if (which == 2 || which == 3) + createScene2704(which, 0x004B1C38, 150); + else + createScene2704(which, 0x004B1C60, 150); + break; + case 19: + _vm->gameState().sceneNum = 19; + _vm->gameState().which = which; + if (which == 2 || which == 3) + createScene2704(which, 0x004B1CB0, 150); + else + createScene2704(which, 0x004B1C88, 150); + break; + case 20: + _vm->gameState().sceneNum = 20; + _vm->gameState().which = which; + if (which == 2 || which == 3) + createScene2704(which, 0x004B1CD8, 150); + else + createScene2704(which, 0x004B1D00, 150); + break; + case 21: + _vm->gameState().sceneNum = 21; + _vm->gameState().which = which; + createScene2704(which, 0x004B1D28, 150); + break; + case 22: + _vm->gameState().sceneNum = 22; + _vm->gameState().which = which; + createScene2704(which, 0x004B1D50, 150); + break; + case 23: + _vm->gameState().sceneNum = 23; + _vm->gameState().which = which; + createScene2704(which, 0x004B1D78, 150, kScene2724StaticSprites, &kScene2724ClipRect); + break; + case 24: + _vm->gameState().sceneNum = 24; + _vm->gameState().which = which; + createScene2704(which, 0x004B1DB0, 150, kScene2725StaticSprites, &kScene2725ClipRect); + break; + case 25: + _vm->gameState().sceneNum = 25; + _vm->gameState().which = which; + createScene2704(which, 0x004B1DE8, 150); + break; + case 26: + _vm->gameState().sceneNum = 26; + _vm->gameState().which = which; + createScene2704(which, 0x004B1E10, 150); + break; + case 27: + _vm->gameState().sceneNum = 27; + _vm->gameState().which = which; + createScene2704(which, 0x004B1E38, 150); + break; + case 28: + _vm->gameState().sceneNum = 28; + _vm->gameState().which = which; + createScene2704(which, 0x004B1E60, 150); + break; + case 30: + _vm->gameState().sceneNum = 30; + createStaticScene(0x09507248, 0x0724C09D); + break; + case 31: + _vm->gameState().sceneNum = 31; + _childObject = new Scene2732(_vm, this); + break; + } + SetUpdateHandler(&Module2700::updateScene); + _childObject->handleUpdate(); +} + +#define SceneLinkIf(moduleResult, sceneNum, which) \ + if (_moduleResult == moduleResult) { createScene(sceneNum, which); break; } + +void Module2700::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + SceneLinkIf(1, 1, 0); + leaveModule(0); + break; + case 1: + SceneLinkIf(1, 14, 1); + SceneLinkIf(2, 2, 2); + SceneLinkIf(3, 14, 3); + SceneLinkIf(4, 2, 6); + SceneLinkIf(5, 2, 4); + createScene(0, 1); + break; + case 2: + SceneLinkIf(1, 5, 0); + SceneLinkIf(2, 1, 2); + SceneLinkIf(3, 5, 2); + SceneLinkIf(4, 1, 5); + SceneLinkIf(5, 5, 4); + SceneLinkIf(6, 1, 4); + SceneLinkIf(7, 11, 0); + createScene(3, 0); + break; + case 3: + createScene(2, 0); + break; + case 4: + SceneLinkIf(1, 7, 2); + createScene(5, 5); + break; + case 5: + SceneLinkIf(1, 6, 0); + SceneLinkIf(2, 2, 3); + SceneLinkIf(3, 8, 2); + SceneLinkIf(4, 2, 5); + SceneLinkIf(5, 4, 0); + SceneLinkIf(6, 7, 0); + createScene(2, 1); + break; + case 6: + SceneLinkIf(1, 8, 0); + createScene(5, 1); + break; + case 7: + SceneLinkIf(1, 8, 3); + SceneLinkIf(2, 4, 1); + SceneLinkIf(3, 9, 0); + createScene(5, 6); + break; + case 8: + SceneLinkIf(1, 10, 0); + SceneLinkIf(2, 5, 3); + SceneLinkIf(3, 7, 1); + createScene(6, 1); + break; + case 9: + SceneLinkIf(1, 10, 1); + createScene(7, 3); + break; + case 10: + SceneLinkIf(1, 9, 1); + createScene(8, 1); + break; + case 11: + SceneLinkIf(1, 12, 0); + createScene(2, 7); + break; + case 12: + SceneLinkIf(1, 13, 0); + createScene(11, 1); + break; + case 13: + SceneLinkIf(1, 30, 0); + createScene(12, 1); + break; + case 14: + SceneLinkIf(1, 1, 1); + SceneLinkIf(2, 15, 3); + SceneLinkIf(3, 1, 3); + SceneLinkIf(4, 15, 5); + SceneLinkIf(5, 22, 0); + createScene(15, 1); + break; + case 15: + SceneLinkIf(1, 14, 0); + SceneLinkIf(2, 16, 3); + SceneLinkIf(3, 14, 2); + SceneLinkIf(4, 16, 5); + SceneLinkIf(5, 14, 4); + createScene(16, 1); + break; + case 16: + SceneLinkIf(1, 15, 0); + SceneLinkIf(2, 17, 3); + SceneLinkIf(3, 15, 2); + SceneLinkIf(4, 17, 5); + SceneLinkIf(5, 15, 4); + createScene(17, 1); + break; + case 17: + SceneLinkIf(1, 16, 0); + SceneLinkIf(2, 18, 3); + SceneLinkIf(3, 16, 2); + SceneLinkIf(4, 20, 1); + SceneLinkIf(5, 16, 4); + createScene(18, 1); + break; + case 18: + SceneLinkIf(1, 17, 0); + SceneLinkIf(2, 19, 2); + SceneLinkIf(3, 17, 2); + createScene(19, 0); + break; + case 19: + SceneLinkIf(1, 20, 2); + SceneLinkIf(2, 18, 2); + SceneLinkIf(3, 20, 0); + createScene(18, 0); + break; + case 20: + SceneLinkIf(1, 17, 4); + SceneLinkIf(2, 19, 1); + SceneLinkIf(3, 21, 0); + createScene(19, 3); + break; + case 21: + _vm->_soundMan->deleteMusic(_musicFileHash); + _vm->_soundMan->startMusic(0x04020210, 0, 2); + _vm->_soundMan->deleteSoundGroup(0x42212411); + createScene(20, 3); + break; + case 22: + SceneLinkIf(1, 23, 0); + createScene(14, 5); + break; + case 23: + SceneLinkIf(1, 24, 0); + createScene(22, 1); + break; + case 24: + SceneLinkIf(1, 25, 0); + createScene(23, 1); + break; + case 25: + SceneLinkIf(1, 26, 0); + createScene(24, 1); + break; + case 26: + SceneLinkIf(1, 27, 0); + createScene(25, 1); + break; + case 27: + SceneLinkIf(1, 28, 0); + createScene(26, 1); + break; + case 28: + SceneLinkIf(1, 31, 0); + createScene(27, 1); + break; + case 30: + createScene(13, 1); + break; + case 31: + createScene(28, 1); + break; + } + } else { + switch (_sceneNum) { + case 21: + if (!_raidoMusicInitialized) { + _vm->_soundMan->stopMusic(0x04020210, 0, 1); + _vm->gameModule()->initRadioPuzzle(); + _musicFileHash = getGlobalVar(V_GOOD_RADIO_MUSIC_NAME); + _vm->_soundMan->addMusic(0x42212411, _musicFileHash); + _vm->_soundMan->startMusic(_musicFileHash, 0, 2); + _vm->_soundMan->addSound(0x42212411, 0x44014282); + _vm->_soundMan->setSoundParams(0x44014282, true, 120, 360, 72, 0); + _raidoMusicInitialized = true; + } + break; + } + } +} + +uint32 Module2700::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Module::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x200D: + playSound(_soundIndex); + _soundIndex++; + if (_soundIndex >= 4) + _soundIndex = 0; + break; + } + return messageResult; +} + +void Module2700::createScene2703(int which, uint32 trackInfoId) { + _childObject = new Scene2703(_vm, this, which, trackInfoId); +} + +void Module2700::createScene2704(int which, uint32 trackInfoId, int16 value, const uint32 *staticSprites, const NRect *clipRect) { + _childObject = new Scene2704(_vm, this, which, trackInfoId, value, staticSprites, clipRect); +} + +static const NPoint kCarShadowOffsets[] = { + {-63, 3}, {-48, 40}, {-33, 58}, + { 0, 65}, { 40, 53}, { 56, 27}, + { 63, 0}, {-30, 26}, { 0, 30}, + { 26, 25} +}; + +SsCommonTrackShadowBackground::SsCommonTrackShadowBackground(NeverhoodEngine *vm, uint32 fileHash) + : StaticSprite(vm, 0) { + + loadSprite(fileHash, kSLFDefDrawOffset | kSLFDefPosition, 0); +} + +AsCommonCarShadow::AsCommonCarShadow(NeverhoodEngine *vm, AnimatedSprite *asCar, BaseSurface *shadowSurface, uint index) + : AnimatedSprite(vm, 1100), _asCar(asCar), _index(index), _animFileHash(0) { + + SetUpdateHandler(&AsCommonCarShadow::update); + createShadowSurface(shadowSurface, 211, 147, 100); + updateShadow(); +} + +void AsCommonCarShadow::update() { + updateShadow(); + AnimatedSprite::update(); +} + +void AsCommonCarShadow::updateShadow() { + if (_asCar->getFrameIndex() != _currFrameIndex || _asCar->getCurrAnimFileHash() != _animFileHash) { + uint32 fileHash = _asCar->getCurrAnimFileHash(); + if (fileHash == 0x35698F78 || fileHash == 0x192ADD30 || fileHash == 0x9C220DA4 || + fileHash == 0x9966B138 || fileHash == 0xB579A77C || fileHash == 0xA86A9538 || + fileHash == 0xD4220027 || fileHash == 0xD00A1364 || fileHash == 0xD4AA03A4 || + fileHash == 0xF46A0324) { + startAnimation(fileHash, _asCar->getFrameIndex(), -1); + _newStickFrameIndex = _asCar->getFrameIndex(); + } + _animFileHash = fileHash; + } + _x = _asCar->getX() + kCarShadowOffsets[_index].x; + _y = _asCar->getY() + kCarShadowOffsets[_index].y; + if (!_asCar->getVisible()) { + startAnimation(0x1209E09F, 0, -1); + _newStickFrameIndex = 0; + } + setDoDeltaX(_asCar->isDoDeltaX() ? 1 : 0); +} + +AsCommonCarConnectorShadow::AsCommonCarConnectorShadow(NeverhoodEngine *vm, Sprite *asCar, BaseSurface *shadowSurface, uint index) + : AnimatedSprite(vm, 1100), _asCar(asCar), _index(index) { + + SetUpdateHandler(&AsCommonCarConnectorShadow::update); + createShadowSurface1(shadowSurface, 0x60281C10, 150); + startAnimation(0x60281C10, -1, -1); + _newStickFrameIndex = STICK_LAST_FRAME; +} + +void AsCommonCarConnectorShadow::update() { + _x = _asCar->getX() + kCarShadowOffsets[_index].x; + _y = _asCar->getY() + kCarShadowOffsets[_index].y; + AnimatedSprite::update(); +} + +AsCommonCarTrackShadow::AsCommonCarTrackShadow(NeverhoodEngine *vm, Sprite *asCar, BaseSurface *shadowSurface, int16 frameIndex) + : AnimatedSprite(vm, 1100), _asCar(asCar) { + + SetUpdateHandler(&AsCommonCarTrackShadow::update); + createShadowSurface1(shadowSurface, 0x0759129C, 100); + startAnimation(0x0759129C, frameIndex, -1); + _newStickFrameIndex = frameIndex; +} + +void AsCommonCarTrackShadow::update() { + _x = _asCar->getX(); + _y = _asCar->getY(); + AnimatedSprite::update(); +} + +Scene2701::Scene2701(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + NRect clipRect; + TrackInfo *tracks = _vm->_staticData->getTrackInfo(0x004B2240); + setGlobalVar(V_CAR_DELTA_X, 1); + + setBackground(tracks->bgFilename); + setPalette(tracks->bgFilename); + _palette->addPalette(calcHash("paPodFloor"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayFloor"), 0, 65, 0); + insertScreenMouse(0x08B08180); + + tempSprite = insertStaticSprite(0x1E086325, 1200); + clipRect.set(0, 0, 640, tempSprite->getDrawRect().y2()); + + if (tracks->bgShadowFilename) { + _ssTrackShadowBackground = createSprite<SsCommonTrackShadowBackground>(tracks->bgShadowFilename); + addEntity(_ssTrackShadowBackground); + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + _asCarShadow = insertSprite<AsCommonCarShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarTrackShadow = insertSprite<AsCommonCarTrackShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarConnectorShadow = insertSprite<AsCommonCarConnectorShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + } else { + _ssTrackShadowBackground = NULL; + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + } + + _asCarConnector = insertSprite<AsCommonCarConnector>(_asCar); + _which1 = tracks->which1; + _which2 = tracks->which2; + _dataResource.load(tracks->dataResourceFilename); + _trackPoints = _dataResource.getPointArray(tracks->trackPointsName); + _asCar->setPathPoints(_trackPoints); + + if (which == _which2) { + NPoint testPoint = (*_trackPoints)[_trackPoints->size() - 1]; + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480) + sendMessage(_asCar, 0x2007, 150); + } else { + NPoint testPoint = (*_trackPoints)[0]; + sendMessage(_asCar, 0x2002, 0); + if (testPoint.x < 0 || testPoint.x >= 640 || testPoint.y < 0 || testPoint.y >= 480) + sendMessage(_asCar, 0x2008, 150); + } + + _asCar->setClipRect(clipRect); + _asCarConnector->setClipRect(clipRect); + + if (which == 1) { + SetMessageHandler(&Scene2701::hmRidingCar); + } else { + sendMessage(_asCar, 0x2009, 0); + SetMessageHandler(&Scene2701::hmCarAtHome); + } + +} + +uint32 Scene2701::hmRidingCar(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + sendPointMessage(_asCar, 0x2004, param.asPoint()); + break; + case 0x2005: + if (_which1 >= 0) + SetMessageHandler(&Scene2701::hmCarAtHome); + break; + case 0x2006: + if (_which2 >= 0) + leaveScene(_which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +uint32 Scene2701::hmCarAtHome(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x >= 385) + leaveScene(0); + else { + sendPointMessage(_asCar, 0x2004, param.asPoint()); + SetMessageHandler(&Scene2701::hmRidingCar); + } + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +Scene2702::Scene2702(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _isInLight(true), _newTrackIndex(-1) { + + SetMessageHandler(&Scene2702::handleMessage); + SetUpdateHandler(&Scene2702::update); + + setBackground(0x18808B00); + setPalette(0x18808B00); + _palette->addPalette(calcHash("paPodFloor"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayFloor"), 0, 65, 0); + addEntity(_palette); + insertScreenMouse(0x08B04180); + + _ssTrackShadowBackground = createSprite<SsCommonTrackShadowBackground>(0x12002035); + addEntity(_ssTrackShadowBackground); + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + _asCarShadow = insertSprite<AsCommonCarShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + insertSprite<AsCommonCarConnector>(_asCar); + _asCarTrackShadow = insertSprite<AsCommonCarTrackShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarConnectorShadow = insertSprite<AsCommonCarConnectorShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _dataResource.load(0x04310014); + + if (which == 1) { + _isUpperTrack = false; + _currTrackIndex = 1; + } else if (which == 2) { + _isUpperTrack = false; + _currTrackIndex = 2; + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + _isInLight = false; + } else if (which == 3) { + _isUpperTrack = true; + _currTrackIndex = 0; + } else if (which == 4) { + _isUpperTrack = true; + _currTrackIndex = 2; + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + _isInLight = false; + } else if (which == 5) { + _isUpperTrack = true; + _currTrackIndex = 1; + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + _isInLight = false; + } else { + _isUpperTrack = false; + _currTrackIndex = 0; + } + + if (_isUpperTrack) { + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B5F68)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B5F8C)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B5FB0)); + } else { + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B5FD8)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B5FFC)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B6020)); + } + + _trackPoints = _dataResource.getPointArray(_tracks[_currTrackIndex]->trackPointsName); + _asCar->setPathPoints(_trackPoints); + + if (which == _tracks[_currTrackIndex]->which2) { + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + sendMessage(_asCar, 0x2007, 150); + } else { + sendMessage(_asCar, 0x2002, 0); + sendMessage(_asCar, 0x2008, 150); + } + + _palette->copyBasePalette(0, 256, 0); + +} + +void Scene2702::update() { + Scene::update(); + if (_isInLight && _asCar->getX() > 422) { + _palette->addBasePalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addBasePalette(calcHash("paKlayShade"), 0, 65, 0); + _palette->startFadeToPalette(12); + _isInLight = false; + } else if (!_isInLight && _asCar->getX() <= 422) { + _palette->addBasePalette(calcHash("paPodFloor"), 65, 31, 65); + _palette->addBasePalette(calcHash("paKlayFloor"), 0, 65, 0); + _palette->startFadeToPalette(12); + _isInLight = true; + } +} + +uint32 Scene2702::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + moveCarToPoint(param.asPoint()); + break; + case 0x2005: + if (_newTrackIndex >= 0) { + if (_tracks[_currTrackIndex]->which1 < 0) + changeTrack(); + } else if (_tracks[_currTrackIndex]->which1 >= 0) + leaveScene(_tracks[_currTrackIndex]->which1); + break; + case 0x2006: + if (_newTrackIndex >= 0) { + if (_tracks[_currTrackIndex]->which2 < 0) + changeTrack(); + } else if (_tracks[_currTrackIndex]->which2 >= 0) + leaveScene(_tracks[_currTrackIndex]->which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +void Scene2702::moveCarToPoint(NPoint pt) { + int minMatchTrackIndex, minMatchDistance; + _tracks.findTrackPoint(pt, minMatchTrackIndex, minMatchDistance, _dataResource); + if (minMatchTrackIndex >= 0 && minMatchTrackIndex != _currTrackIndex) { + _newTrackIndex = minMatchTrackIndex; + _newTrackDestX = pt.x; + if (_isUpperTrack) { + if (_currTrackIndex == 0) + sendMessage(_asCar, 0x2003, _trackPoints->size() - 1); + else + sendMessage(_asCar, 0x2003, 0); + } else if (_currTrackIndex == 2) + sendMessage(_asCar, 0x2003, 0); + else + sendMessage(_asCar, 0x2003, _trackPoints->size() - 1); + } else { + _newTrackIndex = -1; + sendMessage(_asCar, 0x2004, pt.x); + } +} + +void Scene2702::changeTrack() { + _currTrackIndex = _newTrackIndex; + _trackPoints = _dataResource.getPointArray(_tracks[_currTrackIndex]->trackPointsName); + _asCar->setPathPoints(_trackPoints); + if (_isUpperTrack) { + if (_currTrackIndex == 0) + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + else + sendMessage(_asCar, 0x2002, 0); + } else if (_currTrackIndex == 2) + sendMessage(_asCar, 0x2002, 0); + else + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + sendMessage(_asCar, 0x2004, _newTrackDestX); + _newTrackIndex = -1; +} + +Scene2703::Scene2703(NeverhoodEngine *vm, Module *parentModule, int which, uint32 trackInfoId) + : Scene(vm, parentModule) { + + TrackInfo *tracks = _vm->_staticData->getTrackInfo(trackInfoId); + + SetMessageHandler(&Scene2703::handleMessage); + SetUpdateHandler(&Scene2703::update); + + setBackground(tracks->bgFilename); + setPalette(tracks->bgFilename); + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + addEntity(_palette); + insertScreenMouse(tracks->mouseCursorFilename); + + _palStatus = 2; + + if (tracks->bgShadowFilename) { + _ssTrackShadowBackground = createSprite<SsCommonTrackShadowBackground>(tracks->bgShadowFilename); + addEntity(_ssTrackShadowBackground); + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + _asCarShadow = insertSprite<AsCommonCarShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarTrackShadow = insertSprite<AsCommonCarTrackShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarConnectorShadow = insertSprite<AsCommonCarConnectorShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + } else { + _ssTrackShadowBackground = NULL; + _asCarShadow = NULL; + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + } + + _asCarConnector = insertSprite<AsCommonCarConnector>(_asCar); + _which1 = tracks->which1; + _which2 = tracks->which2; + _dataResource.load(tracks->dataResourceFilename); + _trackPoints = _dataResource.getPointArray(tracks->trackPointsName); + _asCar->setPathPoints(_trackPoints); + + if (which == _which2) { + NPoint testPoint = (*_trackPoints)[_trackPoints->size() - 1]; + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + if (testPoint.x > 0 && testPoint.x < 640 && testPoint.y > 0 && testPoint.y < 480) + sendMessage(_asCar, 0x2009, 0); + else + sendMessage(_asCar, 0x2007, 150); + } else { + NPoint testPoint = (*_trackPoints)[0]; + sendMessage(_asCar, 0x2002, 0); + if (testPoint.x > 0 && testPoint.x < 640 && testPoint.y > 0 && testPoint.y < 480) + sendMessage(_asCar, 0x2009, 0); + else + sendMessage(_asCar, 0x2008, 150); + } + + if (which == 0) { + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + _palStatus = 1; + } else if (which == 2 || which == 4 || which == 6) { + _palette->addPalette(calcHash("paPodBlack"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayBlack"), 0, 65, 0); + _palStatus = 0; + } + + _palette->copyBasePalette(0, 256, 0); + +} + +void Scene2703::update() { + Scene::update(); + if (_mouseClicked) { + sendPointMessage(_asCar, 0x2004, _mouseClickPos); + _mouseClicked = false; + } + if (_asCar->getX() > 469) { + if (_palStatus != 2) { + _palette->addBasePalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addBasePalette(calcHash("paKlayShade"), 0, 65, 0); + _palette->startFadeToPalette(12); + _palStatus = 2; + } + } else if (_asCar->getX() > 181) { + if (_palStatus != 1) { + _palette->addBasePalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addBasePalette(calcHash("paKlayShade"), 0, 65, 0); + _palette->startFadeToPalette(12); + _palStatus = 1; + } + } else if (_palStatus != 0) { + _palette->addBasePalette(calcHash("paPodBlack"), 65, 31, 65); + _palette->addBasePalette(calcHash("paKlayBlack"), 0, 65, 0); + _palette->startFadeToPalette(12); + _palStatus = 0; + } +} + +uint32 Scene2703::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + if (_which1 >= 0) + leaveScene(_which1); + break; + case 0x2006: + if (_which2 >= 0) + leaveScene(_which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +Scene2704::Scene2704(NeverhoodEngine *vm, Module *parentModule, int which, uint32 trackInfoId, int16 value, + const uint32 *staticSprites, const NRect *clipRect) + : Scene(vm, parentModule) { + + TrackInfo *tracks = _vm->_staticData->getTrackInfo(trackInfoId); + + SetMessageHandler(&Scene2704::handleMessage); + SetUpdateHandler(&Scene2704::update); + + setBackground(tracks->bgFilename); + setPalette(tracks->bgFilename); + + if (tracks->exPaletteFilename1) + _palette->addPalette(tracks->exPaletteFilename1, 0, 65, 0); + + if (tracks->exPaletteFilename2) + _palette->addPalette(tracks->exPaletteFilename2, 65, 31, 65); + + while (staticSprites && *staticSprites) + insertStaticSprite(*staticSprites++, 1100); + + insertScreenMouse(tracks->mouseCursorFilename); + + if (tracks->bgShadowFilename) { + _ssTrackShadowBackground = createSprite<SsCommonTrackShadowBackground>(tracks->bgShadowFilename); + addEntity(_ssTrackShadowBackground); + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + _asCarShadow = insertSprite<AsCommonCarShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarTrackShadow = insertSprite<AsCommonCarTrackShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarConnectorShadow = insertSprite<AsCommonCarConnectorShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + } else { + _ssTrackShadowBackground = NULL; + _asCarShadow = NULL; + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + } + + _asCarConnector = insertSprite<AsCommonCarConnector>(_asCar); + _which1 = tracks->which1; + _which2 = tracks->which2; + _dataResource.load(tracks->dataResourceFilename); + _trackPoints = _dataResource.getPointArray(tracks->trackPointsName); + _asCar->setPathPoints(_trackPoints); + + if (which == _which2) { + NPoint testPoint = (*_trackPoints)[_trackPoints->size() - 1]; + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + if (testPoint.x > 0 && testPoint.x < 640 && testPoint.y > 0 && testPoint.y < 480) + sendMessage(_asCar, 0x2009, 0); + else + sendMessage(_asCar, 0x2007, 0); + } else { + NPoint testPoint = (*_trackPoints)[0]; + sendMessage(_asCar, 0x2002, 0); + if (testPoint.x > 0 && testPoint.x < 640 && testPoint.y > 0 && testPoint.y < 480) + sendMessage(_asCar, 0x2009, 0); + else + sendMessage(_asCar, 0x2008, 0); + } + + if (clipRect) { + _asCar->getClipRect() = *clipRect; + if (_asCarShadow) + _asCarShadow->getClipRect() = *clipRect; + if (_asCarTrackShadow) + _asCarTrackShadow->getClipRect() = *clipRect; + if (_asCarConnectorShadow) + _asCarConnectorShadow->getClipRect() = *clipRect; + if (_asCarConnector) + _asCarConnector->getClipRect() = *clipRect; + } + +} + +void Scene2704::update() { + Scene::update(); + if (_mouseClicked) { + sendPointMessage(_asCar, 0x2004, _mouseClickPos); + _mouseClicked = false; + } +} + +uint32 Scene2704::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + if (_which1 >= 0) + leaveScene(_which1); + break; + case 0x2006: + if (_which2 >= 0) + leaveScene(_which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +Scene2706::Scene2706(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _newTrackIndex(-1) { + + SetMessageHandler(&Scene2706::handleMessage); + + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B22A0)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B22C4)); + _tracks.push_back(_vm->_staticData->getTrackInfo(0x004B22E8)); + + setBackground(0x18808B88); + setPalette(0x18808B88); + + _palette->addPalette(calcHash("paPodShade"), 65, 31, 65); + _palette->addPalette(calcHash("paKlayShade"), 0, 65, 0); + + insertScreenMouse(0x08B8C180); + + _ssTrackShadowBackground = createSprite<SsCommonTrackShadowBackground>(0x18808B88); + addEntity(_ssTrackShadowBackground); + + _asCar = insertSprite<AsCommonCar>(this, 320, 240); + _asCarShadow = insertSprite<AsCommonCarShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarConnector = insertSprite<AsCommonCarConnector>(_asCar); + _asCarTrackShadow = insertSprite<AsCommonCarTrackShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + _asCarConnectorShadow = insertSprite<AsCommonCarConnectorShadow>(_asCar, _ssTrackShadowBackground->getSurface(), 4); + + _dataResource.load(0x06000162); + + if (which == 5) + _currTrackIndex = 2; + else if (which == 6) + _currTrackIndex = 1; + else + _currTrackIndex = 0; + + _trackPoints = _dataResource.getPointArray(_tracks[_currTrackIndex]->trackPointsName); + _asCar->setPathPoints(_trackPoints); + + if (which == _tracks[_currTrackIndex]->which2) { + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + if (which == 5) + sendMessage(_asCar, 0x2007, 50); + else + sendMessage(_asCar, 0x2007, 150); + } else { + sendMessage(_asCar, 0x2002, 0); + if (which == 5) + sendMessage(_asCar, 0x2008, 50); + else + sendMessage(_asCar, 0x2008, 150); + } + +} + +uint32 Scene2706::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + moveCarToPoint(param.asPoint()); + break; + case 0x2005: + if (_newTrackIndex >= 0) { + if (_tracks[_currTrackIndex]->which1 < 0) + changeTrack(); + } else if (_tracks[_currTrackIndex]->which1 >= 0) + leaveScene(_tracks[_currTrackIndex]->which1); + break; + case 0x2006: + if (_newTrackIndex >= 0) { + if (_tracks[_currTrackIndex]->which2 < 0) + changeTrack(); + } else if (_tracks[_currTrackIndex]->which2 >= 0) + leaveScene(_tracks[_currTrackIndex]->which2); + break; + case 0x200D: + sendMessage(_parentModule, 0x200D, 0); + break; + } + return 0; +} + +void Scene2706::moveCarToPoint(NPoint pt) { + int minMatchTrackIndex, minMatchDistance; + _tracks.findTrackPoint(pt, minMatchTrackIndex, minMatchDistance, _dataResource); + if (minMatchTrackIndex >= 0 && minMatchTrackIndex != _currTrackIndex) { + _newTrackIndex = minMatchTrackIndex; + _newTrackDestX = pt.x; + if (_currTrackIndex == 0) + sendMessage(_asCar, 0x2003, _trackPoints->size() - 1); + else + sendMessage(_asCar, 0x2003, 0); + } else { + _newTrackIndex = -1; + sendMessage(_asCar, 0x2004, pt.x); + } +} + +void Scene2706::changeTrack() { + _currTrackIndex = _newTrackIndex; + _trackPoints = _dataResource.getPointArray(_tracks[_currTrackIndex]->trackPointsName); + _asCar->setPathPoints(_trackPoints); + if (_currTrackIndex == 0) + sendMessage(_asCar, 0x2002, _trackPoints->size() - 1); + else + sendMessage(_asCar, 0x2002, 0); + sendMessage(_asCar, 0x2004, _newTrackDestX); + _newTrackIndex = -1; +} + +Scene2732::Scene2732(NeverhoodEngine *vm, Module *parentModule) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + setBackground(0x0220C041); + setPalette(0x0220C041); + insertScreenMouse(0x0C04502A); + setRectList(0x004AE360); + + insertKlaymen<KmScene2732>(108, 331); + setMessageList(0x004AE328); + + tempSprite = insertStaticSprite(0x50C22C48, 1100); + _klaymen->setClipRect(tempSprite->getDrawRect().x, 0, 640, 480); + +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2700.h b/engines/neverhood/modules/module2700.h new file mode 100644 index 0000000000..003666bb7f --- /dev/null +++ b/engines/neverhood/modules/module2700.h @@ -0,0 +1,182 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2700_H +#define NEVERHOOD_MODULES_MODULE2700_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module1600.h" + +namespace Neverhood { + +// Module2700 + +class Module2700 : public Module { +public: + Module2700(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2700(); +protected: + int _sceneNum; + int _soundIndex; + bool _raidoMusicInitialized; + uint32 _scene2711StaticSprites[6]; + uint32 _musicFileHash; + void createScene(int sceneNum, int which); + void updateScene(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createScene2703(int which, uint32 trackInfoId); + void createScene2704(int which, uint32 trackInfoId, int16 value, const uint32 *staticSprites = NULL, const NRect *clipRect = NULL); +}; + +class SsCommonTrackShadowBackground : public StaticSprite { +public: + SsCommonTrackShadowBackground(NeverhoodEngine *vm, uint32 fileHash); +}; + +class AsCommonCarShadow : public AnimatedSprite { +public: + AsCommonCarShadow(NeverhoodEngine *vm, AnimatedSprite *asCar, BaseSurface *shadowSurface, uint index); +protected: + uint _index; + AnimatedSprite *_asCar; + uint32 _animFileHash; + void update(); + void updateShadow(); +}; + +class AsCommonCarConnectorShadow : public AnimatedSprite { +public: + AsCommonCarConnectorShadow(NeverhoodEngine *vm, Sprite *asCar, BaseSurface *shadowSurface, uint index); +protected: + uint _index; + Sprite *_asCar; + void update(); +}; + +class AsCommonCarTrackShadow : public AnimatedSprite { +public: + AsCommonCarTrackShadow(NeverhoodEngine *vm, Sprite *asCar, BaseSurface *shadowSurface, int16 frameIndex); +protected: + Sprite *_asCar; + void update(); +}; + +class Scene2701 : public Scene { +public: + Scene2701(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + AsCommonCar *_asCar; + Sprite *_ssTrackShadowBackground; + Sprite *_asCarShadow; + Sprite *_asCarTrackShadow; + Sprite *_asCarConnectorShadow; + Sprite *_asCarConnector; + int _which1, _which2; + NPointArray *_trackPoints; + uint32 hmRidingCar(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmCarAtHome(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2702 : public Scene { +public: + Scene2702(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + AsCommonCar *_asCar; + Sprite *_ssTrackShadowBackground; + Sprite *_asCarShadow; + Sprite *_asCarTrackShadow; + Sprite *_asCarConnectorShadow; + int16 _newTrackDestX; + bool _isInLight; + int _currTrackIndex, _newTrackIndex; + bool _isUpperTrack; + Tracks _tracks; + NPointArray *_trackPoints; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void moveCarToPoint(NPoint pt); + void changeTrack(); +}; + +class Scene2703 : public Scene { +public: + Scene2703(NeverhoodEngine *vm, Module *parentModule, int which, uint32 trackInfoId); +protected: + AsCommonCar *_asCar; + Sprite *_ssTrackShadowBackground; + Sprite *_asCarShadow; + Sprite *_asCarConnector; + Sprite *_asCarTrackShadow; + Sprite *_asCarConnectorShadow; + int _palStatus; + int _which1, _which2; + NPointArray *_trackPoints; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2704 : public Scene { +public: + Scene2704(NeverhoodEngine *vm, Module *parentModule, int which, uint32 trackInfoId, int16 value, + const uint32 *staticSprites = NULL, const NRect *clipRect = NULL); +protected: + AsCommonCar *_asCar; + Sprite *_ssTrackShadowBackground; + Sprite *_asCarShadow; + Sprite *_asCarConnector; + Sprite *_asCarTrackShadow; + Sprite *_asCarConnectorShadow; + int _which1, _which2; + NPointArray *_trackPoints; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2706 : public Scene { +public: + Scene2706(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + AsCommonCar *_asCar; + Sprite *_ssTrackShadowBackground; + Sprite *_asCarShadow; + Sprite *_asCarConnector; + Sprite *_asCarTrackShadow; + Sprite *_asCarConnectorShadow; + int16 _newTrackDestX; + int _currTrackIndex, _newTrackIndex; + Tracks _tracks; + NPointArray *_trackPoints; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void moveCarToPoint(NPoint pt); + void changeTrack(); +}; + +class Scene2732 : public Scene { +public: + Scene2732(NeverhoodEngine *vm, Module *parentModule); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2700_H */ diff --git a/engines/neverhood/modules/module2800.cpp b/engines/neverhood/modules/module2800.cpp new file mode 100644 index 0000000000..26df0aefa5 --- /dev/null +++ b/engines/neverhood/modules/module2800.cpp @@ -0,0 +1,3203 @@ +/* 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/module2800.h" +#include "neverhood/gamemodule.h" +#include "neverhood/modules/module1000.h" +#include "neverhood/modules/module1200.h" +#include "neverhood/modules/module1700.h" +#include "neverhood/modules/module2200.h" +#include "neverhood/diskplayerscene.h" + +namespace Neverhood { + +Module2800::Module2800(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule), _musicResource(NULL) { + + _currentMusicFileHash = 0; + _vm->_soundMan->addMusic(0x64210814, 0xD2FA4D14); + setGlobalVar(V_RADIO_MOVE_DISH_VIDEO, 1); + + if (which < 0) { + createScene(_vm->gameState().sceneNum, which); + } else if (which == 2) { + createScene(4, 3); + } else if (which == 1) { + createScene(4, 1); + } else { + createScene(0, 0); + } + +} + +Module2800::~Module2800() { + if (_musicResource) { + _musicResource->unload(); + delete _musicResource; + } + _vm->_soundMan->deleteGroup(0x64210814); +} + +void Module2800::createScene(int sceneNum, int which) { + debug("Module2800::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 0); + _childObject = new Scene2801(_vm, this, which); + break; + case 1: + _vm->gameState().sceneNum = 1; + _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 0); + if (getGlobalVar(V_RADIO_ENABLED)) + _childObject = new Scene2802(_vm, this, which); + else + createStaticScene(0x000C6444, 0xC6440008); + break; + case 2: + _vm->gameState().sceneNum = 2; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + if (getGlobalVar(V_KLAYMEN_SMALL)) + _childObject = new Scene2803Small(_vm, this, which); + else + _childObject = new Scene2803(_vm, this, which); + break; + case 3: + _vm->gameState().sceneNum = 3; + _childObject = new Scene2804(_vm, this, which); + break; + case 4: + _vm->gameState().sceneNum = 4; + _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2805(_vm, this, which); + break; + case 5: + _vm->gameState().sceneNum = 5; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2806(_vm, this, which); + break; + case 6: + _vm->gameState().sceneNum = 6; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2807(_vm, this, which); + break; + case 7: + _vm->gameState().sceneNum = 7; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2808(_vm, this, 0); + break; + case 8: + _vm->gameState().sceneNum = 8; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2809(_vm, this, which); + break; + case 9: + _vm->gameState().sceneNum = 9; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2810(_vm, this, which); + break; + case 10: + _vm->gameState().sceneNum = 10; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2808(_vm, this, 1); + break; + case 11: + _vm->gameState().sceneNum = 11; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2812(_vm, this, which); + break; + case 12: + _vm->gameState().sceneNum = 12; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x0000A245, 0x0A241008); + break; + case 13: + _vm->gameState().sceneNum = 13; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x81C60635, 0x60631814); + break; + case 14: + _vm->gameState().sceneNum = 14; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0xCA811204, 0x11200CA0); + break; + case 15: + _vm->gameState().sceneNum = 15; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x2D438A00, 0x38A042DC); + break; + case 16: + _vm->gameState().sceneNum = 16; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x0A806204, 0x062000A0); + break; + case 17: + _vm->gameState().sceneNum = 17; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x010F9284, 0xF9280018); + break; + case 18: + _vm->gameState().sceneNum = 18; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x0100022B, 0x0022F018); + break; + case 19: + _vm->gameState().sceneNum = 19; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x10866205, 0x66201100); + break; + case 20: + _vm->gameState().sceneNum = 20; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x01C58000, 0x58004014); + break; + case 21: + _vm->gameState().sceneNum = 21; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new Scene2822(_vm, this, which); + break; + case 22: + _vm->gameState().sceneNum = 22; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x9408121E, 0x8121A948); + break; + case 23: + _vm->gameState().sceneNum = 23; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x048C0600, 0xC0604040); + break; + case 24: + _vm->gameState().sceneNum = 24; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + createStaticScene(0x04270A94, 0x70A9004A); + break; + case 25: + _vm->gameState().sceneNum = 25; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + if (getGlobalVar(V_SHRINK_LIGHTS_ON)) + createStaticScene(0x01600204, 0x0020001E); + else + createStaticScene(0x08611204, 0x1120008E); + break; + case 26: + _vm->gameState().sceneNum = 26; + _vm->_soundMan->startMusic(0xD2FA4D14, 0, 2); + _childObject = new DiskplayerScene(_vm, this, 4); + break; + case 1001: + _vm->_soundMan->stopMusic(0xD2FA4D14, 0, 0); + createSmackerScene(0x00800801, true, true, false); + break; + } + SetUpdateHandler(&Module2800::updateScene); + _childObject->handleUpdate(); +} + +void Module2800::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult != 2) { + if (_musicResource) { + _musicResource->unload(); + delete _musicResource; + _musicResource = NULL; + } + _currentMusicFileHash = 0; + } + if (_moduleResult == 1) { + createScene(2, 0); + } else if (_moduleResult == 2) { + createScene(1, 0); + } else { + leaveModule(0); + } + break; + case 1: + if (_moduleResult == 0) { + createScene(0, 2); + } else { + createScene(1001, -1); + } + break; + case 2: + if (_moduleResult == 1) + createScene(3, 0); + else if (_moduleResult == 2) + createScene(5, 0); + else if (_moduleResult == 3) + createScene(6, 0); + else if (_moduleResult == 4) + createScene(9, 0); + else if (_moduleResult == 5) + createScene(25, 0); + else + createScene(0, 1); + break; + case 3: + createScene(2, 1); + break; + case 4: + if (_moduleResult == 1) { + leaveModule(1); + } else { + createScene(11, 1); + } + break; + case 5: + if (_moduleResult == 1) { + createScene(7, 0); + } else { + createScene(2, 2); + } + break; + case 6: + createScene(2, 3); + break; + case 7: + createScene(5, _moduleResult); + break; + case 8: + if (_moduleResult == 1) + createScene(10, 0); + else + createScene(9, 4); + break; + case 9: + if (_moduleResult == 1) + createScene(11, 0); + else if (_moduleResult == 2) + createScene(2, 0); + else if (_moduleResult == 3) + createScene(24, 0); + else if (_moduleResult == 4) + createScene(8, 0); + else if (_moduleResult == 6) + createScene(2, 6); + else if (_moduleResult == 11) + createScene(12, 0); + else if (_moduleResult == 12) + createScene(13, 0); + else if (_moduleResult == 13) + createScene(14, 0); + else if (_moduleResult == 14) + createScene(15, 0); + else if (_moduleResult == 15) + createScene(16, 0); + else if (_moduleResult == 16) + createScene(17, 0); + else if (_moduleResult == 17) + createScene(18, 0); + else if (_moduleResult == 18) + createScene(19, 0); + else if (_moduleResult == 19) + createScene(20, 0); + else if (_moduleResult == 20) + createScene(21, 0); + else if (_moduleResult == 21) + createScene(22, 0); + else if (_moduleResult == 22) + createScene(23, 0); + else + createScene(2, 4); + break; + case 10: + createScene(8, _moduleResult); + break; + case 11: + if (_moduleResult == 1) + createScene(4, 0); + else if (_moduleResult == 2) + createScene(26, 0); + else if (_moduleResult == 3) + createScene(9, 5); + else + createScene(9, 1); + break; + case 12: + createScene(9, 11); + break; + case 13: + createScene(9, 12); + break; + case 14: + createScene(9, 13); + break; + case 15: + createScene(9, 14); + break; + case 16: + createScene(9, 15); + break; + case 17: + createScene(9, 16); + break; + case 18: + createScene(9, 17); + break; + case 19: + createScene(9, 18); + break; + case 20: + createScene(9, 19); + break; + case 21: + createScene(9, 20); + break; + case 22: + createScene(9, 21); + break; + case 23: + createScene(9, 22); + break; + case 24: + createScene(9, 3); + break; + case 25: + createScene(2, 5); + break; + case 26: + createScene(11, 2); + break; + case 1001: + createScene(1, -1); + break; + } + } else { + switch (_sceneNum) { + case 0: + updateMusic(true); + break; + case 1: + updateMusic(false); + break; + } + } +} + +void Module2800::updateMusic(bool halfVolume) { + + uint32 newMusicFileHash = _vm->_gameModule->getCurrRadioMusicFileHash(); + + if (!_musicResource) + _musicResource = new MusicResource(_vm); + + if (newMusicFileHash != _currentMusicFileHash) { + _currentMusicFileHash = newMusicFileHash; + if (_currentMusicFileHash != 0) { + _musicResource->load(_currentMusicFileHash); + _musicResource->setVolume(halfVolume ? 60 : 100); + _musicResource->play(0); + } else { + _musicResource->stop(0); + } + } else if (_currentMusicFileHash != 0) { + if (!_musicResource->isPlaying()) { + _musicResource->setVolume(halfVolume ? 60 : 100); + _musicResource->play(0); + } else { + _musicResource->setVolume(halfVolume ? 60 : 100); + } + } else { + _musicResource->stop(0); + } + +} + +Scene2801::Scene2801(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *_sprite1; + Sprite *_sprite2; + + _vm->gameModule()->initRadioPuzzle(); + + SetMessageHandler(&Scene2801::handleMessage); + SetUpdateHandler(&Scene::update); + + // Display the disabled radio; only possible when the left door is open + if (!getGlobalVar(V_RADIO_ENABLED)) + insertStaticSprite(0x0001264C, 100); + + if (which < 0) { + insertKlaymen<KmScene2801>(194, 430); + setMessageList(0x004B6BB8); + } else if (which == 1) { + insertKlaymen<KmScene2801>(443, 398); + setMessageList(0x004B6BC0); + } else if (which == 2) { + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene2801>(312, 432); + _klaymen->setDoDeltaX(1); + } else { + insertKlaymen<KmScene2801>(194, 432); + } + setMessageList(0x004B6C10); + } else { + insertKlaymen<KmScene2801>(0, 432); + setMessageList(0x004B6BB0); + } + + if (getGlobalVar(V_RADIO_ROOM_LEFT_DOOR)) { + setRectList(0x004B6CE0); + setBackground(0x01400666); + setPalette(0x01400666); + _paletteHash = 0x15021024; + _palette->addBasePalette(0x01400666, 0, 256, 0); + _sprite1 = insertStaticSprite(0x100CA0A8, 1100); + _sprite2 = insertStaticSprite(0x287C21A4, 1100); + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, _sprite2->getDrawRect().x2(), 480); + insertScreenMouse(0x0066201C); + _asTape = insertSprite<AsScene1201Tape>(this, 8, 1100, 302, 437, 0x9148A011); + addCollisionSprite(_asTape); + } else if (getGlobalVar(V_RADIO_ROOM_RIGHT_DOOR)) { + setRectList(0x004B6CD0); + setBackground(0x11E00684); + setPalette(0x11E00684); + _paletteHash = 0x15021024; + _palette->addBasePalette(0x11E00684, 0, 256, 0); + _sprite2 = insertStaticSprite(0x061601C8, 1100); + _klaymen->setClipRect(0, 0, _sprite2->getDrawRect().x2(), 480); + insertScreenMouse(0x00680116); + _asTape = insertSprite<SsScene1705Tape>(this, 8, 1100, 302, 437, 0x01142428); + addCollisionSprite(_asTape); + } else { + setRectList(0x004B6CF0); + setBackground(0x030006E6); + setPalette(0x030006E6); + _paletteHash = 0x15021024; + _palette->addBasePalette(0x030006E6, 0, 256, 0); + _sprite2 = insertStaticSprite(0x273801CE, 1100); + _klaymen->setClipRect(0, 0, _sprite2->getDrawRect().x2(), 480); + insertScreenMouse(0x006E2038); + _asTape = insertSprite<AsScene1201Tape>(this, 8, 1100, 302, 437, 0x9148A011); + addCollisionSprite(_asTape); + } + + addEntity(_palette); + + if (which == 1) { + _palette->addPalette(0xB103B604, 0, 65, 0); + _palette->addBasePalette(0xB103B604, 0, 65, 0); + } else { + _palette->addPalette(_paletteHash, 0, 65, 0); + _palette->addBasePalette(_paletteHash, 0, 65, 0); + } + +} + +Scene2801::~Scene2801() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); +} + +uint32 Scene2801::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4826: + if (sender == _asTape) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004B6C40); + } + break; + case 0x482A: + _palette->addBasePalette(0xB103B604, 0, 65, 0); + _palette->startFadeToPalette(12); + break; + case 0x482B: + _palette->addBasePalette(_paletteHash, 0, 65, 0); + _palette->startFadeToPalette(12); + break; + } + return messageResult; +} + +Scene2802::Scene2802(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _currTuneStatus(0), _countdown1(0), _countdown2(0) { + + SetMessageHandler(&Scene2802::handleMessage); + SetUpdateHandler(&Scene2802::update); + insertPuzzleMouse(0x008810A8, 20, 620); + _smackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, 0x8284C100, true, true, true)); + _currRadioMusicIndex = getGlobalVar(V_CURR_RADIO_MUSIC_INDEX); + // Need to go to the first frame first to load up the palette + _smackerPlayer->gotoFrame(0); + // Now we can actually set the current radio frame + _smackerPlayer->gotoFrame(_currRadioMusicIndex); + _vm->_soundMan->addSound(0x04360A18, 0x422630C2); + _vm->_soundMan->addSound(0x04360A18, 0x00632252); + _vm->_soundMan->addSound(0x04360A18, 0x00372241); + _vm->_soundMan->setSoundVolume(0x00372241, 60); + changeTuneStatus(0, 0); + _vm->_soundMan->playSoundLooping(0x00372241); +} + +Scene2802::~Scene2802() { + _vm->_soundMan->deleteSoundGroup(0x04360A18); + if (_currRadioMusicIndex == 0) { + setGlobalVar(V_RADIO_ROOM_LEFT_DOOR, 1); + setGlobalVar(V_RADIO_ROOM_RIGHT_DOOR, 0); + } else if (_currRadioMusicIndex == getGlobalVar(V_GOOD_RADIO_MUSIC_INDEX)) { + setGlobalVar(V_RADIO_ROOM_LEFT_DOOR, 0); + setGlobalVar(V_RADIO_ROOM_RIGHT_DOOR, 1); + } else { + setGlobalVar(V_RADIO_ROOM_LEFT_DOOR, 0); + setGlobalVar(V_RADIO_ROOM_RIGHT_DOOR, 0); + } + setGlobalVar(V_CURR_RADIO_MUSIC_INDEX, _currRadioMusicIndex); +} + +void Scene2802::update() { + int prevTuneStatus = _currTuneStatus; + uint prevRadioMusicIndex = _currRadioMusicIndex; + + Scene::update(); + if (_countdown1 > 0) + --_countdown1; + else if (_currTuneStatus == 1) + _currTuneStatus = 3; + else if (_currTuneStatus == 4) + _currTuneStatus = 6; + + switch (_currTuneStatus) { + case 2: + if (_currRadioMusicIndex < 90) + incRadioMusicIndex(+1); + _currTuneStatus = 0; + break; + case 3: + if (_countdown2 > 0) + --_countdown2; + else if (_currRadioMusicIndex < 90) { + incRadioMusicIndex(+1); + _countdown2 = 1; + } else + _currTuneStatus = 0; + break; + case 5: + if (_currRadioMusicIndex > 0) + incRadioMusicIndex(-1); + _currTuneStatus = 0; + break; + case 6: + if (_countdown2 > 0) + --_countdown2; + else if (_currRadioMusicIndex > 0) { + incRadioMusicIndex(-1); + _countdown2 = 1; + } else + _currTuneStatus = 0; + break; + + } + + if (prevRadioMusicIndex != _currRadioMusicIndex) + _smackerPlayer->gotoFrame(_currRadioMusicIndex); + + if (prevTuneStatus != _currTuneStatus) + changeTuneStatus(prevTuneStatus, _currTuneStatus); + + //DEBUG>>> + //debug("_currRadioMusicIndex = %d; V_GOOD_RADIO_MUSIC_INDEX = %d", _currRadioMusicIndex, getGlobalVar(V_GOOD_RADIO_MUSIC_INDEX)); + //DEBUG<<< + + if (getGlobalVar(V_RADIO_MOVE_DISH_VIDEO) && prevTuneStatus != _currTuneStatus && _currRadioMusicIndex != 0) { + setGlobalVar(V_RADIO_MOVE_DISH_VIDEO, 0); + leaveScene(1); + } + +} + +uint32 Scene2802::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + int prevTuneStatus = _currTuneStatus; + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } else if (_currTuneStatus == 0) { + if (param.asPoint().x > 180 && param.asPoint().x < 300 && + param.asPoint().y > 130 && param.asPoint().y < 310) { + _currTuneStatus = 4; + } else if (param.asPoint().x > 300 && param.asPoint().x < 400 && + param.asPoint().y > 130 && param.asPoint().y < 310) { + _currTuneStatus = 1; + } + if (_currTuneStatus == 1 || _currTuneStatus == 4) { + _countdown1 = 8; + changeTuneStatus(0, _currTuneStatus); + } + } + break; + case 0x0002: + if (_countdown1 == 0) + _currTuneStatus = 0; + else { + if (_currTuneStatus == 1) + _currTuneStatus = 2; + else if (_currTuneStatus == 4) + _currTuneStatus = 5; + else + _currTuneStatus = 0; + _countdown1 = 0; + } + if (prevTuneStatus != _currTuneStatus) + changeTuneStatus(prevTuneStatus, _currTuneStatus); + break; + } + return 0; +} + +void Scene2802::incRadioMusicIndex(int delta) { + _currRadioMusicIndex += delta; + setGlobalVar(V_CURR_RADIO_MUSIC_INDEX, _currRadioMusicIndex); +} + +void Scene2802::changeTuneStatus(int prevTuneStatus, int newTuneStatus) { + + if (prevTuneStatus == 3 || prevTuneStatus == 6) { + _vm->_soundMan->stopSound(0x422630C2); + _vm->_soundMan->stopSound(0x00632252); + } + + if (newTuneStatus == 0) { + if (_vm->_gameModule->getCurrRadioMusicFileHash() != 0) + _vm->_soundMan->stopSound(0x00632252); + else + _vm->_soundMan->playSoundLooping(0x00632252); + } else if (newTuneStatus == 3 || newTuneStatus == 6) { + _vm->_soundMan->playSoundLooping(0x422630C2); + _vm->_soundMan->playSoundLooping(0x00632252); + } + +} + +AsScene2803LightCord::AsScene2803LightCord(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int16 x, int16 y) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _fileHash1(fileHash1), _fileHash2(fileHash2), + _isPulled(false), _isBusy(false) { + + createSurface(1010, 28, 379); + SetUpdateHandler(&AnimatedSprite::update); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); + _x = x; + _y = y; + stIdle(); +} + +uint32 AsScene2803LightCord::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (!_isBusy && param.asInteger() == calcHash("ClickSwitch")) { + sendMessage(_parentScene, 0x480F, 0); + playSound(0, 0x4E1CA4A0); + } + break; + case 0x480F: + stPulled(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene2803LightCord::hmPulled(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene2803LightCord::stPulled() { + _isBusy = false; + _isPulled = true; + startAnimation(_fileHash2, 0, -1); + SetMessageHandler(&AsScene2803LightCord::hmPulled); + NextState(&AsScene2803LightCord::stIdle); +} + +void AsScene2803LightCord::stIdle() { + _isPulled = false; + startAnimation(_fileHash1, 0, -1); + SetMessageHandler(&AsScene2803LightCord::handleMessage); +} + +void AsScene2803LightCord::setFileHashes(uint32 fileHash1, uint32 fileHash2) { + _fileHash1 = fileHash1; + _fileHash2 = fileHash2; + if (_isPulled) { + startAnimation(_fileHash2, _currFrameIndex, -1); + _isBusy = true; + } else { + startAnimation(_fileHash1, 0, -1); + } +} + +AsScene2803TestTubeOne::AsScene2803TestTubeOne(NeverhoodEngine *vm, uint32 fileHash1, uint32 fileHash2) + : AnimatedSprite(vm, 1200), _fileHash1(fileHash1), _fileHash2(fileHash2) { + + createSurface1(fileHash1, 100); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2803TestTubeOne::handleMessage); + _x = 529; + _y = 326; +} + +uint32 AsScene2803TestTubeOne::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) + startAnimation(_fileHash2, 0, -1); + else + startAnimation(_fileHash1, 0, -1); + break; + } + return messageResult; +} + +AsScene2803Rope::AsScene2803Rope(NeverhoodEngine *vm, Scene *parentScene, int16 x) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + createSurface(990, 68, 476); + SetUpdateHandler(&AnimatedSprite::update); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); + SetMessageHandler(&AsScene2803Rope::handleMessage); + startAnimation(0x9D098C23, 35, 53); + NextState(&AsScene2803Rope::stReleased); + _x = x; + _y = -276; +} + +uint32 AsScene2803Rope::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + startAnimation(0x9D098C23, 50, -1); + SetMessageHandler(&AsScene2803Rope::hmReleased); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene2803Rope::hmReleased(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +void AsScene2803Rope::stReleased() { + startAnimation(0x8258A030, 0, 1); + NextState(&AsScene2803Rope::stHide); +} + +void AsScene2803Rope::stHide() { + stopAnimation(); + setVisible(false); +} + +Scene2803::Scene2803(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _paletteArea(0) { + + static const uint32 kScene2803FileHashes1[] = { + 0, + 0x081000F1, + 0x08100171, + 0x08100271 + }; + + static const uint32 kScene2803FileHashes2[] = { + 0, + 0x286800D4, + 0x286806D4, + 0x28680AD4 + }; + + setGlobalVar(V_BEEN_SHRINKING_ROOM, 1); + _vm->gameModule()->initTestTubes1Puzzle(); + + SetMessageHandler(&Scene2803::handleMessage); + + loadDataResource(0x00900849); + + _background = new Background(_vm, 0); + _background->createSurface(0, 640, 480); + addBackground(_background); + + setPalette(0x412A423E); + addEntity(_palette); + + insertScreenMouse(0xA423A41A); + + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) == 0) { + _asTestTubeOne = (StaticSprite*)insertStaticSprite(0x66121222, 100); + } else { + _asTestTubeOne = (StaticSprite*)insertSprite<AsScene2803TestTubeOne>( + kScene2803FileHashes1[getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0)], + kScene2803FileHashes2[getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0)]); + } + + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 3) + _asTestTubeTwo = (StaticSprite*)insertStaticSprite(0x64330236, 100); + + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 3) + _asTestTubeThree = (StaticSprite*)insertStaticSprite(0x2E4A22A2, 100); + + _asLightCord = insertSprite<AsScene2803LightCord>(this, 0x8FAD5932, 0x276E1A3D, 578, 200); + _sprite3 = (StaticSprite*)insertStaticSprite(0xA40EF2FB, 1100); + _sprite4 = (StaticSprite*)insertStaticSprite(0x0C03AA23, 1100); + _sprite5 = (StaticSprite*)insertStaticSprite(0x2A822E2E, 1100); + _sprite6 = (StaticSprite*)insertStaticSprite(0x2603A202, 1100); + _sprite7 = (StaticSprite*)insertStaticSprite(0x24320220, 1100); + _sprite8 = (StaticSprite*)insertStaticSprite(0x3C42022F, 1100); + _sprite9 = (StaticSprite*)insertStaticSprite(0x341A0237, 1100); + _sprite10 = insertStaticSprite(0x855820A3, 1200); + + _clipRectsFloor[0].x1 = 0; + _clipRectsFloor[0].y1 = 0; + _clipRectsFloor[0].x2 = 640; + _clipRectsFloor[0].y2 = _sprite8->getDrawRect().y2(); + + _clipRectsFloor[1].x1 = _sprite8->getDrawRect().x2(); + _clipRectsFloor[1].y1 = _sprite8->getDrawRect().y2(); + _clipRectsFloor[1].x2 = 640; + _clipRectsFloor[1].y2 = 480; + + _clipRectsStairs[0].x1 = _sprite5->getDrawRect().x; + _clipRectsStairs[0].y1 = 0; + _clipRectsStairs[0].x2 = _sprite5->getDrawRect().x2(); + _clipRectsStairs[0].y2 = _sprite5->getDrawRect().y2(); + + _clipRectsStairs[1].x1 = _sprite6->getDrawRect().x; + _clipRectsStairs[1].y1 = 0; + _clipRectsStairs[1].x2 = _sprite3->getDrawRect().x; + _clipRectsStairs[1].y2 = _sprite6->getDrawRect().y2(); + + _clipRectsStairs[2].x1 = _sprite3->getDrawRect().x; + _clipRectsStairs[2].y1 = 0; + _clipRectsStairs[2].x2 = _sprite4->getDrawRect().x2(); + _clipRectsStairs[2].y2 = 480; + + if (which < 0) { + insertKlaymen<KmScene2803>(302, 445, _clipRectsFloor, 2); + setMessageList(0x004B79F0); + klaymenFloor(); + } else if (which == 1) { + insertKlaymen<KmScene2803>(200, 445, _clipRectsFloor, 2); + setMessageList(0x004B79C8); + klaymenFloor(); + } else if (which == 3) { + NPoint pt = _dataResource.getPoint(0xC2A08694); + insertKlaymen<KmScene2803>(pt.x, pt.y, _clipRectsStairs, 3); + setMessageList(0x004B7A00); + klaymenStairs(); + } else if (which == 5) { + insertKlaymen<KmScene2803>(253, 298, _clipRectsStairs, 3); + setMessageList(0x004B7A00); + klaymenStairs(); + } else if (which == 6) { + _asRope = insertSprite<AsScene2803Rope>(this, 384); + _asRope->setClipRect(0, 25, 640, 480); + insertKlaymen<KmScene2803>(384, 0, _clipRectsFloor, 2); + sendEntityMessage(_klaymen, 0x1014, _asRope); + _klaymen->setClipRect(0, 25, 640, 480); + setMessageList(0x004B7A78); + klaymenFloor(); + } else if (which == 2) { + insertKlaymen<KmScene2803>(400, 445, _clipRectsFloor, 2); + setMessageList(0x004B79F8); + klaymenFloor(); + } else { + insertKlaymen<KmScene2803>(50, 231, _clipRectsStairs, 3); + setMessageList(0x004B79C0); + klaymenStairs(); + } + + changeBackground(); + +} + +void Scene2803::upKlaymenStairs() { + if (_klaymen->getX() < 350) { + setPaletteArea0(); + } else { + setPaletteArea1(); + } + Scene::update(); +} + +uint32 Scene2803::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x480F: + toggleBackground(); + // NOTE Intentional fall-through + case 0x100D: + if (param.asInteger() == 0x84251F82) + setMessageList(0x004B7A50); + else if (param.asInteger() == 0x4254A2D2) + setMessageList(0x004B7A58); + else if (param.asInteger() == 0xE90A40A0) + setMessageList(0x004B7A08); + else if (param.asInteger() == 0x482D1210) + setMessageList(0x004B7A30); + else if (param.asInteger() == 0x802402B2) { + sendEntityMessage(_klaymen, 0x1014, _asLightCord); + setMessageList(0x004B7A68); + } else if (param.asInteger() == 0x9626F390) + setMessageList(0x004B7A88); + break; + case 0x482A: + klaymenStairs(); + setPaletteArea1(); + break; + case 0x482B: + klaymenFloor(); + setPaletteArea0(); + break; + } + return messageResult; +} + +void Scene2803::klaymenStairs() { + SetUpdateHandler(&Scene2803::upKlaymenStairs); + _klaymen->getSurface()->setClipRects(_clipRectsStairs, 3); + sendMessage(_klaymen, 0x482C, 0xE5A48297); + _sprite3->setVisible(true); + _sprite4->setVisible(true); + _sprite5->setVisible(true); + _sprite6->setVisible(true); + _sprite7->setVisible(true); + _sprite8->setVisible(false); + _sprite9->setVisible(false); +} + +void Scene2803::klaymenFloor() { + SetUpdateHandler(&Scene::update); + _klaymen->getSurface()->setClipRects(_clipRectsFloor, 2); + sendMessage(_klaymen, 0x482C, 0); + _sprite3->setVisible(false); + _sprite4->setVisible(false); + _sprite5->setVisible(false); + _sprite6->setVisible(false); + _sprite7->setVisible(false); + _sprite8->setVisible(true); + _sprite9->setVisible(true); +} + +void Scene2803::toggleBackground() { + setGlobalVar(V_SHRINK_LIGHTS_ON, getGlobalVar(V_SHRINK_LIGHTS_ON) ? 0 : 1); + changeBackground(); +} + +void Scene2803::changeBackground() { + if (getGlobalVar(V_SHRINK_LIGHTS_ON)) { + _asLightCord->setFileHashes(0x8FAD5932, 0x276E1A3D); + _background->load(0x412A423E); + _palette->addPalette(0x412A423E, 0, 256, 0); + _palette->addBasePalette(0x412A423E, 0, 256, 0); + _sprite3->loadSprite(0xA40EF2FB); + _sprite4->loadSprite(0x0C03AA23); + _sprite5->loadSprite(0x2A822E2E); + _sprite6->loadSprite(0x2603A202); + _sprite7->loadSprite(0x24320220); + _mouseCursor->load(0xA423A41A); + _mouseCursor->updateCursor(); + _sprite8->loadSprite(0x3C42022F); + _sprite9->loadSprite(0x341A0237); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) == 0) + _asTestTubeOne->loadSprite(0x66121222); + else + sendMessage(_asTestTubeOne, 0x2000, 0); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 3) + _asTestTubeTwo->loadSprite(0x64330236); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 3) + _asTestTubeThree->loadSprite(0x2E4A22A2); + _sprite10->setVisible(true); + } else { + _asLightCord->setFileHashes(0xAFAD591A, 0x276E321D); + _background->load(0x29800A01); + _palette->addPalette(0x29800A01, 0, 256, 0); + _palette->addBasePalette(0x29800A01, 0, 256, 0); + _sprite3->loadSprite(0x234340A0); + _sprite4->loadSprite(0x16202200); + _sprite5->loadSprite(0x1030169A); + _sprite6->loadSprite(0x1600A6A8); + _sprite7->loadSprite(0xD0802EA0); + _mouseCursor->load(0x00A05290); + _mouseCursor->updateCursor(); + _sprite8->loadSprite(0x108012C1); + _sprite9->loadSprite(0x708072E0); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) != 0) + sendMessage(_asTestTubeOne, 0x2000, 1); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 3) + _asTestTubeTwo->loadSprite(0xD48077A0); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 3) + _asTestTubeThree->loadSprite(0x30022689); + _sprite10->setVisible(false); + } + updatePaletteArea(); +} + +void Scene2803::setPaletteArea0() { + if (_paletteArea != 0) { + _paletteArea = 0; + updatePaletteArea(); + } +} + +void Scene2803::setPaletteArea1() { + if (_paletteArea != 1) { + _paletteArea = 1; + updatePaletteArea(); + } +} + +void Scene2803::updatePaletteArea() { + uint32 fadePaletteHash; + if (getGlobalVar(V_SHRINK_LIGHTS_ON)) + fadePaletteHash = (_paletteArea == 1) ? 0xB103B604 : 0x412A423E; + else + fadePaletteHash = (_paletteArea == 1) ? 0x0263D144 : 0x29800A01; + _palette->addBasePalette(fadePaletteHash, 0, 64, 0); + _palette->startFadeToPalette(12); +} + +Scene2803Small::Scene2803Small(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _paletteArea(0) { + + static const uint32 kScene2803SmallFileHashes1[] = { + 0, 0x081000F1, 0x08100171, 0x08100271 + }; + + static const uint32 kScene2803SmallFileHashes2[] = { + 0, 0x286800D4, 0x286806D4, 0x28680AD4 + }; + + SetMessageHandler(&Scene2803Small::handleMessage); + + loadDataResource(0x81120132); + insertScreenMouse(0x00A05290); + + insertSprite<AsScene2803LightCord>(this, 0xAFAD591A, 0x276E321D, 578, 200); + + if (getGlobalVar(V_SHRINK_LIGHTS_ON)) { + setBackground(0x412A423E); + setPalette(0x412A423E); + _palette->addBasePalette(0x412A423E, 0, 256, 0); + addEntity(_palette); + _sprite1 = insertStaticSprite(0x0C03AA23, 1100); + _sprite2 = insertStaticSprite(0x24320220, 1100); + _sprite3 = insertStaticSprite(0x1A032204, 1100); + _sprite4 = insertStaticSprite(0x18032204, 1100); + _sprite5 = insertStaticSprite(0x34422912, 1100); + _sprite6 = insertStaticSprite(0x3C42022F, 1100); + _sprite7 = insertStaticSprite(0x341A0237, 1100); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) == 0) + insertStaticSprite(0x66121222, 100); + else + insertSprite<AnimatedSprite>(kScene2803SmallFileHashes1[getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0)], 100, 529, 326); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 3) + insertStaticSprite(0x64330236, 100); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 3) + insertStaticSprite(0x2E4A22A2, 100); + } else { + setBackground(0x29800A01); + setPalette(0x29800A01); + _palette->addBasePalette(0x29800A01, 0, 256, 0); + addEntity(_palette); + _sprite1 = insertStaticSprite(0x16202200, 1100); + _sprite2 = insertStaticSprite(0xD0802EA0, 1100); + _sprite3 = insertStaticSprite(0x780C2E30, 1100); + _sprite4 = insertStaticSprite(0x700C2E30, 1100); + _sprite5 = insertStaticSprite(0x102CE6E1, 900); + _sprite6 = insertStaticSprite(0x108012C1, 1100); + _sprite7 = insertStaticSprite(0x708072E0, 1100); + insertStaticSprite(0x90582EA4, 100); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) != 0) + insertSprite<AnimatedSprite>(kScene2803SmallFileHashes2[getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0)], 100, 529, 326); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 3) + insertStaticSprite(0xD48077A0, 100); + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 3) + insertStaticSprite(0x30022689, 100); + } + + _sprite6->setVisible(false); + _sprite7->setVisible(false); + + if (which < 0) { + insertKlaymen<KmScene2803Small>(479, 435); + klaymenFloor(); + setMessageList(0x004B60D8); + } else if (which == 3) { + NPoint pt = _dataResource.getPoint(0x096520ED); + insertKlaymen<KmScene2803Small>(pt.x, pt.y); + klaymenSlope(); + setMessageList(0x004B6100); + _klaymen->setRepl(64, 0); + } else if (which == 4) { + NPoint pt = _dataResource.getPoint(0x20C6238D); + insertKlaymen<KmScene2803Small>(pt.x, pt.y); + klaymenSlope(); + setMessageList(0x004B60F8); + _klaymen->setRepl(64, 0); + } else if (which == 5) { + NPoint pt = _dataResource.getPoint(0x2146690D); + insertKlaymen<KmScene2803Small>(pt.x, pt.y); + klaymenSlope(); + setMessageList(0x004B6100); + _klaymen->setRepl(64, 0); + } else if (which == 2) { + NPoint pt = _dataResource.getPoint(0x104C03ED); + insertKlaymen<KmScene2803Small>(pt.x, pt.y); + klaymenFloor(); + setMessageList(0x004B6138); + } else { + insertKlaymen<KmScene2803Small>(135, 444); + klaymenFloor(); + setMessageList(0x004B60E0, false); + _sprite6->setVisible(true); + _sprite7->setVisible(true); + } + +} + +uint32 Scene2803Small::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xB4E4884C) { + setMessageList(0x004B6180); + } else if (param.asInteger() == 0xB1FDAB2E) { + NPoint pt = _dataResource.getPoint(0x0D84A1AD); + _klaymen->setX(pt.x); + _klaymen->setY(pt.y); + _klaymen->updateBounds(); + klaymenFloor(); + _klaymen->setClipRect(517, 401, 536, 480); + setMessageList(0x004B6198); + } else if (param.asInteger() == 0xB00C7C48) { + setMessageList(0x004B6108); + } else if (param.asInteger() == 0x61F64346) { + setMessageList(0x004B6150); + } else if (param.asInteger() == 0xAC69A28D) { + setMessageList(0x004B6168); + } else if (param.asInteger() == 0x00086212) { + _klaymen->setClipRect(0, 0, 560, 315); + _klaymen->setX(560); + _klaymen->setY(315); + _klaymen->updateBounds(); + klaymenSlope(); + setMessageList(0x004B61A0); + } else if (param.asInteger() == 0x002CAA68) { + setMessageList(0x004B61A8); + } + break; + case 0x482A: + if (_klaymen->getX() < 200) { + setPaletteArea3(); + } else if (_klaymen->getX() < 500) { + setSurfacePriority(_sprite5->getSurface(), 1100); + sendMessage(_klaymen, 0x482C, 0); + setPaletteArea2(); + } else { + _klaymen->setClipRect(517, 401, 536, 480); + setPaletteArea2(); + } + break; + case 0x482B: + _sprite6->setVisible(false); + _sprite7->setVisible(false); + _klaymen->setClipRect(0, 0, 640, 480); + setSurfacePriority(_sprite5->getSurface(), 900); + sendMessage(_klaymen, 0x482C, 0x2086222D); + break; + } + return 0; +} + +void Scene2803Small::upKlaymenSlope() { + if (_klaymen->getX() < 388) { + _klaymen->setClipRect(_sprite3->getDrawRect().x, 0, 640, _sprite3->getDrawRect().y2()); + setPaletteArea0(); + } else if (_klaymen->getX() < 500) { + _klaymen->setClipRect(0, 0, _sprite1->getDrawRect().x2(), _sprite1->getDrawRect().y2()); + setPaletteArea1(); + } + Scene::update(); +} + +void Scene2803Small::upKlaymenFloor() { + if (_klaymen->getX() > 194 && _klaymen->getX() < 273) + setPaletteArea2(); + else if (_klaymen->getX() > 155 && _klaymen->getX() < 300) + setPaletteArea0(); + Scene::update(); +} + +void Scene2803Small::klaymenSlope() { + SetUpdateHandler(&Scene2803Small::upKlaymenSlope); + sendMessage(_klaymen, 0x482C, 0x23C630D9); + _klaymen->setClipRect(0, 0, _sprite1->getDrawRect().x2(), _sprite1->getDrawRect().y2()); + _klaymen->setRepl(64, 0); + _sprite1->setVisible(true); +} + +void Scene2803Small::klaymenFloor() { + SetUpdateHandler(&Scene2803Small::upKlaymenFloor); + sendMessage(_klaymen, 0x482C, 0x2086222D); + _klaymen->setClipRect(0, 0, 640, 480); + _klaymen->clearRepl(); + _sprite1->setVisible(false); +} + +void Scene2803Small::setPaletteArea0() { + if (_paletteArea != 0) { + _paletteArea = 0; + updatePaletteArea(false); + } +} + +void Scene2803Small::setPaletteArea1() { + if (_paletteArea != 1) { + _paletteArea = 1; + updatePaletteArea(false); + } +} + +void Scene2803Small::setPaletteArea2() { + if (_paletteArea != 2) { + _paletteArea = 2; + updatePaletteArea(false); + } +} + +void Scene2803Small::setPaletteArea3() { + if (_paletteArea != 3) { + _paletteArea = 3; + updatePaletteArea(true); + } +} + +void Scene2803Small::updatePaletteArea(bool instantly) { + if (getGlobalVar(V_SHRINK_LIGHTS_ON)) { + switch (_paletteArea) { + case 1: + _palette->addBasePalette(0x0A938204, 0, 64, 0); + break; + case 2: + _palette->addBasePalette(0xB103B604, 0, 64, 0); + break; + case 3: + _palette->fillBaseBlack(0, 64); + break; + default: + _palette->addBasePalette(0x412A423E, 0, 64, 0); + break; + } + } else { + switch (_paletteArea) { + case 2: + _palette->addBasePalette(0x0263D144, 0, 64, 0); + break; + case 3: + _palette->fillBaseBlack(0, 64); + break; + default: + _palette->addBasePalette(0x29800A01, 0, 64, 0); + break; + } + } + _palette->startFadeToPalette(instantly ? 0 : 12); +} + +SsScene2804RedButton::SsScene2804RedButton(NeverhoodEngine *vm, Scene2804 *parentScene) + : StaticSprite(vm, 900), _countdown(0), _parentScene(parentScene) { + + loadSprite(getGlobalVar(V_SHRINK_LIGHTS_ON) ? 0x51A10202 : 0x11814A21, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400); + setVisible(false); + SetUpdateHandler(&SsScene2804RedButton::update); + SetMessageHandler(&SsScene2804RedButton::handleMessage); + loadSound(0, 0x44241240); +} + +void SsScene2804RedButton::update() { + updatePosition(); + if (_countdown != 0 && (--_countdown) == 0) { + setVisible(false); + } +} + +uint32 SsScene2804RedButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0 && !_parentScene->isWorking()) { + playSound(0); + setVisible(true); + _countdown = 4; + sendMessage(_parentScene, 0x2000, 0); + } + messageResult = 1; + break; + } + return messageResult; +} + +SsScene2804LightCoil::SsScene2804LightCoil(NeverhoodEngine *vm) + : StaticSprite(vm, 900) { + + loadSprite(0x8889B008, kSLFDefDrawOffset | kSLFDefPosition, 400); + setVisible(false); + SetMessageHandler(&SsScene2804LightCoil::handleMessage); +} + +uint32 SsScene2804LightCoil::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + setVisible(true); + updatePosition(); + messageResult = 1; + break; + case 0x2003: + setVisible(false); + updatePosition(); + messageResult = 1; + break; + } + return messageResult; +} + +SsScene2804LightTarget::SsScene2804LightTarget(NeverhoodEngine *vm) + : StaticSprite(vm, 900) { + + loadSprite(0x06092132, kSLFDefDrawOffset | kSLFDefPosition, 400); + setVisible(false); + SetMessageHandler(&SsScene2804LightTarget::handleMessage); +} + +uint32 SsScene2804LightTarget::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2004: + setVisible(true); + updatePosition(); + messageResult = 1; + break; + case 0x2005: + setVisible(false); + updatePosition(); + messageResult = 1; + break; + } + return messageResult; +} + +SsScene2804Flash::SsScene2804Flash(NeverhoodEngine *vm) + : StaticSprite(vm, 900) { + + loadSprite(0x211003A0, kSLFDefDrawOffset | kSLFDefPosition, 400); + setVisible(false); + loadSound(0, 0xCB36BA54); +} + +void SsScene2804Flash::show() { + setVisible(true); + updatePosition(); + playSound(0); +} + +SsScene2804BeamCoilBody::SsScene2804BeamCoilBody(NeverhoodEngine *vm) + : StaticSprite(vm, 900) { + + loadSprite(0x9A816000, kSLFDefDrawOffset | kSLFDefPosition, 400); + setVisible(false); +} + +AsScene2804CrystalWaves::AsScene2804CrystalWaves(NeverhoodEngine *vm, uint crystalIndex) + : AnimatedSprite(vm, 1100), _crystalIndex(crystalIndex) { + + static const NPoint kAsScene2804CrystalWavesPoints[] = { + {323, 245}, + {387, 76}, + {454, 260}, + {527, 70} + }; + + _x = kAsScene2804CrystalWavesPoints[crystalIndex].x; + _y = kAsScene2804CrystalWavesPoints[crystalIndex].y; + createSurface1(0x840C41F0, 1200); + if (crystalIndex & 1) + setDoDeltaY(1); + setVisible(false); + _needRefresh = true; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&Sprite::handleMessage); +} + +void AsScene2804CrystalWaves::show() { + setVisible(true); + startAnimation(0x840C41F0, 0, -1); +} + +void AsScene2804CrystalWaves::hide() { + setVisible(false); + stopAnimation(); +} + +static const int16 kAsScene2804CrystalFrameNums[] = { + 0, 6, 2, 8, 1, 10, 0, 0 +}; + +static const uint32 kAsScene2804CrystalFileHashes[] = { + 0x000540B0, + 0x001280D0, + 0x003D0010, + 0x00620190, + 0x00DC0290 +}; + +AsScene2804Crystal::AsScene2804Crystal(NeverhoodEngine *vm, AsScene2804CrystalWaves *asCrystalWaves, uint crystalIndex) + : AnimatedSprite(vm, 1100), _asCrystalWaves(asCrystalWaves), _crystalIndex(crystalIndex), _isShowing(false) { + + static const NPoint kAsScene2804CrystalPoints[] = { + {204, 196}, + {272, 316}, + {334, 206}, + {410, 334}, + {470, 180} + }; + + _colorNum = (int16)getSubVar(VA_CURR_CRYSTAL_COLORS, crystalIndex); + _isLightOn = getGlobalVar(V_SHRINK_LIGHTS_ON) != 0; + if (_isLightOn) { + _x = kAsScene2804CrystalPoints[crystalIndex].x; + _y = kAsScene2804CrystalPoints[crystalIndex].y; + createSurface1(0x108DFB12, 1200); + startAnimation(0x108DFB12, kAsScene2804CrystalFrameNums[_colorNum], -1); + _needRefresh = true; + _newStickFrameIndex = kAsScene2804CrystalFrameNums[_colorNum]; + } else { + _x = 320; + _y = 240; + createSurface1(kAsScene2804CrystalFileHashes[crystalIndex], 1200); + startAnimation(kAsScene2804CrystalFileHashes[crystalIndex], _colorNum, -1); + setVisible(false); + _needRefresh = true; + _newStickFrameIndex = _colorNum; + } + loadSound(0, 0x725294D4); + SetUpdateHandler(&AnimatedSprite::update); +} + +void AsScene2804Crystal::show() { + if (!_isLightOn) { + setVisible(true); + _isShowing = true; + if (_asCrystalWaves) + _asCrystalWaves->show(); + playSound(0); + } +} + +void AsScene2804Crystal::hide() { + if (!_isLightOn) { + setVisible(false); + _isShowing = false; + if (_asCrystalWaves) + _asCrystalWaves->hide(); + } +} + +void AsScene2804Crystal::activate() { + if (!_isShowing) { + int16 frameNum = kAsScene2804CrystalFrameNums[_colorNum]; + _colorNum++; + if (_colorNum >= 6) + _colorNum = 0; + if (_isLightOn) { + startAnimation(0x108DFB12, frameNum, kAsScene2804CrystalFrameNums[_colorNum]); + _playBackwards = kAsScene2804CrystalFrameNums[_colorNum] < _colorNum; + _newStickFrameIndex = kAsScene2804CrystalFrameNums[_colorNum]; + } else { + startAnimation(kAsScene2804CrystalFileHashes[_crystalIndex], _colorNum, -1); + _newStickFrameIndex = _colorNum; + } + setSubVar(VA_CURR_CRYSTAL_COLORS, _crystalIndex, _colorNum); + } +} + +SsScene2804CrystalButton::SsScene2804CrystalButton(NeverhoodEngine *vm, Scene2804 *parentScene, AsScene2804Crystal *asCrystal, uint crystalIndex) + : StaticSprite(vm, 900), _countdown(0), _parentScene(parentScene), _asCrystal(asCrystal), _crystalIndex(crystalIndex) { + + static const uint32 kSsScene2804CrystalButtonFileHashes1[] = { + 0x911101B0, + 0x22226001, + 0x4444A362, + 0x888925A4, + 0x11122829 + }; + + static const uint32 kSsScene2804CrystalButtonFileHashes2[] = { + 0xB500A1A0, + 0x6A012021, + 0xD4022322, + 0xA8042525, + 0x5008292B + }; + + loadSprite(getGlobalVar(V_SHRINK_LIGHTS_ON) ? kSsScene2804CrystalButtonFileHashes1[crystalIndex] : kSsScene2804CrystalButtonFileHashes2[crystalIndex], + kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400); + setVisible(false); + loadSound(0, 0x44045140); + SetUpdateHandler(&SsScene2804CrystalButton::update); + SetMessageHandler(&SsScene2804CrystalButton::handleMessage); +} + +void SsScene2804CrystalButton::update() { + updatePosition(); + if (_countdown != 0 && (--_countdown) == 0) { + setVisible(false); + } +} + +uint32 SsScene2804CrystalButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0 && !_parentScene->isWorking()) { + playSound(0); + setVisible(true); + _countdown = 4; + _asCrystal->activate(); + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene2804BeamCoil::AsScene2804BeamCoil(NeverhoodEngine *vm, Scene *parentScene, SsScene2804BeamCoilBody *ssBeamCoilBody) + : AnimatedSprite(vm, 1400), _parentScene(parentScene), _ssBeamCoilBody(ssBeamCoilBody), _countdown(0) { + + createSurface1(0x00494891, 1000); + _x = 125; + _y = 184; + setVisible(false); + _needRefresh = true; + AnimatedSprite::updatePosition(); + loadSound(0, 0x6352F051); + _vm->_soundMan->addSound(0xC5EA0B28, 0xEF56B094); + SetUpdateHandler(&AsScene2804BeamCoil::update); + SetMessageHandler(&AsScene2804BeamCoil::handleMessage); +} + +AsScene2804BeamCoil::~AsScene2804BeamCoil() { + _vm->_soundMan->deleteSoundGroup(0xC5EA0B28); +} + +void AsScene2804BeamCoil::update() { + updateAnim(); + updatePosition(); + if (_countdown != 0 && (--_countdown) == 0) { + sendMessage(_parentScene, 0x2001, 0); + } +} + +uint32 AsScene2804BeamCoil::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2002: + show(); + _countdown = 92; + messageResult = 1; + break; + case 0x2003: + hide(); + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene2804BeamCoil::show() { + _ssBeamCoilBody->setVisible(true); + setVisible(true); + startAnimation(0x00494891, 0, -1); + playSound(0); + SetMessageHandler(&AsScene2804BeamCoil::hmBeaming); + NextState(&AsScene2804BeamCoil::stBeaming); +} + +void AsScene2804BeamCoil::hide() { + stopAnimation(); + SetMessageHandler(&AsScene2804BeamCoil::handleMessage); + setVisible(false); + _ssBeamCoilBody->setVisible(false); + _vm->_soundMan->stopSound(0xEF56B094); +} + +void AsScene2804BeamCoil::stBeaming() { + startAnimation(0x00494891, 93, -1); + NextState(&AsScene2804BeamCoil::stBeaming); + _vm->_soundMan->playSoundLooping(0xEF56B094); +} + +uint32 AsScene2804BeamCoil::hmBeaming(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +AsScene2804BeamTarget::AsScene2804BeamTarget(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1400) { + + createSurface1(0x03842000, 1000); + _x = 475; + _y = 278; + setVisible(false); + _needRefresh = true; + updatePosition(); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2804BeamTarget::handleMessage); +} + +uint32 AsScene2804BeamTarget::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2004: + setVisible(true); + startAnimation(0x03842000, 0, -1); + messageResult = 1; + break; + case 0x2005: + setVisible(false); + stopAnimation(); + messageResult = 1; + break; + } + return messageResult; +} + +Scene2804::Scene2804(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _countdown1(0), _countdown2(0), _countdown3(0), + _beamStatus(0), _isSolved(false), _isWorking(false) { + + _vm->gameModule()->initCrystalColorsPuzzle(); + + SetMessageHandler(&Scene2804::handleMessage); + SetUpdateHandler(&Scene2804::update); + + if (getGlobalVar(V_SHRINK_LIGHTS_ON)) { + setBackground(0xA1D03005); + setPalette(0xA1D03005); + addEntity(_palette); + insertPuzzleMouse(0x03001A15, 20, 620); + _asCoil = insertSprite<SsScene2804LightCoil>(); + _asTarget = insertSprite<SsScene2804LightTarget>(); + } else { + SsScene2804BeamCoilBody *ssBeamCoilBody; + setBackground(0x01C01414); + setPalette(0x01C01414); + addEntity(_palette); + insertPuzzleMouse(0x01410014, 20, 620); + ssBeamCoilBody = insertSprite<SsScene2804BeamCoilBody>(); + _asCoil = insertSprite<AsScene2804BeamCoil>(this, ssBeamCoilBody); + _asTarget = insertSprite<AsScene2804BeamTarget>(); + _ssFlash = insertSprite<SsScene2804Flash>(); + } + + _ssRedButton = insertSprite<SsScene2804RedButton>(this); + addCollisionSprite(_ssRedButton); + + for (uint crystalIndex = 0; crystalIndex < 5; crystalIndex++) { + AsScene2804CrystalWaves *asCrystalWaves = NULL; + if (crystalIndex < 4 && getGlobalVar(V_SHRINK_LIGHTS_ON) == 0) + asCrystalWaves = insertSprite<AsScene2804CrystalWaves>(crystalIndex); + _asCrystals[crystalIndex] = insertSprite<AsScene2804Crystal>(asCrystalWaves, crystalIndex); + _ssCrystalButtons[crystalIndex] = insertSprite<SsScene2804CrystalButton>(this, _asCrystals[crystalIndex], crystalIndex); + addCollisionSprite(_ssCrystalButtons[crystalIndex]); + } + +} + +uint32 Scene2804::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: + _isWorking = true; + sendMessage(_asCoil, 0x2002, 0); + if (getGlobalVar(V_SHRINK_LIGHTS_ON)) { + sendMessage(_asTarget, 0x2004, 0); + _countdown2 = 48; + } + break; + case 0x2001: + _countdown3 = 2; + _isSolved = true; + _beamStatus = 0; + for (uint index = 0; index < 5; index++) + if (_asCrystals[index]->getColorNum() != (int16)getSubVar(VA_GOOD_CRYSTAL_COLORS, index)) + _isSolved = false; + _countdown2 = 48; + break; + } + return 0; +} + +void Scene2804::update() { + + Scene::update(); + + if (_countdown1 != 0 && (--_countdown1) == 0) { + leaveScene(0); + } + + if (_countdown2 != 0 && (--_countdown2) == 0) { + _isWorking = false; + sendMessage(_asCoil, 0x2003, 0); + sendMessage(_asTarget, 0x2005, 0); + for (uint index = 0; index < 5; index++) + _asCrystals[index]->hide(); + } + + if (_countdown3 != 0 && (--_countdown3) == 0) { + if (_beamStatus == 5) { + sendMessage(_asTarget, 0x2004, 0); + if (_isSolved) { + _palette->fillBaseWhite(0, 256); + _palette->startFadeToPalette(18); + setGlobalVar(V_KLAYMEN_SMALL, 1); + _countdown1 = 48; + } + } else if (_beamStatus == 6) { + if (_isSolved) + _ssFlash->show(); + } else { + _asCrystals[_beamStatus]->show(); + } + _beamStatus++; + if (_beamStatus < 6) + _countdown3 = 2; + else if (_beamStatus < 7) + _countdown3 = 4; + } + +} + +Scene2805::Scene2805(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene2805::handleMessage); + + setBackground(0x08021E04); + setPalette(0x08021E04); + _palette->addPalette(0x8A6B1F91, 0, 65, 0); + insertScreenMouse(0x21E00088); + + _sprite1 = insertStaticSprite(0x008261E7, 1100); + _sprite2 = insertStaticSprite(0x020CE421, 1100); + + if (which < 0) { + insertKlaymen<KmScene2805>(380, 338); + setMessageList(0x004AE1C8); + sendMessage(this, 0x2000, 0); + } else if (which == 1) { + insertKlaymen<KmScene2805>(493, 338); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004AE1D0, false); + sendMessage(this, 0x2000, 1); + } else if (which == 2) { + insertKlaymen<KmScene2805>(493, 338); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004AE288, false); + sendMessage(this, 0x2000, 1); + } else if (which == 3) { + insertKlaymen<KmScene2805>(493, 338); + sendMessage(_klaymen, 0x2000, 1); + setMessageList(0x004AE1E0, false); + sendMessage(this, 0x2000, 1); + } else { + insertKlaymen<KmScene2805>(340, 338); + setMessageList(0x004AE1C0); + sendMessage(this, 0x2000, 0); + } + + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, _sprite2->getDrawRect().x2(), 480); + +} + +uint32 Scene2805::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + if (param.asInteger()) { + setRectList(0x004AE318); + _klaymen->setKlaymenIdleTable3(); + } else { + setRectList(0x004AE308); + _klaymen->setKlaymenIdleTable1(); + } + break; + } + return 0; +} + +AsScene2806Spew::AsScene2806Spew(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200) { + + createSurface1(0x04211490, 1200); + _x = 378; + _y = 423; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2806Spew::handleMessage); + setDoDeltaX(1); + setVisible(false); +} + +uint32 AsScene2806Spew::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + playSound(0, 0x48640244); + startAnimation(0x04211490, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +Scene2806::Scene2806(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + SetMessageHandler(&Scene2806::handleMessage); + SetUpdateHandler(&Scene2806::update); + + loadDataResource(0x98182003); + loadHitRectList(); + + _pointList = _dataResource.getPointArray(0x3606A422); + + insertScreenMouse(0x22114C13); + setBackground(0xC1B22110); + setPalette(0xC1B22110); + + _sprite1 = insertStaticSprite(0xA21F82CB, 1100); + _clipRects[0].x1 = _sprite1->getDrawRect().x; + _clipRects[0].y1 = _sprite1->getDrawRect().y; + _clipRects[0].x2 = _sprite1->getDrawRect().x2(); + _clipRects[0].y2 = _sprite1->getDrawRect().y2(); + + _sprite2 = insertStaticSprite(0x92035301, 1100); + _clipRects[1].y2 = _sprite2->getDrawRect().y2(); + + _sprite3 = insertStaticSprite(0x3182220E, 1100); + + _sprite4 = insertStaticSprite(0x72090342, 1100); + _clipRects[1].x1 = _sprite4->getDrawRect().x; + _clipRects[1].y1 = _sprite4->getDrawRect().y; + + tempSprite = insertStaticSprite(0xD2012C02, 1100); + _clipRects[2].x1 = tempSprite->getDrawRect().x; + _clipRects[2].y2 = tempSprite->getDrawRect().y2(); + _clipRects[3].y1 = tempSprite->getDrawRect().y2(); + _clipRects[1].x2 = tempSprite->getDrawRect().x; + + tempSprite = insertStaticSprite(0x72875F42, 1100); + _clipRects[3].x1 = tempSprite->getDrawRect().x; + + insertStaticSprite(0x0201410A, 1100); + insertStaticSprite(0x72875F42, 1100); + + _asSpew = insertSprite<AsScene2806Spew>(); + + _clipRects[2].y1 = 0; + _clipRects[3].y2 = 480; + _clipRects[2].x2 = 640; + _clipRects[3].x2 = 640; + + if (which < 0) { + insertKlaymen<KmScene2806>(441, 423, false, _clipRects, 4); + setMessageList(0x004AF098); + } else if (which == 1) { + insertKlaymen<KmScene2806>(378, 423, false, _clipRects, 4); + setMessageList(0x004AF098); + } else if (which == 2) { + insertKlaymen<KmScene2806>(378, 423, false, _clipRects, 4); + setMessageList(0x004AF0C8, false); + } else if (which == 3) { + insertKlaymen<KmScene2806>(378, 423, true, _clipRects, 4); + setMessageList(0x004AF0A0, false); + setGlobalVar(V_KLAYMEN_SMALL, 0); + } else { + insertKlaymen<KmScene2806>(670, 423, false, _clipRects, 4); + setMessageList(0x004AF090); + } + + _pointIndex = -1; + findClosestPoint(); + +} + +uint32 Scene2806::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x44262B12) { + setMessageList(0x004AF0E0); + } + break; + case 0x2000: + sendMessage(_asSpew, 0x2000, 0); + break; + } + return 0; +} + +void Scene2806::update() { + Scene::update(); + findClosestPoint(); +} + +void Scene2806::findClosestPoint() { + + static const uint32 kScene2806PaletteFileHashes[] = { + 0x48052508, + 0x01139404, + 0x01138C04, + 0x01138004, + 0x01138604, + 0x086B8890 + }; + + int16 x = MIN<int16>(_klaymen->getX(), 639); + int index = 1; + + while (index < (int)_pointList->size() && (*_pointList)[index].x < x) + ++index; + --index; + + if (_pointIndex != index) { + _pointIndex = index; + _palette->addPalette(kScene2806PaletteFileHashes[index], 0, 64, 0); + } + +} + +Scene2807::Scene2807(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + SetMessageHandler(&Scene2807::handleMessage); + + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) == 1) { + insertStaticSprite(0x103021E2, 300); + } else if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) == 2) { + insertStaticSprite(0x103022E2, 300); + } else if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) == 3) { + insertStaticSprite(0x103024E2, 300); + } + + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 1) { + insertStaticSprite(0x4800A52A, 200); + } else if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 2) { + insertStaticSprite(0x4800A62A, 200); + } else if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) == 3) { + insertStaticSprite(0x4800A02A, 200); + } + + if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 1) { + insertStaticSprite(0x31203430, 100); + } else if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 2) { + insertStaticSprite(0x31203400, 100); + } else if (getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2) == 3) { + insertStaticSprite(0x31203460, 100); + } + + setBackground(0x3E049A95); + setPalette(0x3E049A95); + insertPuzzleMouse(0x49A913E8, 20, 620); + +} + +uint32 Scene2807::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; + } + return 0; +} + +static const uint32 kScene2808FileHashes1[] = { + 0x90B0392, + 0x90B0192 +}; + +static const uint32 kScene2808FileHashes2[] = { + 0xB0396098, + 0xB0196098 +}; + +static const uint32 kClass428FileHashes[] = { + 0x140022CA, + 0x4C30A602, + 0xB1633402, + 0x12982135, + 0x0540B728, + 0x002A81E3, + 0x08982841, + 0x10982841, + 0x20982841, + 0x40982841, + 0x80982841, + 0x40800711 +}; + +static const int kClass428Countdowns1[] = { + 18, 16, 10, 0 +}; + +static const int kClass428Countdowns2[] = { + 9, 9, 8, 8, 5, 5, 0, 0 +}; + +static const uint32 kClass490FileHashes[] = { + 0x08100071, + 0x24084215, + 0x18980A10 +}; + +static const int16 kClass490FrameIndices1[] = { + 0, 8, 15, 19 +}; + +static const int16 kClass490FrameIndices2[] = { + 0, 4, 8, 11, 15, 17, 19, 0 +}; + +SsScene2808Dispenser::SsScene2808Dispenser(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum, int testTubeIndex) + : StaticSprite(vm, 900), _parentScene(parentScene), _countdown(0), _testTubeSetNum(testTubeSetNum), + _testTubeIndex(testTubeIndex) { + + loadSprite(kClass428FileHashes[testTubeSetNum * 3 + testTubeIndex], kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 1500); + setVisible(false); + SetUpdateHandler(&SsScene2808Dispenser::update); + SetMessageHandler(&SsScene2808Dispenser::handleMessage); +} + +void SsScene2808Dispenser::update() { + updatePosition(); + if (_countdown != 0 && (--_countdown) == 0) { + setVisible(false); + } +} + +uint32 SsScene2808Dispenser::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + sendMessage(_parentScene, 0x2000, _testTubeIndex); + messageResult = 1; + break; + } + return messageResult; +} + +void SsScene2808Dispenser::startCountdown(int index) { + setVisible(true); + updatePosition(); + if (_testTubeSetNum == 0) { + _countdown = kClass428Countdowns1[index]; + } else { + _countdown = kClass428Countdowns2[index]; + } +} + +AsScene2808TestTube::AsScene2808TestTube(NeverhoodEngine *vm, int testTubeSetNum, int testTubeIndex, SsScene2808Dispenser *ssDispenser) + : AnimatedSprite(vm, 1100), _testTubeSetNum(testTubeSetNum), _testTubeIndex(testTubeIndex), _ssDispenser(ssDispenser), _fillLevel(0) { + + if (testTubeSetNum == 0) { + _x = 504; + _y = 278; + } else { + setDoDeltaX(1); + _x = 136; + _y = 278; + } + + createSurface1(kClass490FileHashes[testTubeIndex], 1100); + + if (testTubeSetNum == 0) { + loadSound(0, 0x30809E2D); + loadSound(1, 0x72811E2D); + loadSound(2, 0x78B01625); + } else { + loadSound(3, 0x70A41E0C); + loadSound(4, 0x50205E2D); + loadSound(5, 0xF8621E2D); + loadSound(6, 0xF1A03C2D); + loadSound(7, 0x70A43D2D); + loadSound(8, 0xF0601E2D); + } + + startAnimation(kClass490FileHashes[testTubeIndex], 0, -1); + _newStickFrameIndex = 0; + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2808TestTube::handleMessage); + + if (_fillLevel == 0) + setVisible(false); + +} + +uint32 AsScene2808TestTube::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + fill(); + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene2808TestTube::fill() { + if ((int)_fillLevel < _testTubeSetNum * 3 + 3) { + if (_testTubeSetNum == 0) { + playSound(_fillLevel); + setVisible(true); + startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices1[_fillLevel], kClass490FrameIndices1[_fillLevel + 1]); + _newStickFrameIndex = kClass490FrameIndices1[_fillLevel + 1]; + } else { + playSound(3 + _fillLevel); + setVisible(true); + startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices2[_fillLevel], kClass490FrameIndices2[_fillLevel + 1]); + _newStickFrameIndex = kClass490FrameIndices2[_fillLevel + 1]; + } + _ssDispenser->startCountdown(_fillLevel); + _fillLevel++; + } +} + +void AsScene2808TestTube::flush() { + if (_fillLevel != 0) { + if (_testTubeSetNum == 0) { + startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices1[_fillLevel], -1); + } else { + startAnimation(kClass490FileHashes[_testTubeIndex], kClass490FrameIndices2[_fillLevel], -1); + } + _newStickFrameIndex = 0; + _playBackwards = true; + setVisible(true); + } +} + +AsScene2808Handle::AsScene2808Handle(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum) + : AnimatedSprite(vm, 1300), _parentScene(parentScene), _testTubeSetNum(testTubeSetNum), _isActivated(false) { + + loadSound(0, 0xE18D1F30); + _x = 320; + _y = 240; + if (_testTubeSetNum == 1) + setDoDeltaX(1); + createSurface1(0x040900D0, 1300); + startAnimation(0x040900D0, 0, -1); + _needRefresh = true; + _newStickFrameIndex = 0; + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2808Handle::handleMessage); + AnimatedSprite::updatePosition(); +} + +uint32 AsScene2808Handle::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_isActivated) { + sendMessage(_parentScene, 0x2001, 0); + playSound(0); + activate(); + } + messageResult = 1; + break; + } + return messageResult; +} + +uint32 AsScene2808Handle::hmActivating(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene2808Handle::activate() { + startAnimation(0x040900D0, 0, -1); + SetMessageHandler(&AsScene2808Handle::hmActivating); + NextState(&AsScene2808Handle::stActivated); + _isActivated = true; + _newStickFrameIndex = -1; +} + +void AsScene2808Handle::stActivated() { + stopAnimation(); + sendMessage(_parentScene, 0x2002, 0); +} + +AsScene2808Flow::AsScene2808Flow(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _testTubeSetNum(testTubeSetNum) { + + if (testTubeSetNum == 0) { + _x = 312; + _y = 444; + } else { + _x = 328; + _y = 444; + } + createSurface1(0xB8414818, 1200); + startAnimation(0xB8414818, 0, -1); + setVisible(false); + _newStickFrameIndex = 0; + _needRefresh = true; + loadSound(0, 0x6389B652); + SetUpdateHandler(&AnimatedSprite::update); + AnimatedSprite::updatePosition(); +} + +uint32 AsScene2808Flow::hmFlowing(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene2808Flow::start() { + startAnimation(0xB8414818, 0, -1); + setVisible(true); + SetMessageHandler(&AsScene2808Flow::hmFlowing); + NextState(&AsScene2808Flow::stKeepFlowing); + playSound(0); +} + +void AsScene2808Flow::stKeepFlowing() { + startAnimation(0xB8414818, 1, -1); + NextState(&AsScene2808Flow::stKeepFlowing); +} + +AsScene2808LightEffect::AsScene2808LightEffect(NeverhoodEngine *vm, int testTubeSetNum) + : AnimatedSprite(vm, 800), _countdown(1) { + + _x = 320; + _y = 240; + if (testTubeSetNum == 1) + setDoDeltaX(1); + createSurface1(0x804C2404, 800); + SetUpdateHandler(&AsScene2808LightEffect::update); + _needRefresh = true; + AnimatedSprite::updatePosition(); +} + +void AsScene2808LightEffect::update() { + if (_countdown != 0 && (--_countdown) == 0) { + int16 frameIndex = _vm->_rnd->getRandomNumber(3 - 1); + startAnimation(0x804C2404, frameIndex, frameIndex); + updateAnim(); + updatePosition(); + _countdown = _vm->_rnd->getRandomNumber(3 - 1) + 1; + } +} + +Scene2808::Scene2808(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _countdown(0), _testTubeSetNum(which), _leaveResult(0), _isFlowing(false) { + + Sprite *asHandle; + + if (which == 0) + _vm->gameModule()->initTestTubes1Puzzle(); + else + _vm->gameModule()->initTestTubes2Puzzle(); + + SetMessageHandler(&Scene2808::handleMessage); + SetUpdateHandler(&Scene2808::update); + + setBackground(kScene2808FileHashes1[which]); + setPalette(kScene2808FileHashes1[which]); + + asHandle = insertSprite<AsScene2808Handle>(this, which); + addCollisionSprite(asHandle); + + _asFlow = insertSprite<AsScene2808Flow>(this, which); + insertSprite<AsScene2808LightEffect>(which); + + for (int testTubeIndex = 0; testTubeIndex < 3; testTubeIndex++) { + SsScene2808Dispenser *ssDispenser = insertSprite<SsScene2808Dispenser>(this, which, testTubeIndex); + addCollisionSprite(ssDispenser); + _asTestTubes[testTubeIndex] = insertSprite<AsScene2808TestTube>(which, testTubeIndex, ssDispenser); + addCollisionSprite(_asTestTubes[testTubeIndex]); + } + + insertScreenMouse(kScene2808FileHashes2[which]); + +} + +uint32 Scene2808::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) && !isAnyTestTubeFilled()) { + leaveScene(1); + } + break; + case 0x2000: + if (!_isFlowing) + _asTestTubes[param.asInteger()]->fill(); + break; + case 0x2001: + _isFlowing = true; + break; + case 0x2002: + if (isAnyTestTubeFilled()) { + _leaveResult = 3; + if (!isMixtureGood()) + _leaveResult = 2; + _asFlow->start(); + for (int i = 0; i < 3; i++) + _asTestTubes[i]->flush(); + _mouseCursor->setVisible(false); + _countdown = 16; + } else { + leaveScene(1); + } + break; + } + return 0; +} + +void Scene2808::update() { + + // DEBUG>>> Show correct values + #if 1 + debug("---------------"); + if (_testTubeSetNum == 0) + debug("%03d %03d %03d", getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0), getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1), getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2)); + else + debug("%03d %03d %03d", getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 0), getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 1), getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 2)); + debug("%03d %03d %03d", _asTestTubes[0]->getFillLevel(), _asTestTubes[1]->getFillLevel(), _asTestTubes[2]->getFillLevel()); + #endif + // DEBUG<<< + + Scene::update(); + if (_countdown != 0 && (--_countdown) == 0) { + leaveScene(_leaveResult); + } +} + +bool Scene2808::isMixtureGood() { + if (_testTubeSetNum == 0) { + return + _asTestTubes[0]->getFillLevel() == getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0) && + _asTestTubes[1]->getFillLevel() == getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1) && + _asTestTubes[2]->getFillLevel() == getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2); + } else { + return + _asTestTubes[0]->getFillLevel() == getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 0) && + _asTestTubes[1]->getFillLevel() == getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 1) && + _asTestTubes[2]->getFillLevel() == getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 2); + } +} + +bool Scene2808::isAnyTestTubeFilled() { + return + _asTestTubes[0]->getFillLevel() > 0 || + _asTestTubes[1]->getFillLevel() > 0 || + _asTestTubes[2]->getFillLevel() > 0; +} + +AsScene2809Spew::AsScene2809Spew(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1200) { + + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2809Spew::handleMessage); + createSurface1(0x04211490, 1200); + _x = 262; + _y = 423; + setDoDeltaX(0); + setVisible(false); +} + +uint32 AsScene2809Spew::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + playSound(0, 0x48640244); + startAnimation(0x04211490, 0, -1); + setVisible(true); + break; + case 0x3002: + stopAnimation(); + setVisible(false); + break; + } + return messageResult; +} + +Scene2809::Scene2809(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + SetMessageHandler(&Scene2809::handleMessage); + SetUpdateHandler(&Scene2809::update); + + loadDataResource(0x1830009A); + loadHitRectList(); + + _pointList = _dataResource.getPointArray(0x064A310E); + + setBackground(0xB22116C5); + setPalette(0xB22116C5); + insertScreenMouse(0x116C1B2A); + + _sprite1 = insertStaticSprite(0x1FA2EB82, 1100); + + _clipRects[0].x1 = _sprite1->getDrawRect().x; + _clipRects[0].y1 = _sprite1->getDrawRect().y; + _clipRects[0].x2 = _sprite1->getDrawRect().x2(); + _clipRects[0].y2 = _sprite1->getDrawRect().y2(); + + _sprite2 = insertStaticSprite(0x037321B2, 1100); + _clipRects[1].y2 = _sprite2->getDrawRect().y2(); + + _sprite3 = insertStaticSprite(0x82022E11, 1100); + + _sprite4 = insertStaticSprite(0x09236252, 1100); + _clipRects[1].x2 = _sprite4->getDrawRect().x2(); + _clipRects[1].y1 = _sprite4->getDrawRect().y; + + tempSprite = insertStaticSprite(0x010C22F2, 1100); + _clipRects[2].x2 = tempSprite->getDrawRect().x2(); + _clipRects[2].y2 = tempSprite->getDrawRect().y2(); + _clipRects[3].y1 = tempSprite->getDrawRect().y2(); + _clipRects[1].x1 = tempSprite->getDrawRect().x2(); + + tempSprite = insertStaticSprite(0x877F6252, 1100); + _clipRects[3].x2 = tempSprite->getDrawRect().x2(); + + insertStaticSprite(0x01612A22, 1100); + insertStaticSprite(0x877F6252, 1100); + + _asSpew = insertSprite<AsScene2809Spew>(); + _clipRects[2].y1 = 0; + _clipRects[3].y2 = 480; + _clipRects[2].x1 = 0; + _clipRects[3].x1 = 0; + + if (which < 0) { + insertKlaymen<KmScene2809>(226, 423, false, _clipRects, 4); + setMessageList(0x004B5B90); + } else if (which == 1) { + insertKlaymen<KmScene2809>(262, 423, false, _clipRects, 4); + setMessageList(0x004B5B90); + } else if (which == 2) { + insertKlaymen<KmScene2809>(262, 423, false, _clipRects, 4); + setMessageList(0x004B5BD0); + } else if (which == 3) { + insertKlaymen<KmScene2809>(262, 423, true, _clipRects, 4); + setMessageList(0x004B5BA8, false); + setGlobalVar(V_KLAYMEN_SMALL, 0); + } else { + insertKlaymen<KmScene2809>(-30, 423, false, _clipRects, 4); + setMessageList(0x004B5B88); + } + + _pointIndex = -1; + findClosestPoint(); + +} + +void Scene2809::update() { + Scene::update(); + findClosestPoint(); +} + +uint32 Scene2809::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x160DA937) { + setMessageList(0x004B5B98); + } + break; + case 0x2000: + sendMessage(_asSpew, 0x2000, 0); + break; + } + return 0; +} + +void Scene2809::findClosestPoint() { + + static const uint32 kScene2809PaletteFileHashes[] = { + 0x04260848, + 0x12970401, + 0x128F0401, + 0x12830401, + 0x12850401, + 0x6A8B9008 + }; + + int16 x = MAX<int16>(_klaymen->getX(), 2); + int index = 1; + + while (index < (int)_pointList->size() && (*_pointList)[index].x >= x) + ++index; + --index; + + if (_pointIndex != index) { + _pointIndex = index; + _palette->addPalette(kScene2809PaletteFileHashes[index], 0, 64, 0); + } + +} + +AsScene2810Rope::AsScene2810Rope(NeverhoodEngine *vm, Scene *parentScene, int16 x) + : AnimatedSprite(vm, 1100) { + + createSurface(990, 68, 476); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2810Rope::handleMessage); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); + _x = x; + _y = -276; + startAnimation(0x9D098C23, 35, 53); +} + +uint32 AsScene2810Rope::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + startAnimation(0x9D098C23, 35, 53); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +Scene2810::Scene2810(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule) { + + Sprite *tempSprite; + + SetMessageHandler(&Scene2810::handleMessage); + + setBackground(0x26508804); + setPalette(0x26508804); + insertScreenMouse(0x0880026D); + + _sprite6 = insertStaticSprite(0x03615227, 1100); + _sprite5 = insertStaticSprite(0xE059A224, 1100); + + _clipRects[0].x1 = 0; + _clipRects[0].y1 = 0; + _clipRects[0].x2 = 640; + _clipRects[0].y2 = 400; + _clipRects[1].x1 = _sprite5->getDrawRect().x; + _clipRects[1].y1 = 400; + _clipRects[1].x2 = _sprite6->getDrawRect().x2(); + _clipRects[1].y2 = 480; + + if (getGlobalVar(V_KLAYMEN_SMALL)) { + _asTape = insertSprite<AsScene1201Tape>(this, 0, 900, 245, 429, 0x9148A011); + addCollisionSprite(_asTape); + } else { + _asTape = insertSprite<AsScene1201Tape>(this, 0, 1100, 245, 429, 0x9148A011); + addCollisionSprite(_asTape); + } + + _sprite1 = insertStaticSprite(0x430001C4, 1200); + + if (getGlobalVar(V_LADDER_DOWN)) { + setGlobalVar(V_BEEN_STATUE_ROOM, 1); + if (getGlobalVar(V_KLAYMEN_SMALL)) { + _sprite4 = insertStaticSprite(0x82653808, 100); + } else { + _sprite4 = insertStaticSprite(0x82653808, 1100); + } + _sprite4->setClipRect(0, _sprite1->getDrawRect().y, 640, 480); + } + + if (which < 0) { + if (getGlobalVar(V_KLAYMEN_SMALL)) { + insertKlaymen<KmScene2810Small>(240, 448); + _klaymen->setClipRect(_sprite5->getDrawRect().x, 0, 640, 480); + setMessageList(0x004AE438); + setRectList(0x004AE810); + _isRopingDown = false; + removeCollisionSprite(_asTape); + } else { + insertKlaymen<KmScene2810>(300, 424, _clipRects, 2); + setMessageList(0x004AE438); + if (getGlobalVar(V_LADDER_DOWN)) + loadDataResource(0x84130112); + else + loadDataResource(0x84500132); + tempSprite = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + tempSprite->setClipRect(0, _sprite1->getDrawRect().y, 640, 480); + _clipRects[0].y1 = _sprite1->getDrawRect().y; + _isRopingDown = false; + } + } else if (which == 1) { + insertKlaymen<KmScene2810>(186, 64, _clipRects, 2); + setMessageList(0x004AE440); + loadDataResource(0x84130112); + tempSprite = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + tempSprite->setClipRect(0, _sprite1->getDrawRect().y, 640, 480); + _isRopingDown = true; + _clipRects[0].y1 = _sprite1->getDrawRect().y; + } else if (which == 5) { + insertStaticSprite(0xC3007EA0, 100); + _sprite2 = insertStaticSprite(0x02780936, 1100); + _sprite3 = insertStaticSprite(0x1CA02160, 1100); + _asRope = insertSprite<AsScene2810Rope>(this, 384); + insertKlaymen<KmScene2810>(384, 0, _clipRects, 0); + sendEntityMessage(_klaymen, 0x1014, _asRope); + setMessageList(0x004AE738); + _klaymen->setClipRect(0, _sprite2->getDrawRect().y, 640, _sprite3->getDrawRect().y2()); + _asRope->setClipRect(0, _sprite2->getDrawRect().y, 640, _sprite3->getDrawRect().y2()); + _vm->_soundMan->addSound(0x84400112, 0xC874EE6C); + _vm->_soundMan->playSoundLooping(0xC874EE6C); + _vm->_soundMan->setSoundVolume(0xC874EE6C, 50); + _isRopingDown = false; + } else if ((which >= 11 && which <= 14) || (which >= 19 && which <= 22) || which == 3) { + if (getGlobalVar(V_KLAYMEN_SMALL)) { + insertKlaymen<KmScene2810Small>((int16)getGlobalVar(V_KLAYMEN_SAVED_X), 448); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) + _klaymen->setDoDeltaX(1); + _klaymen->setClipRect(_sprite5->getDrawRect().x, 0, 640, 480); + setMessageList(0x004AE6D8); + setRectList(0x004AE810); + _isRopingDown = false; + removeCollisionSprite(_asTape); + } else { + insertKlaymenLadder(); + if (getGlobalVar(V_LADDER_DOWN_ACTION)) { + setMessageList(0x004AE6E8); + setGlobalVar(V_LADDER_DOWN_ACTION, 0); + _isRopingDown = false; + } else { + setMessageList(0x004AE6D8); + _isRopingDown = false; + } + } + } else if (which >= 15 && which <= 18) { + insertKlaymenLadder(); + setMessageList(0x004AE6E0); + _isRopingDown = false; + } else if (which == 4) { + if (getGlobalVar(V_KLAYMEN_SMALL)) { + insertKlaymen<KmScene2810Small>(473, 448); + _klaymen->setClipRect(_sprite5->getDrawRect().x, 0, 640, 480); + setMessageList(0x004AE428); + setRectList(0x004AE810); + _isRopingDown = false; + removeCollisionSprite(_asTape); + } else { + insertKlaymen<KmScene2810>(450, 424, _clipRects, 2); + setMessageList(0x004AE418); + if (getGlobalVar(V_LADDER_DOWN)) + loadDataResource(0x84130112); + else + loadDataResource(0x84500132); + tempSprite = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + tempSprite->setClipRect(0, _sprite1->getDrawRect().y, 640, 480); + _clipRects[0].y1 = _sprite1->getDrawRect().y; + _isRopingDown = false; + } + } else { + insertKlaymen<KmScene2810Small>(120, 448); + _klaymen->setClipRect(_sprite5->getDrawRect().x, 0, 640, 480); + setMessageList(0x004AE410); + setRectList(0x004AE810); + _isRopingDown = false; + removeCollisionSprite(_asTape); + } + +} + +Scene2810::~Scene2810() { + setGlobalVar(V_KLAYMEN_IS_DELTA_X, _klaymen->isDoDeltaX() ? 1 : 0); + setGlobalVar(V_KLAYMEN_SAVED_X, _klaymen->getX()); + _vm->_soundMan->deleteSoundGroup(0x84400112); +} + +void Scene2810::insertKlaymenLadder() { + Sprite *tempSprite; + + if (getGlobalVar(V_LADDER_DOWN_ACTION)) { + insertKlaymen<KmScene2810>(430, 424, _clipRects, 2); + _klaymen->setDoDeltaX(1); + } else { + insertKlaymen<KmScene2810>((int16)getGlobalVar(V_KLAYMEN_SAVED_X), 424, _clipRects, 2); + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) + _klaymen->setDoDeltaX(1); + } + if (getGlobalVar(V_LADDER_DOWN)) + loadDataResource(0x84130112); + else + loadDataResource(0x84500132); + tempSprite = insertSprite<AsScene1002KlaymenLadderHands>(_klaymen); + tempSprite->setClipRect(0, _sprite1->getDrawRect().y, 640, 480); + _clipRects[0].y1 = _sprite1->getDrawRect().y; +} + +uint32 Scene2810::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0xE574F14C) + setMessageList(0x004AE458); + else if (param.asInteger() == 0x7214A05C || param.asInteger() == 0x2905E574) + setMessageList(0x004AE4A8); + else if (param.asInteger() == 0x7274E24C || param.asInteger() == 0x2D24E572) + setMessageList(0x004AE4D0); + else if (param.asInteger() == 0x4A07A040 || param.asInteger() == 0x190426F5) + setMessageList(0x004AE4F8); + else if (param.asInteger() == 0x6604200C || param.asInteger() == 0x2100E435) + setMessageList(0x004AE520); + else if (param.asInteger() == 0xE216A05C || param.asInteger() == 0x0905EC74) + setMessageList(0x004AE548); + else if (param.asInteger() == 0x721DA05C || param.asInteger() == 0xB905E574) + setMessageList(0x004AE570); + else if (param.asInteger() == 0x6214E09C || param.asInteger() == 0x2D09E474) + setMessageList(0x004AE598); + else if (param.asInteger() == 0x6276A04C || param.asInteger() == 0x0904E472) + setMessageList(0x004AE5C0); + else if (param.asInteger() == 0x6E14A00C || param.asInteger() == 0x2900E4B4) + setMessageList(0x004AE5E8); + else if (param.asInteger() == 0x6014A04D || param.asInteger() == 0x2904F454) + setMessageList(0x004AE610); + else if (param.asInteger() == 0x6215A3C4 || param.asInteger() == 0x393C6474) + setMessageList(0x004AE638); + else if (param.asInteger() == 0x6A54E24D || param.asInteger() == 0x2D24F4F0) + setMessageList(0x004AE660); + else if (param.asInteger() == 0x2064294C || param.asInteger() == 0x2194E053) + setMessageList(0x004AE688); + break; + case 0x2000: + setRectList(0x004AE800); + _isRopingDown = true; + break; + case 0x2001: + if (getGlobalVar(V_LADDER_DOWN)) + loadDataResource(0x84130112); + else + loadDataResource(0x84500132); + _isRopingDown = false; + break; + case 0x4826: + if (sender == _asTape && getGlobalVar(V_KLAYMEN_SMALL) == 0 && !_isRopingDown) { + sendEntityMessage(_klaymen, 0x1014, _asTape); + setMessageList(0x004AE750); + } + break; + } + return messageResult; +} + +AsScene2812Winch::AsScene2812Winch(NeverhoodEngine *vm) + : AnimatedSprite(vm, 1100) { + + createSurface1(0x20DA08A0, 1200); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2812Winch::handleMessage); + setVisible(false); + _x = 280; + _y = 184; +} + +AsScene2812Winch::~AsScene2812Winch() { + _vm->_soundMan->deleteSoundGroup(0x00B000E2); +} + +uint32 AsScene2812Winch::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + startAnimation(0x20DA08A0, 0, -1); + setVisible(true); + _vm->_soundMan->addSound(0x00B000E2, 0xC874EE6C); + _vm->_soundMan->playSoundLooping(0xC874EE6C); + break; + case 0x3002: + startAnimation(0x20DA08A0, 7, -1); + break; + } + return messageResult; +} + +AsScene2812Rope::AsScene2812Rope(NeverhoodEngine *vm, Scene *parentScene) + : AnimatedSprite(vm, 1100), _parentScene(parentScene) { + + createSurface(990, 68, 476); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene2812Rope::handleMessage); + SetSpriteUpdate(&AnimatedSprite::updateDeltaXY); + startAnimation(0xAE080551, 0, -1); + _x = 334; + _y = 201; +} + +uint32 AsScene2812Rope::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x4806: + setDoDeltaX(((Sprite*)sender)->isDoDeltaX() ? 1 : 0); + stRopingDown(); + break; + case 0x482A: + sendMessage(_parentScene, 0x1022, 990); + break; + case 0x482B: + sendMessage(_parentScene, 0x1022, 1010); + break; + } + return messageResult; +} + +uint32 AsScene2812Rope::hmRopingDown(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene2812Rope::stRopingDown() { + sendMessage(_parentScene, 0x4806, 0); + startAnimation(0x9D098C23, 0, -1); + SetMessageHandler(&AsScene2812Rope::hmRopingDown); +} + +AsScene2812TrapDoor::AsScene2812TrapDoor(NeverhoodEngine *vm) + : AnimatedSprite(vm, 0x805D0029, 100, 320, 240) { + + SetMessageHandler(&AsScene2812TrapDoor::handleMessage); + _newStickFrameIndex = 0; +} + +uint32 AsScene2812TrapDoor::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2000: + startAnimation(0x805D0029, 0, -1); + playSound(0, 0xEA005F40); + _newStickFrameIndex = STICK_LAST_FRAME; + break; + } + return messageResult; +} + +Scene2812::Scene2812(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _paletteArea(0) { + + if (getGlobalVar(V_HAS_FINAL_KEY) && getGlobalVar(V_KEY3_LOCATION) == 0) + setGlobalVar(V_KEY3_LOCATION, 3); + + SetMessageHandler(&Scene2812::handleMessage); + SetUpdateHandler(&Scene2812::update); + + setRectList(0x004AF700); + + setBackground(0x03600606); + setPalette(0x03600606); + addEntity(_palette); + _palette->addBasePalette(0x03600606, 0, 256, 0); + + _sprite1 = insertStaticSprite(0x0C06C860, 1100); + insertScreenMouse(0x0060203E); + + if (getGlobalVar(V_KEY3_LOCATION) == 3) { + _asKey = insertSprite<AsCommonKey>(this, 2, 1100, 474, 437); + addCollisionSprite(_asKey); + } + + _ssTape = insertSprite<SsScene1705Tape>(this, 6, 1100, 513, 437, 0xA1361863); + addCollisionSprite(_ssTape); + + _asWinch = insertSprite<AsScene2812Winch>(); + _asTrapDoor = insertSprite<AsScene2812TrapDoor>(); + _asRope = insertSprite<AsScene2812Rope>(this); + + _sprite2 = insertStaticSprite(0x08478078, 1100); + _sprite3 = insertStaticSprite(0x2203B821, 1100); + _sprite4 = insertStaticSprite(0x08592134, 1100); + + if (which < 0) { + _isRopingDown = false; + insertKlaymen<KmScene2812>(272, 432); + setMessageList(0x004AF560); + _sprite1->setVisible(false); + _klaymen->setClipRect(_sprite4->getDrawRect().x, 0, 640, _sprite3->getDrawRect().y2()); + } else if (which == 1) { + _isRopingDown = false; + insertKlaymen<KmScene2812>(338, 398); + setMessageList(0x004AF588); + setPaletteArea1(true); + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, _sprite1->getDrawRect().x2(), _sprite3->getDrawRect().y2()); + } else if (which == 2) { + _isRopingDown = false; + if (getGlobalVar(V_KLAYMEN_IS_DELTA_X)) { + insertKlaymen<KmScene2812>(554, 432); + _klaymen->setDoDeltaX(1); + } else { + insertKlaymen<KmScene2812>(394, 432); + } + setMessageList(0x004AF5F0); + _sprite1->setVisible(false); + _klaymen->setClipRect(_sprite4->getDrawRect().x, 0, 640, _sprite3->getDrawRect().y2()); + } else { + _isRopingDown = true; + insertKlaymen<KmScene2812>(150, 582); + setMessageList(0x004AF568); + setPaletteArea2(true); + _sprite1->setVisible(false); + _klaymen->setClipRect(_sprite4->getDrawRect().x, 0, 640, _sprite3->getDrawRect().y2()); + } + + _asRope->setClipRect(0, _sprite2->getDrawRect().y, 640, _sprite3->getDrawRect().y2()); + +} + +void Scene2812::update() { + if (_klaymen->getX() < 220) + setPaletteArea2(false); + else if (_klaymen->getX() < 240) + setPaletteArea0(false); + Scene::update(); +} + +uint32 Scene2812::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x100D: + if (param.asInteger() == 0x0004269B) + sendEntityMessage(_klaymen, 0x1014, _asRope); + break; + case 0x2001: + _isRopingDown = true; + setRectList(0x004AF710); + _klaymen->setClipRect(_sprite4->getDrawRect().x, 0, 640, _sprite4->getDrawRect().y2()); + break; + case 0x2002: + _isRopingDown = false; + setRectList(0x004AF700); + _klaymen->setClipRect(_sprite4->getDrawRect().x, 0, 640, _sprite3->getDrawRect().y2()); + break; + case 0x4806: + sendMessage(_asWinch, 0x2000, 0); + sendMessage(_asTrapDoor, 0x2000, 0); + break; + case 0x4826: + if (sender == _ssTape && !_isRopingDown) { + sendEntityMessage(_klaymen, 0x1014, _ssTape); + setMessageList(0x004AF658); + } else if (sender == _asKey && !_isRopingDown) { + sendEntityMessage(_klaymen, 0x1014, _asKey); + setMessageList(0x004AF668); + } + break; + case 0x482A: + setPaletteArea1(false); + _sprite1->setVisible(true); + _klaymen->setClipRect(_sprite1->getDrawRect().x, 0, _sprite1->getDrawRect().x2(), _sprite3->getDrawRect().y2()); + break; + case 0x482B: + setPaletteArea0(false); + _sprite1->setVisible(false); + _klaymen->setClipRect(_sprite4->getDrawRect().x, 0, 640, _sprite3->getDrawRect().y2()); + break; + } + return messageResult; +} + +void Scene2812::setPaletteArea0(bool instantly) { + if (_paletteArea != 0) { + _paletteArea = 0; + updatePaletteArea(instantly); + } +} + +void Scene2812::setPaletteArea1(bool instantly) { + if (_paletteArea != 1) { + _paletteArea = 1; + updatePaletteArea(instantly); + } +} + +void Scene2812::setPaletteArea2(bool instantly) { + if (_paletteArea != 2) { + _paletteArea = 2; + updatePaletteArea(instantly); + } +} + +void Scene2812::updatePaletteArea(bool instantly) { + if (_paletteArea == 0) + _palette->addBasePalette(0x05D30F11, 0, 64, 0); + else if (_paletteArea == 1) + _palette->addBasePalette(0x92CA2C9B, 0, 64, 0); + else if (_paletteArea == 2) + _palette->addBasePalette(0x381F92C5, 0, 64, 0); + _palette->startFadeToPalette(instantly ? 0 : 12); +} + +Scene2822::Scene2822(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _countdown(0), _scrollIndex(0) { + + SetMessageHandler(&Scene2822::handleMessage); + SetUpdateHandler(&Scene2822::update); + _background = new Background(_vm, 0xD542022E, 0, 0); + addBackground(_background); + _background->getSurface()->getDrawRect().y = -10; + setPalette(0xD542022E); + insertPuzzleMouse(0x0028D089, 20, 620); + _ssButton = insertStaticSprite(0x1A4D4120, 1100); + _ssButton->setVisible(false); + loadSound(2, 0x19044E72); +} + +void Scene2822::update() { + + static const int16 kScene2822BackgroundYPositions[] = { + 0, -20, -5, -15, -8, -12, -9, -11, -10, 0 + }; + + Scene::update(); + + if (_countdown != 0) { + if ((--_countdown) == 0) { + if (_countdownStatus == 0) { + _ssButton->setVisible(false); + _countdownStatus = 1; + _countdown = 48; + } else if (_countdownStatus == 1) { + playSound(0, 0x1384CB60); + _countdownStatus = 2; + _countdown = 12; + } else if (_countdownStatus == 2 && getGlobalVar(V_LADDER_DOWN_ACTION)) { + leaveScene(0); + } + } else if (_countdownStatus == 2 && getGlobalVar(V_LADDER_DOWN_ACTION)) { + if (_scrollIndex < 9) { + _background->getSurface()->getDrawRect().y = kScene2822BackgroundYPositions[_scrollIndex]; + _scrollIndex++; + } else { + _background->getSurface()->getDrawRect().y = -10; + } + } + } + +} + +uint32 Scene2822::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } else if (param.asPoint().x >= 257 && param.asPoint().y >= 235 && + param.asPoint().x <= 293 && param.asPoint().y <= 273) { + _ssButton->setVisible(true); + _countdownStatus = 0; + _countdown = 12; + playSound(1, 0x44061000); + if (getGlobalVar(V_LADDER_DOWN) == 0) { + setGlobalVar(V_LADDER_DOWN, 1); + setGlobalVar(V_LADDER_DOWN_ACTION, 1); + SetMessageHandler(NULL); + playSound(2); + _mouseCursor->setVisible(false); + } + } + break; + } + return messageResult; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2800.h b/engines/neverhood/modules/module2800.h new file mode 100644 index 0000000000..fe62f11307 --- /dev/null +++ b/engines/neverhood/modules/module2800.h @@ -0,0 +1,505 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2800_H +#define NEVERHOOD_MODULES_MODULE2800_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module2800 + +class Module2800 : public Module { +public: + Module2800(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module2800(); +protected: + int _sceneNum; + uint32 _currentMusicFileHash; + MusicResource *_musicResource; + void createScene(int sceneNum, int which); + void updateScene(); + void updateMusic(bool halfVolume); +}; + +class Scene2801 : public Scene { +public: + Scene2801(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene2801(); +protected: + Sprite *_asTape; + uint32 _paletteHash; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2802 : public Scene { +public: + Scene2802(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene2802(); +protected: + SmackerPlayer *_smackerPlayer; + uint _currRadioMusicIndex; + int _currTuneStatus; + int _countdown1; + int _countdown2; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void incRadioMusicIndex(int delta); + void changeTuneStatus(int prevTuneStatus, int newTuneStatus); +}; + +class AsScene2803LightCord : public AnimatedSprite { +public: + AsScene2803LightCord(NeverhoodEngine *vm, Scene *parentScene, uint32 fileHash1, uint32 fileHash2, int16 x, int16 y); + void stPulled(); + void stIdle(); + void setFileHashes(uint32 fileHash1, uint32 fileHash2); +protected: + Scene *_parentScene; + uint32 _fileHash1, _fileHash2; + bool _isPulled, _isBusy; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmPulled(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2803TestTubeOne : public AnimatedSprite { +public: + AsScene2803TestTubeOne(NeverhoodEngine *vm, uint32 fileHash1, uint32 fileHash2); +protected: + uint32 _fileHash1, _fileHash2; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2803Rope : public AnimatedSprite { +public: + AsScene2803Rope(NeverhoodEngine *vm, Scene *parentScene, int16 x); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmReleased(int messageNum, const MessageParam ¶m, Entity *sender); + void stReleased(); + void stHide(); +}; + +class Scene2803 : public Scene { +public: + Scene2803(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + StaticSprite *_asTestTubeOne; + StaticSprite *_asTestTubeTwo; + StaticSprite *_asTestTubeThree; + Sprite *_asRope; + AsScene2803LightCord *_asLightCord; + StaticSprite *_sprite3; + StaticSprite *_sprite4; + StaticSprite *_sprite5; + StaticSprite *_sprite6; + StaticSprite *_sprite7; + StaticSprite *_sprite8; + StaticSprite *_sprite9; + Sprite *_sprite10; + NRect _clipRectsFloor[2]; + NRect _clipRectsStairs[3]; + int _paletteArea; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void upKlaymenStairs(); + void klaymenStairs(); + void klaymenFloor(); + void toggleBackground(); + void changeBackground(); + void setPaletteArea0(); + void setPaletteArea1(); + void updatePaletteArea(); +}; + +class Scene2803Small : public Scene { +public: + Scene2803Small(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + int _paletteArea; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_sprite4; + Sprite *_sprite5; + Sprite *_sprite6; + Sprite *_sprite7; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void upKlaymenSlope(); + void upKlaymenFloor(); + void klaymenSlope(); + void klaymenFloor(); + void setPaletteArea0(); + void setPaletteArea1(); + void setPaletteArea2(); + void setPaletteArea3(); + void updatePaletteArea(bool instantly); +}; + +class Scene2804; + +class SsScene2804RedButton : public StaticSprite { +public: + SsScene2804RedButton(NeverhoodEngine *vm, Scene2804 *parentScene); +protected: + Scene2804 *_parentScene; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2804LightCoil : public StaticSprite { +public: + SsScene2804LightCoil(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2804BeamCoilBody : public StaticSprite { +public: + SsScene2804BeamCoilBody(NeverhoodEngine *vm); +}; + +class SsScene2804LightTarget : public StaticSprite { +public: + SsScene2804LightTarget(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2804Flash : public StaticSprite { +public: + SsScene2804Flash(NeverhoodEngine *vm); + void show(); +}; + +class AsScene2804CrystalWaves : public AnimatedSprite { +public: + AsScene2804CrystalWaves(NeverhoodEngine *vm, uint crystalIndex); + void show(); + void hide(); +protected: + uint _crystalIndex; +}; + +class AsScene2804Crystal : public AnimatedSprite { +public: + AsScene2804Crystal(NeverhoodEngine *vm, AsScene2804CrystalWaves *asCrystalWaves, uint crystalIndex); + void show(); + void hide(); + void activate(); + int16 getColorNum() const { return _colorNum; } +protected: + AsScene2804CrystalWaves *_asCrystalWaves; + uint _crystalIndex; + int16 _colorNum; + bool _isLightOn; + bool _isShowing; +}; + +class SsScene2804CrystalButton : public StaticSprite { +public: + SsScene2804CrystalButton(NeverhoodEngine *vm, Scene2804 *parentScene, AsScene2804Crystal *asCrystal, uint crystalIndex); +protected: + Scene2804 *_parentScene; + AsScene2804Crystal *_asCrystal; + uint _crystalIndex; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2804BeamCoil : public AnimatedSprite { +public: + AsScene2804BeamCoil(NeverhoodEngine *vm, Scene *parentScene, SsScene2804BeamCoilBody *ssBeamCoilBody); + virtual ~AsScene2804BeamCoil(); +protected: + Scene *_parentScene; + SsScene2804BeamCoilBody *_ssBeamCoilBody; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void show(); + void hide(); + void stBeaming(); + uint32 hmBeaming(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2804BeamTarget : public AnimatedSprite { +public: + AsScene2804BeamTarget(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2804 : public Scene { +public: + Scene2804(NeverhoodEngine *vm, Module *parentModule, int which); + bool isWorking() const { return _isWorking; } +protected: + int _countdown1; + int _countdown2; + int _countdown3; + int _beamStatus; + bool _isSolved; + bool _isWorking; + Sprite *_ssRedButton; + Sprite *_asCoil; + Sprite *_asTarget; + SsScene2804Flash *_ssFlash; + AsScene2804Crystal *_asCrystals[5]; + Sprite *_ssCrystalButtons[5]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2805 : public Scene { +public: + Scene2805(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_sprite1; + Sprite *_sprite2; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2806Spew : public AnimatedSprite { +public: + AsScene2806Spew(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2806 : public Scene { +public: + Scene2806(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + NPointArray *_pointList; + int _pointIndex; + NRect _clipRects[4]; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_sprite4; + Sprite *_asSpew; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void update(); + void findClosestPoint(); +}; + +class Scene2807 : public Scene { +public: + Scene2807(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2808Dispenser : public StaticSprite { +public: + SsScene2808Dispenser(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum, int testTubeIndex); + void startCountdown(int index); +protected: + Scene *_parentScene; + int _countdown; + int _testTubeSetNum, _testTubeIndex; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2808TestTube : public AnimatedSprite { +public: + AsScene2808TestTube(NeverhoodEngine *vm, int testTubeSetNum, int testTubeIndex, SsScene2808Dispenser *ssDispenser); + void fill(); + void flush(); + uint32 getFillLevel() const { return _fillLevel; } +protected: + SsScene2808Dispenser *_ssDispenser; + int _testTubeSetNum; + uint32 _fillLevel; + int _testTubeIndex; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2808Handle : public AnimatedSprite { +public: + AsScene2808Handle(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum); + void activate(); + void stActivated(); +protected: + Scene *_parentScene; + int _testTubeSetNum; + bool _isActivated; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmActivating(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2808Flow : public AnimatedSprite { +public: + AsScene2808Flow(NeverhoodEngine *vm, Scene *parentScene, int testTubeSetNum); + void start(); + void stKeepFlowing(); +protected: + Scene *_parentScene; + int _testTubeSetNum; + uint32 hmFlowing(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2808LightEffect : public AnimatedSprite { +public: + AsScene2808LightEffect(NeverhoodEngine *vm, int which); +protected: + int _countdown; + void update(); +}; + +class Scene2808 : public Scene { +public: + Scene2808(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + int _countdown; + int _testTubeSetNum; + AsScene2808Flow *_asFlow; + int _leaveResult; + bool _isFlowing; + AsScene2808TestTube *_asTestTubes[3]; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void update(); + bool isMixtureGood(); + bool isAnyTestTubeFilled(); +}; + +class AsScene2809Spew : public AnimatedSprite { +public: + AsScene2809Spew(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2809 : public Scene { +public: + Scene2809(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + NPointArray *_pointList; + int _pointIndex; + NRect _clipRects[4]; + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_sprite4; + Sprite *_asSpew; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void findClosestPoint(); +}; + +class AsScene2810Rope : public AnimatedSprite { +public: + AsScene2810Rope(NeverhoodEngine *vm, Scene *parentScene, int16 x); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2810 : public Scene { +public: + Scene2810(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Scene2810(); +protected: + Sprite *_sprite1; + Sprite *_sprite2; + Sprite *_sprite3; + Sprite *_asRope; + Sprite *_sprite4; + Sprite *_asTape; + Sprite *_sprite5; + Sprite *_sprite6; + bool _isRopingDown; + NRect _clipRects[2]; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void insertKlaymenLadder(); +}; + +class AsScene2812Winch : public AnimatedSprite { +public: + AsScene2812Winch(NeverhoodEngine *vm); + virtual ~AsScene2812Winch(); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene2812Rope : public AnimatedSprite { +public: + AsScene2812Rope(NeverhoodEngine *vm, Scene *parentScene); +protected: + Scene *_parentScene; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + uint32 hmRopingDown(int messageNum, const MessageParam ¶m, Entity *sender); + void stRopingDown(); +}; + +class AsScene2812TrapDoor : public AnimatedSprite { +public: + AsScene2812TrapDoor(NeverhoodEngine *vm); +protected: + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2812 : public Scene { +public: + Scene2812(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_asWinch; + Sprite *_asTrapDoor; + Sprite *_asRope; + Sprite *_sprite3; + Sprite *_sprite2; + Sprite *_sprite4; + Sprite *_ssTape; + Sprite *_asKey; + Sprite *_sprite1; + bool _isRopingDown; + int _paletteArea; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void setPaletteArea0(bool instantly); + void setPaletteArea1(bool instantly); + void setPaletteArea2(bool instantly); + void updatePaletteArea(bool instantly); +}; + +class Scene2822 : public Scene { +public: + Scene2822(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_ssButton; + int _scrollIndex; + int _countdown; + int _countdownStatus; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2800_H */ diff --git a/engines/neverhood/modules/module2900.cpp b/engines/neverhood/modules/module2900.cpp new file mode 100644 index 0000000000..bd95b82f4c --- /dev/null +++ b/engines/neverhood/modules/module2900.cpp @@ -0,0 +1,439 @@ +/* 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/module2900.h" +#include "neverhood/gamemodule.h" +#include "neverhood/modules/module1100.h" +#include "neverhood/modules/module1300.h" +#include "neverhood/modules/module1700.h" +#include "neverhood/modules/module2000.h" +#include "neverhood/modules/module2100.h" +#include "neverhood/modules/module2800.h" + +namespace Neverhood { + +Module2900::Module2900(NeverhoodEngine *vm, Module *parentModule, int which) + : Module(vm, parentModule) { + + if (which >= 0) + setGlobalVar(V_TELEPORTER_WHICH, which); + + createScene(0, 0); + +} + +void Module2900::createScene(int sceneNum, int which) { + debug("Module2900::createScene(%d, %d)", sceneNum, which); + _sceneNum = sceneNum; + switch (_sceneNum) { + case 0: + _vm->gameState().sceneNum = 0; + _childObject = new Scene2901(_vm, this, getGlobalVar(V_TELEPORTER_WHICH)); + break; + case 1: + _vm->gameState().sceneNum = 0; + _childObject = new Scene2805(_vm, this, which); + break; + case 2: + _vm->gameState().sceneNum = 0; + _childObject = new Scene2101(_vm, this, which); + break; + case 3: + _vm->gameState().sceneNum = 0; + _childObject = new Scene1306(_vm, this, which); + break; + case 4: + _vm->gameState().sceneNum = 0; + _childObject = new Scene1705(_vm, this, which); + break; + case 5: + _vm->gameState().sceneNum = 0; + _childObject = new Scene1109(_vm, this, which); + break; + case 6: + _vm->gameState().sceneNum = 0; + _childObject = new Scene2001(_vm, this, which); + break; + } + SetUpdateHandler(&Module2900::updateScene); + _childObject->handleUpdate(); +} + +void Module2900::updateScene() { + if (!updateChild()) { + switch (_sceneNum) { + case 0: + if (_moduleResult == (uint32)-1) { + leaveModule((uint32)-1); + } else { + _teleporterModuleResult = _moduleResult; + switch (getGlobalVar(V_TELEPORTER_WHICH)) { + case 0: + createScene(3, 4); + break; + case 1: + createScene(2, 2); + break; + case 2: + createScene(5, 2); + break; + case 3: + createScene(4, 2); + break; + case 4: + createScene(6, 2); + break; + case 5: + createScene(1, 2); + break; + default: + leaveModule(_moduleResult); + break; + } + } + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + leaveModule(_teleporterModuleResult); + break; + } + } +} + +static const uint32 kScene2901FileHashes1[] = { + 0x023023B4, + 0x36204507, + 0x046CF08E, + 0x9313A237, + 0xA651F246, + 0x02108034 +}; + +static const uint32 kScene2901FileHashes2[] = { + 0x023B002B, + 0x0450336A, + 0xCF08A04E, + 0x3A233939, + 0x1F242A6D, + 0x08030029 +}; + +static const uint32 kSsScene2901LocationButtonFileHashes[] = { + 0x2311326A, + 0x212323AC, + 0x10098138, + 0x25213167, + 0x1119A363, + 0x94452612, + 0x39464212, + 0x01860450, + 0x53002104, + 0x58E68412, + 0x18600300, + 0xB650A890, + 0x2452A7C4, + 0xA0232748, + 0x08862B02, + 0x2491E648, + 0x0010EB46, + 0x214C8A11, + 0x16A31921, + 0x0AC33A00, + 0x238028AA, + 0x26737A21, + 0x063039A8, + 0x51286C60, + 0x464006B4, + 0x42242538, + 0x20716010, + 0x4A2000AE, + 0x225124A6, + 0x28E82E45, + 0x58652C04, + 0xC82210A4, + 0x62A84060, + 0xC0693CB4, + 0x22212C64, + 0x5034EA71 +}; + +static const NPoint kSsScene2901LocationButtonPoints[] = { + {525, 120}, {576, 149}, {587, 205}, + {538, 232}, {484, 205}, {479, 153} +}; + +static const uint32 kSsScene2901LocationButtonLightFileHashes1[] = { + 0x03136246, + 0x2106216E, + 0x4025A13A, + 0x21816927, + 0x110B2202, + 0xCC0522B2, + 0x3CC24258, + 0x59C600F0, + 0x534A2480, + 0x50E61019, + 0x34400150, + 0x225BA090, + 0xB059AFC4, + 0xE093A741, + 0x0086BF09, + 0x3281E760, + 0xA048AB42, + 0x20649C01, + 0x14611904, + 0x26E33850, + 0x23A52A68, + 0xA2733024, + 0x10203880, + 0x1B2DE860, + 0x0644A6EC, + 0x426E20BC, + 0x80292014, + 0x4360B02E, + 0x22742664, + 0x98682705, + 0x0925B82C, + 0x5C2918A4, + 0xD2284920, + 0x41083CA6, + 0x6824A864, + 0x50266B10 +}; + +static const uint32 kSsScene2901LocationButtonLightFileHashes2[] = { + 0x43C46D4C, + 0x43C4AD4C, + 0x43C52D4C, + 0x43C62D4C, + 0x43C02D4C, + 0x43CC2D4C +}; + +static const uint32 kSsScene2901BrokenButtonFileHashes[] = { + 0x3081BD3A, + 0xD3443003, + 0x0786A320, + 0xE3A22029, + 0x61611814, + 0x425848E2 +}; + +static const uint32 kSsScene2901BigButtonFileHashes[] = { + 0x010D7748, + 0x9D02019A, + 0x351A2F43, + 0x448138E5, + 0x02788CF0, + 0x71718024 +}; + +SsScene2901LocationButton::SsScene2901LocationButton(NeverhoodEngine *vm, Scene *parentScene, int which, uint index) + : StaticSprite(vm, 900), _parentScene(parentScene), _index(index), _countdown1(0) { + + const NPoint &pt = kSsScene2901LocationButtonPoints[_index]; + + loadSprite(kSsScene2901LocationButtonFileHashes[which * 6 + index], kSLFDefDrawOffset | kSLFDefPosition, 800); + _collisionBounds.set(pt.x - 25, pt.y - 25, pt.x + 25, pt.y + 25); + setVisible(false); + loadSound(0, 0x440430C0); + SetUpdateHandler(&SsScene2901LocationButton::update); + SetMessageHandler(&SsScene2901LocationButton::handleMessage); +} + +void SsScene2901LocationButton::update() { + updatePosition(); + if (_countdown1 != 0 && (--_countdown1) == 0) { + setVisible(false); + } +} + +uint32 SsScene2901LocationButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown1 == 0) { + playSound(0); + setVisible(true); + _countdown1 = 4; + sendMessage(_parentScene, 0x2001, _index); + } + messageResult = 1; + break; + } + return messageResult; +} + +SsScene2901LocationButtonLight::SsScene2901LocationButtonLight(NeverhoodEngine *vm, int which, uint index) + : StaticSprite(vm, 900), _index(index) { + + loadSprite(kSsScene2901LocationButtonLightFileHashes1[which * 6 + index], kSLFDefDrawOffset | kSLFDefPosition, 900); + setVisible(false); + loadSound(0, kSsScene2901LocationButtonLightFileHashes2[_index]); +} + +void SsScene2901LocationButtonLight::show() { + playSound(0); + setVisible(true); + updatePosition(); +} + +void SsScene2901LocationButtonLight::hide() { + setVisible(false); + updatePosition(); +} + +SsScene2901BrokenButton::SsScene2901BrokenButton(NeverhoodEngine *vm, int which) + : StaticSprite(vm, 900) { + + loadSprite(kSsScene2901BrokenButtonFileHashes[which], kSLFDefDrawOffset | kSLFDefPosition, 900); +} + +SsScene2901BigButton::SsScene2901BigButton(NeverhoodEngine *vm, Scene *parentScene, int which) + : StaticSprite(vm, 900), _parentScene(parentScene), _which(which), _countdown1(0) { + + loadSprite(kSsScene2901BigButtonFileHashes[which], kSLFDefDrawOffset | kSLFDefPosition, 400); + _collisionBounds.set(62, 94, 322, 350); + setVisible(false); + loadSound(0, 0xF3D420C8); + SetUpdateHandler(&SsScene2901BigButton::update); + SetMessageHandler(&SsScene2901BigButton::handleMessage); +} + +void SsScene2901BigButton::update() { + updatePosition(); + if (_countdown1 != 0 && (--_countdown1) == 0) { + setVisible(false); + sendMessage(_parentScene, 0x2000, 0); + } +} + +uint32 SsScene2901BigButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown1 == 0) { + playSound(0); + setVisible(true); + _countdown1 = 4; + } + messageResult = 1; + break; + } + return messageResult; +} + +Scene2901::Scene2901(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _currLocationButtonNum(which), _selectedButtonNum(which), + _currWhirlButtonNum(0), _prevWhirlButtonNum(0), _countdown1(1), _skipCountdown(0), _blinkOn(0) { + + _isButton2Broken = getGlobalVar(V_ENTRANCE_OPEN) != 0; + + setSubVar(V_TELEPORTER_DEST_AVAILABLE, which, 1); + setSubVar(V_TELEPORTER_DEST_AVAILABLE, 5, 1); + setSubVar(V_TELEPORTER_DEST_AVAILABLE, 4, 1); + + if (_currLocationButtonNum == 3) + setSubVar(V_TELEPORTER_DEST_AVAILABLE, 2, 1); + + setBackground(kScene2901FileHashes1[_currLocationButtonNum]); + setPalette(kScene2901FileHashes1[_currLocationButtonNum]); + + for (uint i = 0; i < 6; ++i) { + if (i != 2 || !_isButton2Broken) { + _ssLocationButtons[i] = insertSprite<SsScene2901LocationButton>(this, _currLocationButtonNum, i); + addCollisionSprite(_ssLocationButtons[i]); + _ssLocationButtonLights[i] = insertSprite<SsScene2901LocationButtonLight>(_currLocationButtonNum, i); + } + } + + if (_isButton2Broken) + insertSprite<SsScene2901BrokenButton>(_currLocationButtonNum); + + _ssBigButton = insertSprite<SsScene2901BigButton>(this, _currLocationButtonNum); + addCollisionSprite(_ssBigButton); + + insertPuzzleMouse(kScene2901FileHashes2[_currLocationButtonNum], 20, 620); + + SetUpdateHandler(&Scene2901::update); + SetMessageHandler(&Scene2901::handleMessage); + +} + +void Scene2901::update() { + Scene::update(); + if (_countdown1 != 0 && (--_countdown1) == 0) { + if (_currLocationButtonNum == _selectedButtonNum) { + _ssLocationButtonLights[_currWhirlButtonNum]->hide(); + ++_currWhirlButtonNum; + while (!getSubVar(V_TELEPORTER_DEST_AVAILABLE, _currWhirlButtonNum) || (_currWhirlButtonNum == 2 && _isButton2Broken) || _currLocationButtonNum == _currWhirlButtonNum) { + ++_currWhirlButtonNum; + if (_currWhirlButtonNum >= 6) + _currWhirlButtonNum = 0; + } + if (_currWhirlButtonNum != _prevWhirlButtonNum || _skipCountdown == 0) { + _ssLocationButtonLights[_currWhirlButtonNum]->show(); + _skipCountdown = 4; + } + _countdown1 = 2; + --_skipCountdown; + _prevWhirlButtonNum = _currWhirlButtonNum; + } else if (_blinkOn) { + _blinkOn = false; + _ssLocationButtonLights[_selectedButtonNum]->hide(); + _countdown1 = 16; + } else { + _blinkOn = true; + _ssLocationButtonLights[_selectedButtonNum]->show(); + _countdown1 = 4; + } + } +} + +uint32 Scene2901::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((uint32)-1); + break; + case 0x2000: + if (_currLocationButtonNum != _selectedButtonNum) + leaveScene(_selectedButtonNum); + break; + case 0x2001: + if (_currLocationButtonNum == _selectedButtonNum) + _selectedButtonNum = _currWhirlButtonNum; + _ssLocationButtonLights[_selectedButtonNum]->hide(); + _selectedButtonNum = param.asInteger(); + if (!getSubVar(V_TELEPORTER_DEST_AVAILABLE, _selectedButtonNum)) + _selectedButtonNum = _currLocationButtonNum; + break; + } + return 0; +} + +} // End of namespace Neverhood diff --git a/engines/neverhood/modules/module2900.h b/engines/neverhood/modules/module2900.h new file mode 100644 index 0000000000..75b29567f6 --- /dev/null +++ b/engines/neverhood/modules/module2900.h @@ -0,0 +1,102 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE2900_H +#define NEVERHOOD_MODULES_MODULE2900_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" + +namespace Neverhood { + +// Module2900 + +class Module2900 : public Module { +public: + Module2900(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + int _sceneNum; + int _teleporterModuleResult; + void createScene(int sceneNum, int which); + void updateScene(); + void updateMusic(bool halfVolume); +}; + +class SsScene2901LocationButton : public StaticSprite { +public: + SsScene2901LocationButton(NeverhoodEngine *vm, Scene *parentScene, int which, uint index); +protected: + Scene *_parentScene; + uint _index; + int _countdown1; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene2901LocationButtonLight : public StaticSprite { +public: + SsScene2901LocationButtonLight(NeverhoodEngine *vm, int which, uint index); + void show(); + void hide(); +protected: + uint _index; +}; + +class SsScene2901BrokenButton : public StaticSprite { +public: + SsScene2901BrokenButton(NeverhoodEngine *vm, int which); +}; + +class SsScene2901BigButton : public StaticSprite { +public: + SsScene2901BigButton(NeverhoodEngine *vm, Scene *parentScene, int which); +protected: + Scene *_parentScene; + int _which; + int _countdown1; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene2901 : public Scene { +public: + Scene2901(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_ssLocationButtons[6]; + SsScene2901LocationButtonLight *_ssLocationButtonLights[6]; + Sprite *_ssBigButton; + int _currWhirlButtonNum; + int _prevWhirlButtonNum; + int _countdown1; + int _currLocationButtonNum; + int _selectedButtonNum; + int _skipCountdown; + int _blinkOn; + bool _isButton2Broken; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE2900_H */ diff --git a/engines/neverhood/modules/module3000.cpp b/engines/neverhood/modules/module3000.cpp new file mode 100644 index 0000000000..2bdb9f0497 --- /dev/null +++ b/engines/neverhood/modules/module3000.cpp @@ -0,0 +1,1548 @@ +/* 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/module3000.h" +#include "neverhood/gamemodule.h" +#include "neverhood/navigationscene.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), _soundVolume(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); + + _isWallBroken = getGlobalVar(V_WALL_BROKEN) != 0; + + if (_isWallBroken) { + _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[] = {3, 0}; + static const byte kNavigationTypes06[] = {5}; + debug("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 (_isWallBroken) { + _soundVolume = 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 (_isWallBroken) { + _soundVolume = 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: + _isWallBroken = getGlobalVar(V_WALL_BROKEN) != 0; + 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 (_isWallBroken && _soundVolume < 90 && frameNumber % 2) { + if (frameNumber == 0) + _soundVolume = 40; + else + _soundVolume++; + _vm->_soundMan->setSoundVolume(0x90F0D1C3, _soundVolume); + } + } + } + break; + case 2: + if (navigationScene()->isWalkingForward()) { + uint32 frameNumber = navigationScene()->getFrameNumber(); + int navigationIndex = navigationScene()->getNavigationIndex(); + if (_isWallBroken && _soundVolume > 1 && frameNumber % 2) { + _soundVolume--; + _vm->_soundMan->setSoundVolume(0x90F0D1C3, _soundVolume); + } + 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 (_isWallBroken && _soundVolume < 90 && frameNumber % 2) { + if (frameNumber == 0) + _soundVolume = 40; + else + _soundVolume++; + _vm->_soundMan->setSoundVolume(0x90F0D1C3, _soundVolume); + } + } + } + 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 +}; + +static const uint32 kSsScene3009SymbolEdgesFileHashes[] = { + 0x618827A0, + 0xB1A92322 +}; + +static const uint32 kSsScene3009TargetLineFileHashes[] = { + 0x4011018C, + 0x15086623 +}; + +static const NPoint kAsScene3009SymbolPoints[] = { + {289, 338}, + {285, 375}, + {284, 419}, + {456, 372}, + {498, 372}, + {541, 372} +}; + +static const uint32 kAsScene3009SymbolFileHashes[] = { + 0x24542582, + 0x1CD61D96 +}; + +static const uint32 kSsScene3009SymbolArrowFileHashes1[] = { + 0x24016060, + 0x21216221, + 0x486160A0, + 0x42216422, + 0x90A16120, + 0x84216824, + 0x08017029, + 0x08217029, + 0x10014032, + 0x10214032, + 0x20012004, + 0x20212004 +}; + +static const uint32 kSsScene3009SymbolArrowFileHashes2[] = { + 0x40092024, + 0x01636002, + 0x8071E028, + 0x02A56064, + 0x00806031, + 0x052960A8, + 0x0A116130, + 0x0A316130, + 0x14216200, + 0x14016200, + 0x28416460, + 0x28616460 +}; + +SsScene3009FireCannonButton::SsScene3009FireCannonButton(NeverhoodEngine *vm, Scene3009 *parentScene) + : StaticSprite(vm, 1400), _parentScene(parentScene), _isClicked(false) { + + loadSprite(0x120B24B0, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400); + setVisible(false); + SetUpdateHandler(&SsScene3009FireCannonButton::update); + SetMessageHandler(&SsScene3009FireCannonButton::handleMessage); + loadSound(0, 0x3901B44F); +} + +void SsScene3009FireCannonButton::update() { + updatePosition(); + if (_isClicked && !isSoundPlaying(0)) { + sendMessage(_parentScene, 0x2000, 0); + setVisible(false); + } +} + +uint32 SsScene3009FireCannonButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_isClicked && !_parentScene->isTurning()) { + _isClicked = true; + setVisible(true); + playSound(0); + } + messageResult = 1; + break; + } + return messageResult; +} + +SsScene3009SymbolEdges::SsScene3009SymbolEdges(NeverhoodEngine *vm, int index) + : StaticSprite(vm, 1400), _blinkCountdown(0) { + + loadSprite(kSsScene3009SymbolEdgesFileHashes[index], kSLFDefDrawOffset | kSLFDefPosition, 600); + if (getGlobalVar(V_ROBOT_HIT)) + hide(); + else + startBlinking(); + SetUpdateHandler(&SsScene3009SymbolEdges::update); +} + +void SsScene3009SymbolEdges::update() { + if (_blinkCountdown != 0 && (--_blinkCountdown == 0)) { + if (_blinkToggle) { + setVisible(true); + } else { + setVisible(false); + } + updatePosition(); + _blinkCountdown = 3; + _blinkToggle = !_blinkToggle; + } +} + +void SsScene3009SymbolEdges::show() { + setVisible(true); + updatePosition(); + _blinkCountdown = 0; +} + +void SsScene3009SymbolEdges::hide() { + setVisible(false); + updatePosition(); + _blinkCountdown = 0; +} + +void SsScene3009SymbolEdges::startBlinking() { + setVisible(true); + updatePosition(); + _blinkCountdown = 3; + _blinkToggle = true; +} + +SsScene3009TargetLine::SsScene3009TargetLine(NeverhoodEngine *vm, int index) + : StaticSprite(vm, 1400) { + + loadSprite(kSsScene3009TargetLineFileHashes[index], kSLFDefDrawOffset | kSLFDefPosition, 600); + setVisible(false); +} + +void SsScene3009TargetLine::show() { + setVisible(true); + updatePosition(); +} + +SsScene3009SymbolArrow::SsScene3009SymbolArrow(NeverhoodEngine *vm, Sprite *asSymbol, int index) + : StaticSprite(vm, 1400), _asSymbol(asSymbol), _index(index), _enabled(true), _countdown(0) { + + _incrDecr = _index % 2; + + createSurface(1200, 33, 31); + loadSprite(kSsScene3009SymbolArrowFileHashes2[_index], kSLFDefPosition); + _drawOffset.set(0, 0, 33, 31); + _collisionBoundsOffset = _drawOffset; + updateBounds(); + _needRefresh = true; + + SetUpdateHandler(&SsScene3009SymbolArrow::update); + SetMessageHandler(&SsScene3009SymbolArrow::handleMessage); + loadSound(0, 0x2C852206); +} + +void SsScene3009SymbolArrow::hide() { + _enabled = false; + setVisible(false); +} + +void SsScene3009SymbolArrow::update() { + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) { + loadSprite(kSsScene3009SymbolArrowFileHashes2[_index], kSLFDefDrawOffset); + } +} + +uint32 SsScene3009SymbolArrow::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_enabled && _countdown == 0) { + _countdown = 2; + loadSprite(kSsScene3009SymbolArrowFileHashes1[_index], kSLFDefDrawOffset); + playSound(0); + sendMessage(_asSymbol, 0x2005, _incrDecr); + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene3009VerticalIndicator::AsScene3009VerticalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, int index) + : AnimatedSprite(vm, 1000), _parentScene(parentScene), _enabled(false) { + + _x = 300; + _y = getGlobalVar(V_CANNON_RAISED) ? 52 : 266; + createSurface1(0xC2463913, 1200); + _needRefresh = true; + updatePosition(); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene3009VerticalIndicator::handleMessage); +} + +void AsScene3009VerticalIndicator::show() { + startAnimation(0xC2463913, 0, -1); + setVisible(true); + updatePosition(); + _enabled = true; +} + +uint32 AsScene3009VerticalIndicator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_enabled) { + sendMessage(_parentScene, 0x2002, 0); + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene3009HorizontalIndicator::AsScene3009HorizontalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, uint32 cannonTargetStatus) + : AnimatedSprite(vm, 1000), _parentScene(parentScene), _enabled(false) { + + _x = getGlobalVar(V_CANNON_TURNED) ? 533 : 92; + _y = 150; + createSurface1(0xC0C12954, 1200); + _needRefresh = true; + updatePosition(); + setVisible(false); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene3009HorizontalIndicator::handleMessage); + if (cannonTargetStatus == kCTSRightRobotNoTarget || cannonTargetStatus == kCTSRightRobotIsTarget || cannonTargetStatus == kCTSRightNoRobot) { + SetSpriteUpdate(&AsScene3009HorizontalIndicator::suMoveRight); + _x = 280; + } +} + +uint32 AsScene3009HorizontalIndicator::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_enabled) { + sendMessage(_parentScene, 0x2004, 0); + } + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene3009HorizontalIndicator::suMoveLeft() { + _x -= 6; + if (_x < 92) { + SetSpriteUpdate(NULL); + _x = 92; + } +} + +void AsScene3009HorizontalIndicator::suMoveRight() { + _x += 6; + if (_x > 533) { + SetSpriteUpdate(NULL); + _x = 533; + } +} + +void AsScene3009HorizontalIndicator::show() { + startAnimation(0xC0C12954, 0, -1); + setVisible(true); + updatePosition(); + _enabled = true; +} + +void AsScene3009HorizontalIndicator::stMoveLeft() { + _x = 533; + SetSpriteUpdate(&AsScene3009HorizontalIndicator::suMoveLeft); +} + +void AsScene3009HorizontalIndicator::stMoveRight() { + _x = 330; + SetSpriteUpdate(&AsScene3009HorizontalIndicator::suMoveRight); +} + +AsScene3009Symbol::AsScene3009Symbol(NeverhoodEngine *vm, Scene3009 *parentScene, int symbolPosition) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _symbolPosition(symbolPosition) { + + _symbolIndex = getSubVar(VA_CURR_CANNON_SYMBOLS, _symbolPosition); + + _x = kAsScene3009SymbolPoints[_symbolPosition].x; + _y = kAsScene3009SymbolPoints[_symbolPosition].y; + createSurface1(kAsScene3009SymbolFileHashes[_symbolPosition / 3], 1200); + startAnimation(kAsScene3009SymbolFileHashes[_symbolPosition / 3], _symbolIndex, -1); + _newStickFrameIndex = _symbolIndex; + _needRefresh = true; + updatePosition(); + SetUpdateHandler(&AnimatedSprite::update); + SetMessageHandler(&AsScene3009Symbol::handleMessage); + _ssArrowPrev = _parentScene->insertSprite<SsScene3009SymbolArrow>(this, _symbolPosition * 2 + 0); + _parentScene->addCollisionSprite(_ssArrowPrev); + _ssArrowNext = _parentScene->insertSprite<SsScene3009SymbolArrow>(this, _symbolPosition * 2 + 1); + _parentScene->addCollisionSprite(_ssArrowNext); +} + +uint32 AsScene3009Symbol::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x2005: + if (param.asInteger()) { + if (_symbolIndex == 11) + _symbolIndex = 0; + else + _symbolIndex++; + } else { + if (_symbolIndex == 0) + _symbolIndex = 11; + else + _symbolIndex--; + } + startAnimation(kAsScene3009SymbolFileHashes[_symbolPosition / 3], _symbolIndex, -1); + _newStickFrameIndex = _symbolIndex; + setSubVar(VA_CURR_CANNON_SYMBOLS, _symbolPosition, _symbolIndex); + if (_symbolPosition / 3 == 0) { + sendMessage(_parentScene, 0x2001, 0); + } else { + sendMessage(_parentScene, 0x2003, 0); + } + messageResult = 1; + break; + } + return messageResult; +} + +void AsScene3009Symbol::hide() { + _ssArrowPrev->hide(); + _ssArrowNext->hide(); +} + +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<SsScene3009FireCannonButton>(this); + addCollisionSprite(_ssFireCannonButton); + + _asVerticalIndicator = insertSprite<AsScene3009VerticalIndicator>(this, _cannonTargetStatus); + addCollisionSprite(_asVerticalIndicator); + + _asHorizontalIndicator = insertSprite<AsScene3009HorizontalIndicator>(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; + } + } + + _smackerPlayer = addSmackerPlayer(new SmackerPlayer(_vm, this, kScene3009CannonScopeVideos[_cannonTargetStatus], false, _keepVideo)); + _smackerPlayer->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<SsScene3009SymbolEdges>(i); + _ssTargetLines[i] = insertSprite<SsScene3009TargetLine>(i); + } + + for (int symbolPosition = 0; symbolPosition < 6; symbolPosition++) { + _asSymbols[symbolPosition] = insertSprite<AsScene3009Symbol>(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); + + // DEBUG Enable to set the correct code +#if 0 + for (int i = 0; i < 6; i++) + setSubVar(VA_CURR_CANNON_SYMBOLS, i, _correctSymbols[i]); + sendMessage(this, 0x2003, 0); +#endif + +} + +void Scene3009::update() { + Scene::update(); + + if (!_keepVideo && _smackerPlayer->isDone() && _cannonTargetStatus <= kCTSCount) { + switch (_cannonTargetStatus) { + case kCTSNull: + case kCTSLowerCannon: + _smackerPlayer->open(0x340A0049, true); + _palette->usePalette(); + _keepVideo = true; + break; + case kCTSRightRobotNoTarget: + _smackerPlayer->open(0x0082080D, true); + _palette->usePalette(); + _keepVideo = true; + _isTurning = false; + break; + case kCTSRightRobotIsTarget: + _smackerPlayer->open(0x0282080D, true); + _palette->usePalette(); + _keepVideo = true; + _isTurning = false; + break; + case kCTSRightNoRobot: + _smackerPlayer->open(0x0882080D, true); + _palette->usePalette(); + _keepVideo = true; + _isTurning = false; + break; + case kCTSLeftRobotNoTarget: + case kCTSLeftRobotIsTarget: + case kCTSLeftNoRobot: + if (_moveCannonLeftFirst) { + if (_cannonTargetStatus == kCTSLeftRobotNoTarget) + _smackerPlayer->open(0x110A000F, false); + else if (_cannonTargetStatus == kCTSLeftRobotIsTarget) + _smackerPlayer->open(0x500B004F, false); + else if (_cannonTargetStatus == kCTSLeftNoRobot) + _smackerPlayer->open(0x100B010E, false); + _palette->usePalette(); + _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 0x0001: + if ((param.asPoint().x <= 20 || param.asPoint().x >= 620) && !getGlobalVar(V_CANNON_RAISED)) { + setGlobalVar(V_CANNON_TARGET_STATUS, 0); + leaveScene(0); + } + break; + case 0x2000: + 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 0x2002: + // 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; + _smackerPlayer->open(0x108A000F, false); + } else if (!getGlobalVar(V_ROBOT_HIT)) { + _cannonTargetStatus = kCTSLeftRobotIsTarget; + _smackerPlayer->open(0x500B002F, false); + } else { + _cannonTargetStatus = kCTSLeftNoRobot; + _smackerPlayer->open(0x100B008E, false); + } + _palette->usePalette(); + _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 +}; + +static const uint32 kScene3010DeadBoltButtonFileHashes1[] = { + 0x301024C2, + 0x20280580, + 0x30200452 +}; + +static const uint32 kScene3010DeadBoltButtonFileHashes2[] = { + 0x50C025A8, + 0x1020A0A0, + 0x5000A7E8 +}; + +static const NPoint kAsScene3010DeadBoltPoints[] = { + {550, 307}, + {564, 415}, + {560, 514} +}; + +static const uint32 kAsScene3010DeadBoltFileHashes2[] = { + 0x181A0042, + 0x580A08F2, + 0x18420076 +}; + +static const uint32 kAsScene3010DeadBoltFileHashes1[] = { + 0x300E105A, + 0x804E0052, + 0x040E485A +}; + +SsScene3010DeadBoltButton::SsScene3010DeadBoltButton(NeverhoodEngine *vm, Scene *parentScene, int buttonIndex, int initCountdown, bool initDisabled) + : StaticSprite(vm, 900), _parentScene(parentScene), _buttonLocked(false), _countdown1(0), _countdown2(0), _buttonIndex(buttonIndex) { + + _buttonEnabled = getSubVar(VA_LOCKS_DISABLED, kScene3010ButtonNameHashes[_buttonIndex]) != 0; + createSurface(400, 88, 95); + setSprite(kScene3010DeadBoltButtonFileHashes2[_buttonIndex]); + if (initDisabled) + disableButton(); + else if (_buttonEnabled) + _countdown1 = initCountdown * 12 + 1; + loadSound(0, 0xF4217243); + loadSound(1, 0x44049000); + loadSound(2, 0x6408107E); + SetUpdateHandler(&SsScene3010DeadBoltButton::update); + SetMessageHandler(&SsScene3010DeadBoltButton::handleMessage); +} + +void SsScene3010DeadBoltButton::update() { + + if (_countdown1 != 0 && (--_countdown1 == 0)) { + playSound(0); + setVisible(false); + setSprite(kScene3010DeadBoltButtonFileHashes1[_buttonIndex]); + } + + if (_countdown2 != 0 && (--_countdown2 == 0)) { + setVisible(true); + setSprite(kScene3010DeadBoltButtonFileHashes2[_buttonIndex]); + } + +} + +uint32 SsScene3010DeadBoltButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (!_buttonLocked && _countdown1 == 0) { + if (_buttonEnabled) { + playSound(1); + playSound(2); + setVisible(true); + _buttonLocked = true; + sendMessage(_parentScene, 0x2000, _buttonIndex); + } else { + sendMessage(_parentScene, 0x2002, _buttonIndex); + } + _needRefresh = true; + updatePosition(); + } + messageResult = 1; + break; + } + return messageResult; +} + +void SsScene3010DeadBoltButton::disableButton() { + _buttonLocked = true; + setSprite(kScene3010DeadBoltButtonFileHashes1[_buttonIndex]); + setVisible(true); +} + +void SsScene3010DeadBoltButton::setSprite(uint32 fileHash) { + loadSprite(fileHash, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset); +} + +void SsScene3010DeadBoltButton::setCountdown(int count) { + _countdown2 = count * 18 + 1; +} + +AsScene3010DeadBolt::AsScene3010DeadBolt(NeverhoodEngine *vm, Scene *parentScene, int boltIndex, bool initUnlocked) + : AnimatedSprite(vm, 1100), _parentScene(parentScene), _boltIndex(boltIndex), _soundToggle(true), + _unlocked(false), _locked(false), _countdown(0) { + + _x = kAsScene3010DeadBoltPoints[_boltIndex].x; + _y = kAsScene3010DeadBoltPoints[_boltIndex].y; + + if (getSubVar(VA_LOCKS_DISABLED, kScene3010ButtonNameHashes[_boltIndex])) { + createSurface1(kAsScene3010DeadBoltFileHashes1[_boltIndex], 1200); + startAnimation(kAsScene3010DeadBoltFileHashes1[_boltIndex], 0, -1); + loadSound(0, 0x46005BC4); + } else { + createSurface1(kAsScene3010DeadBoltFileHashes2[_boltIndex], 1200); + startAnimation(kAsScene3010DeadBoltFileHashes2[_boltIndex], 0, -1); + loadSound(0, 0x420073DC); + loadSound(1, 0x420073DC); + } + + setVisible(false); + stIdle(); + if (initUnlocked) + unlock(true); + + _needRefresh = true; + AnimatedSprite::updatePosition(); + +} + +void AsScene3010DeadBolt::update() { + updateAnim(); + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) { + stDisabled(); + } +} + +uint32 AsScene3010DeadBolt::hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x3002: + gotoNextState(); + break; + } + return messageResult; +} + +void AsScene3010DeadBolt::stIdle() { + stopAnimation(); + SetUpdateHandler(&AsScene3010DeadBolt::update); + SetMessageHandler(&Sprite::handleMessage); + _locked = false; +} + +void AsScene3010DeadBolt::unlock(bool skipAnim) { + if (!_unlocked) { + setVisible(true); + if (skipAnim) { + startAnimation(kAsScene3010DeadBoltFileHashes1[_boltIndex], -1, 0); + _newStickFrameIndex = STICK_LAST_FRAME; + } else { + startAnimation(kAsScene3010DeadBoltFileHashes1[_boltIndex], 0, -1); + SetMessageHandler(&AsScene3010DeadBolt::hmAnimation); + FinalizeState(&AsScene3010DeadBolt::stIdleMessage); + NextState(&AsScene3010DeadBolt::stIdle); + playSound(0); + } + _unlocked = true; + loadSound(2, 0x4010C345); + } +} + +void AsScene3010DeadBolt::stIdleMessage() { + stopAnimation(); + SetMessageHandler(&Sprite::handleMessage); + sendMessage(_parentScene, 0x2001, _boltIndex); +} + +void AsScene3010DeadBolt::lock() { + if (!_locked) { + _locked = true; + setVisible(true); + startAnimation(kAsScene3010DeadBoltFileHashes2[_boltIndex], 0, -1); + SetMessageHandler(&AsScene3010DeadBolt::hmAnimation); + FinalizeState(&AsScene3010DeadBolt::stDisabledMessage); + NextState(&AsScene3010DeadBolt::stIdle); + if (_soundToggle) { + playSound(0); + } else { + playSound(1); + } + _soundToggle = !_soundToggle; + } +} + +void AsScene3010DeadBolt::setCountdown(int count) { + _countdown = count * 18 + 1; +} + +void AsScene3010DeadBolt::stDisabled() { + setVisible(true); + startAnimation(kAsScene3010DeadBoltFileHashes1[_boltIndex], 0, -1); + SetMessageHandler(&AsScene3010DeadBolt::hmAnimation); + FinalizeState(&AsScene3010DeadBolt::stDisabledMessage); + NextState(&AsScene3010DeadBolt::stIdle); + _playBackwards = true; + playSound(2); +} + +void AsScene3010DeadBolt::stDisabledMessage() { + setVisible(false); + sendMessage(_parentScene, 0x2003, _boltIndex); +} + +Scene3010::Scene3010(NeverhoodEngine *vm, Module *parentModule, int which) + : Scene(vm, parentModule), _countdown(0), _doorUnlocked(false), _checkUnlocked(false) { + + int initCountdown = 0; + + // DEBUG Enable to activate all buttons +#if 0 + setSubVar(VA_LOCKS_DISABLED, kScene3010ButtonNameHashes[0], 1); + setSubVar(VA_LOCKS_DISABLED, kScene3010ButtonNameHashes[1], 1); + setSubVar(VA_LOCKS_DISABLED, kScene3010ButtonNameHashes[2], 1); +#endif + + setBackground(0x80802626); + setPalette(0x80802626); + + for (int i = 0; i < 3; i++) { + _asDeadBolts[i] = insertSprite<AsScene3010DeadBolt>(this, i, which == 1);//CHECKME + _ssDeadBoltButtons[i] = insertSprite<SsScene3010DeadBoltButton>(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 0x0001: + 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 0x2000: + 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 0x2002: + if (!_checkUnlocked && _countdown == 0) { + _asDeadBolts[param.asInteger()]->lock(); + } + break; + case 0x2003: + _boltUnlocked[param.asInteger()] = false; + break; + } + return 0; +} + +// Scene3011 + +static const uint32 kAsScene3011SymbolFileHashes[] = { + 0x00C88050, + 0x01488050, + 0x02488050, + 0x04488050, + 0x08488050, + 0x10488050, + 0x20488050, + 0x40488050, + 0x80488050, + 0x00488051, + 0x00488052, + 0x00488054, + 0x008B0000, + 0x008D0000, + 0x00810000, + 0x00990000, + 0x00A90000, + 0x00C90000, + 0x00090000, + 0x01890000, + 0x02890000, + 0x04890000, + 0x08890000, + 0x10890000 +}; + +SsScene3011Button::SsScene3011Button(NeverhoodEngine *vm, Scene *parentScene, bool flag) + : StaticSprite(vm, 1400), _parentScene(parentScene), _countdown(0) { + + loadSprite(flag ? 0x11282020 : 0x994D0433, kSLFDefDrawOffset | kSLFDefPosition | kSLFDefCollisionBoundsOffset, 400); + setVisible(false); + loadSound(0, 0x44061000); + SetUpdateHandler(&SsScene3011Button::update); + SetMessageHandler(&SsScene3011Button::handleMessage); +} + +void SsScene3011Button::update() { + updatePosition(); + if (_countdown != 0 && (--_countdown == 0)) { + setVisible(false); + } +} + +uint32 SsScene3011Button::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + uint32 messageResult = 0; + StaticSprite::handleMessage(messageNum, param, sender); + switch (messageNum) { + case 0x1011: + if (_countdown == 0) { + setVisible(true); + _countdown = 4; + sendMessage(_parentScene, 0x2000, 0); + playSound(0); + } + messageResult = 1; + break; + } + return messageResult; +} + +AsScene3011Symbol::AsScene3011Symbol(NeverhoodEngine *vm, int symbolIndex, bool largeSymbol) + : AnimatedSprite(vm, 1000), _symbolIndex(symbolIndex), _largeSymbol(largeSymbol), _isNoisy(false) { + + if (_largeSymbol) { + _x = 310; + _y = 200; + createSurface1(kAsScene3011SymbolFileHashes[_symbolIndex], 1200); + loadSound(0, 0x6052C60F); + loadSound(1, 0x6890433B); + } else { + _symbolIndex = 12; + _x = symbolIndex * 39 + 96; + _y = 225; + createSurface(1200, 41, 48); + loadSound(0, 0x64428609); + loadSound(1, 0x7080023B); + } + setVisible(false); + _needRefresh = true; + SetUpdateHandler(&AnimatedSprite::update); +} + +void AsScene3011Symbol::show(bool isNoisy) { + _isNoisy = isNoisy; + startAnimation(kAsScene3011SymbolFileHashes[_symbolIndex], 0, -1); + setVisible(true); + if (_isNoisy) { + playSound(1); + } else { + playSound(0); + } +} + +void AsScene3011Symbol::hide() { + stopAnimation(); + setVisible(false); +} + +void AsScene3011Symbol::stopSymbolSound() { + if (_isNoisy) { + stopSound(1); + } else { + stopSound(0); + } +} + +void AsScene3011Symbol::change(int symbolIndex, bool isNoisy) { + _symbolIndex = symbolIndex; + _isNoisy = isNoisy; + startAnimation(kAsScene3011SymbolFileHashes[_symbolIndex], 0, -1); + setVisible(true); + if (_isNoisy) { + playSound(1); + } else { + playSound(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<AsScene3011Symbol>(symbolIndex, true); + + _ssButton = insertSprite<SsScene3011Button>(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 0x0001: + if (param.asPoint().x <= 20 || param.asPoint().x >= 620) { + leaveScene(0); + } + break; + case 0x2000: + _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 diff --git a/engines/neverhood/modules/module3000.h b/engines/neverhood/modules/module3000.h new file mode 100644 index 0000000000..7634360d7c --- /dev/null +++ b/engines/neverhood/modules/module3000.h @@ -0,0 +1,255 @@ +/* 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. + * + */ + +#ifndef NEVERHOOD_MODULES_MODULE3000_H +#define NEVERHOOD_MODULES_MODULE3000_H + +#include "neverhood/neverhood.h" +#include "neverhood/module.h" +#include "neverhood/scene.h" +#include "neverhood/modules/module1200.h" + +namespace Neverhood { + +class Module3000 : public Module { +public: + Module3000(NeverhoodEngine *vm, Module *parentModule, int which); + virtual ~Module3000(); +protected: + int _soundVolume; + bool _isWallBroken; + void createScene(int sceneNum, int which); + void updateScene(); +}; + +// Scene3009 + +class Scene3009; + +class SsScene3009FireCannonButton : public StaticSprite { +public: + SsScene3009FireCannonButton(NeverhoodEngine *vm, Scene3009 *parentScene); +protected: + Scene3009 *_parentScene; + bool _isClicked; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene3009SymbolEdges : public StaticSprite { +public: + SsScene3009SymbolEdges(NeverhoodEngine *vm, int index); + void show(); + void hide(); + void startBlinking(); +protected: + int _blinkCountdown; + bool _blinkToggle; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class SsScene3009TargetLine : public StaticSprite { +public: + SsScene3009TargetLine(NeverhoodEngine *vm, int index); + void show(); +}; + +class SsScene3009SymbolArrow : public StaticSprite { +public: + SsScene3009SymbolArrow(NeverhoodEngine *vm, Sprite *asSymbol, int index); + void hide(); +protected: + Sprite *_asSymbol; + int _index; + int _incrDecr; + bool _enabled; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene3009VerticalIndicator : public AnimatedSprite { +public: + AsScene3009VerticalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, int index); + void show(); +protected: + Scene3009 *_parentScene; + bool _enabled; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene3009HorizontalIndicator : public AnimatedSprite { +public: + AsScene3009HorizontalIndicator(NeverhoodEngine *vm, Scene3009 *parentScene, uint32 cannonTargetStatus); + void show(); + void stMoveLeft(); + void stMoveRight(); +protected: + Scene3009 *_parentScene; + bool _enabled; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void suMoveLeft(); + void suMoveRight(); +}; + +class AsScene3009Symbol : public AnimatedSprite { +public: + AsScene3009Symbol(NeverhoodEngine *vm, Scene3009 *parentScene, int symbolPosition); + void hide(); +protected: + Scene3009 *_parentScene; + int _symbolPosition; + uint32 _symbolIndex; + SsScene3009SymbolArrow *_ssArrowPrev; + SsScene3009SymbolArrow *_ssArrowNext; + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class Scene3009 : public Scene { +public: + Scene3009(NeverhoodEngine *vm, Module *parentModule, int which); + bool isTurning(); +protected: + int _lockSymbolsPart1Countdown; + int _lockSymbolsPart2Countdown; + SmackerPlayer *_smackerPlayer; + Sprite *_ssFireCannonButton; + SsScene3009SymbolEdges *_ssSymbolEdges[2]; + SsScene3009TargetLine *_ssTargetLines[2]; + AsScene3009VerticalIndicator *_asVerticalIndicator; + AsScene3009HorizontalIndicator *_asHorizontalIndicator; + AsScene3009Symbol *_asSymbols[6]; + uint32 _cannonTargetStatus; + uint32 _correctSymbols[6]; + bool _keepVideo; + bool _moveCannonLeftFirst; + bool _isTurning; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void playActionVideo(); + bool isSymbolsPart1Solved(); + bool isSymbolsPart2Solved(); +}; + +// Scene3010 + +class SsScene3010DeadBoltButton : public StaticSprite { +public: + SsScene3010DeadBoltButton(NeverhoodEngine *vm, Scene *parentScene, int buttonIndex, int initCountdown, bool initDisabled); + void setCountdown(int count); +protected: + Scene *_parentScene; + int _buttonIndex; + bool _buttonEnabled; + bool _buttonLocked; + int _countdown1; + int _countdown2; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void disableButton(); + void setSprite(uint32 fileHash); +}; + +class AsScene3010DeadBolt : public AnimatedSprite { +public: + AsScene3010DeadBolt(NeverhoodEngine *vm, Scene *parentScene, int boltIndex, bool initUnlocked); + void setCountdown(int count); + void lock(); + void unlock(bool skipAnim); +protected: + Scene *_parentScene; + int _boltIndex; + int _countdown; + bool _soundToggle; + bool _unlocked; + bool _locked; + void update(); + uint32 hmAnimation(int messageNum, const MessageParam ¶m, Entity *sender); + void stIdle(); + void stIdleMessage(); + void stDisabled(); + void stDisabledMessage(); +}; + +class Scene3010 : public Scene { +public: + Scene3010(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + int _countdown; + bool _doorUnlocked; + bool _checkUnlocked; + SsScene3010DeadBoltButton *_ssDeadBoltButtons[3]; + AsScene3010DeadBolt *_asDeadBolts[3]; + bool _boltUnlocked[3]; + bool _boltUnlocking[3]; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +// Scene3011 + +class SsScene3011Button : public StaticSprite { +public: + SsScene3011Button(NeverhoodEngine *vm, Scene *parentScene, bool flag); +protected: + Scene *_parentScene; + int _countdown; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class AsScene3011Symbol : public AnimatedSprite { +public: + AsScene3011Symbol(NeverhoodEngine *vm, int symbolIndex, bool largeSymbol); + void show(bool isNoisy); + void hide(); + void stopSymbolSound(); + void change(int symbolIndex, bool isNoisy); + int getSymbolIndex() { return _largeSymbol ? _symbolIndex : _symbolIndex - 12; } +protected: + bool _largeSymbol; + bool _isNoisy; + int _symbolIndex; +}; + +class Scene3011 : public Scene { +public: + Scene3011(NeverhoodEngine *vm, Module *parentModule, int which); +protected: + Sprite *_ssButton; + AsScene3011Symbol *_asSymbols[12]; + int _updateStatus; + bool _buttonClicked; + int _countdown; + int _noisySymbolIndex; + int _currentSymbolIndex; + int _noisyRandomSymbolIndex; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void fadeIn(); + void fadeOut(); +}; + +} // End of namespace Neverhood + +#endif /* NEVERHOOD_MODULES_MODULE3000_H */ |