aboutsummaryrefslogtreecommitdiff
path: root/engines/neverhood/modules
diff options
context:
space:
mode:
Diffstat (limited to 'engines/neverhood/modules')
-rw-r--r--engines/neverhood/modules/module1000.cpp1699
-rw-r--r--engines/neverhood/modules/module1000.h300
-rw-r--r--engines/neverhood/modules/module1100.cpp707
-rw-r--r--engines/neverhood/modules/module1100.h130
-rw-r--r--engines/neverhood/modules/module1200.cpp1102
-rw-r--r--engines/neverhood/modules/module1200.h216
-rw-r--r--engines/neverhood/modules/module1300.cpp1835
-rw-r--r--engines/neverhood/modules/module1300.h295
-rw-r--r--engines/neverhood/modules/module1400.cpp1621
-rw-r--r--engines/neverhood/modules/module1400.h281
-rw-r--r--engines/neverhood/modules/module1500.cpp134
-rw-r--r--engines/neverhood/modules/module1500.h58
-rw-r--r--engines/neverhood/modules/module1600.cpp1412
-rw-r--r--engines/neverhood/modules/module1600.h183
-rw-r--r--engines/neverhood/modules/module1700.cpp279
-rw-r--r--engines/neverhood/modules/module1700.h72
-rw-r--r--engines/neverhood/modules/module1800.cpp181
-rw-r--r--engines/neverhood/modules/module1800.h46
-rw-r--r--engines/neverhood/modules/module1900.cpp650
-rw-r--r--engines/neverhood/modules/module1900.h143
-rw-r--r--engines/neverhood/modules/module2000.cpp160
-rw-r--r--engines/neverhood/modules/module2000.h55
-rw-r--r--engines/neverhood/modules/module2100.cpp336
-rw-r--r--engines/neverhood/modules/module2100.h92
-rw-r--r--engines/neverhood/modules/module2200.cpp2564
-rw-r--r--engines/neverhood/modules/module2200.h375
-rw-r--r--engines/neverhood/modules/module2300.cpp186
-rw-r--r--engines/neverhood/modules/module2300.h48
-rw-r--r--engines/neverhood/modules/module2400.cpp992
-rw-r--r--engines/neverhood/modules/module2400.h182
-rw-r--r--engines/neverhood/modules/module2500.cpp546
-rw-r--r--engines/neverhood/modules/module2500.h101
-rw-r--r--engines/neverhood/modules/module2600.cpp348
-rw-r--r--engines/neverhood/modules/module2600.h74
-rw-r--r--engines/neverhood/modules/module2700.cpp1211
-rw-r--r--engines/neverhood/modules/module2700.h182
-rw-r--r--engines/neverhood/modules/module2800.cpp3205
-rw-r--r--engines/neverhood/modules/module2800.h505
-rw-r--r--engines/neverhood/modules/module2900.cpp439
-rw-r--r--engines/neverhood/modules/module2900.h102
-rw-r--r--engines/neverhood/modules/module3000.cpp1548
-rw-r--r--engines/neverhood/modules/module3000.h255
42 files changed, 24850 insertions, 0 deletions
diff --git a/engines/neverhood/modules/module1000.cpp b/engines/neverhood/modules/module1000.cpp
new file mode 100644
index 0000000000..ef2872ba2e
--- /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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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) {
+
+ _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 &param, 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 &param, 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 &param, Entity *sender) {
+ uint32 messageResult = handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x3002:
+ gotoNextState();
+ break;
+ }
+ return messageResult;
+}
+
+uint32 AsScene1002VenusFlyTrap::hmAnimationExt(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class AsScene1001Window : public AnimatedSprite {
+public:
+ AsScene1001Window(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmRingPulled1(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmRingPulled2(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmRingHangingLow(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmRingReleased(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class AsScene1002Door : public StaticSprite {
+public:
+ AsScene1002Door(NeverhoodEngine *vm, NRect &clipRect);
+protected:
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void suOpenDoor();
+ void suCloseDoor();
+};
+
+class AsScene1002BoxingGloveHitEffect : public AnimatedSprite {
+public:
+ AsScene1002BoxingGloveHitEffect(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ uint32 hmDoorSpyAnimation(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmAnimationSimple(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmAnimationExt(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ uint32 hmAnimation(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender);
+};
+
+// Scene1004
+
+class AsScene1004TrashCan : public AnimatedSprite {
+public:
+ AsScene1004TrashCan(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ void updatePaletteArea();
+};
+
+// Scene1005
+
+class Scene1005 : public Scene {
+public:
+ Scene1005(NeverhoodEngine *vm, Module *parentModule, int which);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender) {
+ uint32 messageResult = hmOnDoorFrameAboutToMove(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x3002:
+ gotoNextState();
+ break;
+ }
+ return messageResult;
+}
+
+uint32 AsScene1201Match::hmIdle(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender) {
+ uint32 messageResult = hmWaiting(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x3002:
+ gotoNextState();
+ break;
+ }
+ return messageResult;
+}
+
+uint32 AsScene1201Creature::hmPincerSnapKlaymen(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class AsScene1201TntManRope : public AnimatedSprite {
+public:
+ AsScene1201TntManRope(NeverhoodEngine *vm, bool isDummyHanging);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ void stOpenDoor();
+ void stCloseDoor();
+ void stCloseDoorDone();
+};
+
+class AsScene1201KlaymenHead : public AnimatedSprite {
+public:
+ AsScene1201KlaymenHead(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ uint32 hmComingDown(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ uint32 hmOnDoorFrameMoving(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmIdle(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ uint32 hmPincerSnap(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmPincerSnapKlaymen(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmChangePosition(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ uint32 hmSolved(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender) {
+ uint32 messageResult = Scene::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x3002:
+ stChooseKing();
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Scene1317::hmChooseKing(int messageNum, const MessageParam &param, 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 &param, Entity *sender) {
+ uint32 messageResult = Scene::handleMessage(messageNum, param, sender);
+ switch (messageNum) {
+ case 0x3002:
+ stEndMovie();
+ break;
+ }
+ return messageResult;
+}
+
+uint32 Scene1317::hmEndMovie(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class AsScene1303Balloon : public AnimatedSprite {
+public:
+ AsScene1303Balloon(NeverhoodEngine *vm, Scene *parentScene);
+protected:
+ Scene *_parentScene;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmBalloonPopped(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class Scene1305 : public Scene {
+public:
+ Scene1305(NeverhoodEngine *vm, Module *parentModule, int which);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender);
+ uint32 handleMessage416EB0(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class AsScene1308JaggyDoor : public AnimatedSprite {
+public:
+ AsScene1308JaggyDoor(NeverhoodEngine *vm, Scene *parentScene);
+protected:
+ Scene *_parentScene;
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmChooseKing(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmHoborgAsKing(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmEndMovie(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmSuckInProjector(int messageNum, const MessageParam &param, Entity *sender);
+ void stStartSucking();
+ void stDoneSucking();
+ void stSuckInProjector();
+};
+
+class AsScene1401Mouse : public AnimatedSprite {
+public:
+ AsScene1401Mouse(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ void suSuckedIn();
+ void stSuckedIn();
+};
+
+class AsScene1401Cheese : public AnimatedSprite {
+public:
+ AsScene1401Cheese(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmLockedInSlot(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmAnimation(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmAnimation(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmLeaveCar(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+ uint32 hmUpperFloor(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmRidingCar(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmCarAtHome(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmTryToPlugIn(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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..b8da0f64ff
--- /dev/null
+++ b/engines/neverhood/modules/module2200.cpp
@@ -0,0 +1,2564 @@
+/* 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 &param, 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 &param, 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;
+ 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class SsScene2205DoorFrame : public StaticSprite {
+public:
+ SsScene2205DoorFrame(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+};
+
+class AsScene2206DoorSpikes : public StaticSprite {
+public:
+ AsScene2206DoorSpikes(NeverhoodEngine *vm, uint32 fileHash);
+protected:
+ int _deltaIndex;
+ void update();
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 handleMessage2(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ void readClickedColumn();
+};
+
+class Scene2247 : public Scene {
+public:
+ Scene2247(NeverhoodEngine *vm, Module *parentModule, int which);
+ ~Scene2247();
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class AsScene2401WaterSpit : public AnimatedSprite {
+public:
+ AsScene2401WaterSpit(NeverhoodEngine *vm);
+protected:
+ int _soundIndex;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class AsScene2401FlowingWater : public AnimatedSprite {
+public:
+ AsScene2401FlowingWater(NeverhoodEngine *vm);
+ virtual ~AsScene2401FlowingWater();
+protected:
+ bool _isWaterFlowing;
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmRidingCar(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmCarAtHome(int messageNum, const MessageParam &param, 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 &param, Entity *sender);
+};
+
+class Scene2504 : public Scene {
+public:
+ Scene2504(NeverhoodEngine *vm, Module *parentModule, int which);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class AsScene2609Water : public AnimatedSprite {
+public:
+ AsScene2609Water(NeverhoodEngine *vm);
+ virtual ~AsScene2609Water();
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmCarAtHome(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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..183de8e6b2
--- /dev/null
+++ b/engines/neverhood/modules/module2800.cpp
@@ -0,0 +1,3205 @@
+/* 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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;
+
+ which = 3;
+
+ 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmPulled(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class AsScene2803TestTubeOne : public AnimatedSprite {
+public:
+ AsScene2803TestTubeOne(NeverhoodEngine *vm, uint32 fileHash1, uint32 fileHash2);
+protected:
+ uint32 _fileHash1, _fileHash2;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class AsScene2803Rope : public AnimatedSprite {
+public:
+ AsScene2803Rope(NeverhoodEngine *vm, Scene *parentScene, int16 x);
+protected:
+ Scene *_parentScene;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmReleased(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class SsScene2804LightCoil : public StaticSprite {
+public:
+ SsScene2804LightCoil(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ void show();
+ void hide();
+ void stBeaming();
+ uint32 hmBeaming(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class AsScene2804BeamTarget : public AnimatedSprite {
+public:
+ AsScene2804BeamTarget(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender);
+};
+
+class AsScene2806Spew : public AnimatedSprite {
+public:
+ AsScene2806Spew(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ uint32 hmActivating(int messageNum, const MessageParam &param, 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 &param, 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 &param, Entity *sender);
+ void update();
+ bool isMixtureGood();
+ bool isAnyTestTubeFilled();
+};
+
+class AsScene2809Spew : public AnimatedSprite {
+public:
+ AsScene2809Spew(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ void insertKlaymenLadder();
+};
+
+class AsScene2812Winch : public AnimatedSprite {
+public:
+ AsScene2812Winch(NeverhoodEngine *vm);
+ virtual ~AsScene2812Winch();
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+};
+
+class AsScene2812Rope : public AnimatedSprite {
+public:
+ AsScene2812Rope(NeverhoodEngine *vm, Scene *parentScene);
+protected:
+ Scene *_parentScene;
+ uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
+ uint32 hmRopingDown(int messageNum, const MessageParam &param, Entity *sender);
+ void stRopingDown();
+};
+
+class AsScene2812TrapDoor : public AnimatedSprite {
+public:
+ AsScene2812TrapDoor(NeverhoodEngine *vm);
+protected:
+ uint32 handleMessage(int messageNum, const MessageParam &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, 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 &param, Entity *sender);
+ void fadeIn();
+ void fadeOut();
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_MODULES_MODULE3000_H */